From 2a9bc9ebf09fbafa5378d143083434204e9f233e Mon Sep 17 00:00:00 2001 From: Curl Upstream Date: Wed, 27 Apr 2022 08:11:01 +0200 Subject: curl 2022-04-27 (1669b17d) Code extracted from: https://github.com/curl/curl.git at commit 1669b17d3a1a1fd824308544ca0ec02a2a4f50ea (curl-7_83_0). --- CMake/CurlTests.c | 4 +- CMake/FindMSH3.cmake | 68 +++ CMake/OtherTests.cmake | 6 +- CMakeLists.txt | 21 +- COPYING | 2 +- include/curl/curl.h | 32 +- include/curl/curlver.h | 10 +- include/curl/header.h | 64 ++ include/curl/options.h | 4 +- include/curl/system.h | 20 +- lib/Makefile.inc | 30 +- lib/altsvc.c | 8 +- lib/asyn-ares.c | 5 +- lib/base64.c | 35 +- lib/c-hyper.c | 27 +- lib/conncache.c | 63 +- lib/conncache.h | 5 +- lib/connect.c | 37 +- lib/cookie.c | 15 +- lib/curl_base64.h | 9 +- lib/curl_config.h.cmake | 5 +- lib/curl_ctype.c | 5 +- lib/curl_ctype.h | 30 +- lib/curl_des.c | 11 +- lib/curl_des.h | 11 +- lib/curl_gssapi.c | 4 +- lib/curl_multibyte.c | 12 +- lib/curl_ntlm_core.c | 46 +- lib/curl_ntlm_core.h | 31 +- lib/curl_path.c | 4 +- lib/curl_sasl.c | 20 +- lib/curl_sasl.h | 4 +- lib/curl_setup.h | 43 +- lib/curl_sha256.h | 4 +- lib/dict.c | 14 +- lib/doh.c | 8 +- lib/dotdot.c | 4 +- lib/easy.c | 13 +- lib/escape.c | 37 +- lib/escape.h | 5 +- lib/file.c | 4 +- lib/formdata.c | 23 +- lib/ftp.c | 28 +- lib/gopher.c | 4 +- lib/h2h3.c | 310 ++++++++++ lib/h2h3.h | 59 ++ lib/headers.c | 324 ++++++++++ lib/headers.h | 53 ++ lib/hmac.c | 6 +- lib/hostcheck.c | 142 ----- lib/hostcheck.h | 31 - lib/hostip.c | 13 +- lib/hsts.c | 4 +- lib/http.c | 322 +++++----- lib/http.h | 59 +- lib/http2.c | 360 +++-------- lib/http_aws_sigv4.c | 6 +- lib/http_chunks.c | 35 +- lib/http_negotiate.c | 4 +- lib/http_ntlm.c | 8 +- lib/http_proxy.c | 53 +- lib/http_proxy.h | 3 +- lib/idn_win32.c | 26 +- lib/if2ip.c | 46 +- lib/if2ip.h | 14 +- lib/imap.c | 20 +- lib/krb5.c | 12 +- lib/ldap.c | 27 +- lib/md5.c | 158 +++-- lib/mime.c | 125 ++-- lib/mime.h | 9 +- lib/mprintf.c | 5 +- lib/mqtt.c | 29 +- lib/multi.c | 44 +- lib/non-ascii.c | 336 ----------- lib/non-ascii.h | 61 -- lib/nonblock.c | 4 +- lib/nwlib.c | 327 ---------- lib/nwos.c | 88 --- lib/openldap.c | 373 ++++++++++-- lib/pingpong.c | 15 +- lib/pop3.c | 16 +- lib/quic.h | 5 +- lib/rand.c | 4 +- lib/rtsp.c | 41 +- lib/select.c | 92 +-- lib/select.h | 17 +- lib/sendf.c | 88 +-- lib/sendf.h | 10 +- lib/setopt.c | 51 +- lib/sha256.c | 12 +- lib/smb.c | 23 +- lib/smtp.c | 19 +- lib/socks.c | 2 +- lib/socks.h | 4 +- lib/strcase.c | 140 +---- lib/strcase.h | 13 +- lib/strerror.c | 29 +- lib/telnet.c | 6 +- lib/tftp.c | 8 +- lib/timediff.c | 84 +++ lib/timediff.h | 50 ++ lib/timeval.h | 10 +- lib/transfer.c | 130 ++-- lib/transfer.h | 5 +- lib/url.c | 265 +++++---- lib/url.h | 6 +- lib/urlapi.c | 85 +-- lib/urldata.h | 59 +- lib/vauth/digest.c | 25 +- lib/vauth/ntlm.c | 101 +--- lib/vauth/spnego_gssapi.c | 8 +- lib/vauth/spnego_sspi.c | 24 +- lib/vauth/vauth.h | 5 +- lib/version.c | 33 +- lib/version_win32.c | 4 +- lib/vquic/msh3.c | 498 ++++++++++++++++ lib/vquic/msh3.h | 38 ++ lib/vquic/ngtcp2.c | 263 ++++----- lib/vquic/quiche.c | 317 +++++----- lib/vquic/quiche.h | 6 +- lib/vquic/vquic.c | 4 +- lib/vquic/vquic.h | 2 +- lib/vssh/libssh.c | 37 +- lib/vssh/libssh2.c | 55 +- lib/vssh/wolfssh.c | 14 +- lib/vtls/bearssl.c | 337 ++++++++++- lib/vtls/gskit.c | 79 ++- lib/vtls/gtls.c | 92 ++- lib/vtls/hostcheck.c | 140 +++++ lib/vtls/hostcheck.h | 31 + lib/vtls/mbedtls.c | 126 ++-- lib/vtls/mesalink.c | 679 --------------------- lib/vtls/mesalink.h | 32 - lib/vtls/nss.c | 95 ++- lib/vtls/openssl.c | 294 +++++---- lib/vtls/openssl.h | 20 +- lib/vtls/rustls.c | 80 ++- lib/vtls/schannel.c | 83 +-- lib/vtls/schannel.h | 6 +- lib/vtls/schannel_verify.c | 15 +- lib/vtls/sectransp.c | 55 +- lib/vtls/vtls.c | 64 +- lib/vtls/vtls.h | 17 +- lib/vtls/wolfssl.c | 64 +- lib/vtls/x509asn1.c | 1409 ++++++++++++++++++++++++++++++++++++++++++++ lib/vtls/x509asn1.h | 78 +++ lib/warnless.c | 6 +- lib/warnless.h | 8 +- lib/x509asn1.c | 1329 ----------------------------------------- lib/x509asn1.h | 134 ----- 151 files changed, 6247 insertions(+), 6080 deletions(-) create mode 100644 CMake/FindMSH3.cmake create mode 100644 include/curl/header.h create mode 100644 lib/h2h3.c create mode 100644 lib/h2h3.h create mode 100644 lib/headers.c create mode 100644 lib/headers.h delete mode 100644 lib/hostcheck.c delete mode 100644 lib/hostcheck.h delete mode 100644 lib/non-ascii.c delete mode 100644 lib/non-ascii.h delete mode 100644 lib/nwlib.c delete mode 100644 lib/nwos.c create mode 100644 lib/timediff.c create mode 100644 lib/timediff.h create mode 100644 lib/vquic/msh3.c create mode 100644 lib/vquic/msh3.h create mode 100644 lib/vtls/hostcheck.c create mode 100644 lib/vtls/hostcheck.h delete mode 100644 lib/vtls/mesalink.c delete mode 100644 lib/vtls/mesalink.h create mode 100644 lib/vtls/x509asn1.c create mode 100644 lib/vtls/x509asn1.h delete mode 100644 lib/x509asn1.c delete mode 100644 lib/x509asn1.h diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 8666da0..42addd7 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -56,7 +56,7 @@ return 0; # define PLATFORM_AIX_V3 #endif /* */ -#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__) +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) #error "O_NONBLOCK does not work on this platform" #endif diff --git a/CMake/FindMSH3.cmake b/CMake/FindMSH3.cmake new file mode 100644 index 0000000..1b8b9d8 --- /dev/null +++ b/CMake/FindMSH3.cmake @@ -0,0 +1,68 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### + +#[=======================================================================[.rst: +FindMSH3 +---------- + +Find the msh3 library + +Result Variables +^^^^^^^^^^^^^^^^ + +``MSH3_FOUND`` + System has msh3 +``MSH3_INCLUDE_DIRS`` + The msh3 include directories. +``MSH3_LIBRARIES`` + The libraries needed to use msh3 +#]=======================================================================] +if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(PC_MSH3 libmsh3) +endif() + +find_path(MSH3_INCLUDE_DIR msh3.h + HINTS + ${PC_MSH3_INCLUDEDIR} + ${PC_MSH3_INCLUDE_DIRS} +) + +find_library(MSH3_LIBRARY NAMES msh3 + HINTS + ${PC_MSH3_LIBDIR} + ${PC_MSH3_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MSH3 + REQUIRED_VARS + MSH3_LIBRARY + MSH3_INCLUDE_DIR +) + +if(MSH3_FOUND) + set(MSH3_LIBRARIES ${MSH3_LIBRARY}) + set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR}) +endif() + +mark_as_advanced(MSH3_INCLUDE_DIRS MSH3_LIBRARIES) diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 73c813f..d710eb8 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -236,8 +236,8 @@ endif() unset(CMAKE_TRY_COMPILE_TARGET_TYPE) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) - if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - # only try this on non-macOS + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "iOS") + # only try this on non-apple platforms # if not cross-compilation... include(CheckCSourceRuns) diff --git a/CMakeLists.txt b/CMakeLists.txt index 125b0a0..6957f61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -109,7 +109,7 @@ option(ENABLE_CURLDEBUG "Set to ON to build with TrackMemory feature enabled" OF if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) if(PICKY_COMPILER) - foreach(_CCOPT -pedantic -Wall -W -Wpointer-arith -Wwrite-strings -Wunused -Wshadow -Winline -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wfloat-equal -Wsign-compare -Wundef -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement -Wstrict-aliasing=3 -Wcast-align -Wtype-limits -Wold-style-declaration -Wmissing-parameter-type -Wempty-body -Wclobbered -Wignored-qualifiers -Wconversion -Wvla -Wdouble-promotion) + foreach(_CCOPT -pedantic -Wall -W -Wpointer-arith -Wwrite-strings -Wunused -Wshadow -Winline -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wfloat-equal -Wsign-compare -Wundef -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement -Wstrict-aliasing=3 -Wcast-align -Wtype-limits -Wold-style-declaration -Wmissing-parameter-type -Wempty-body -Wclobbered -Wignored-qualifiers -Wconversion -Wvla -Wdouble-promotion -Wenum-conversion -Warith-conversion) # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new # test result in. string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) @@ -334,13 +334,6 @@ check_library_exists_concat("${CMAKE_DL_LIBS}" dlopen HAVE_LIBDL) check_library_exists_concat("socket" connect HAVE_LIBSOCKET) check_library_exists("c" gethostbyname "" NOT_NEED_LIBNSL) -# Yellowtab Zeta needs different libraries than BeOS 5. -if(BEOS) - set(NOT_NEED_LIBNSL 1) - check_library_exists_concat("bind" gethostbyname HAVE_LIBBIND) - check_library_exists_concat("bnetapi" closesocket HAVE_LIBBNETAPI) -endif() - if(NOT NOT_NEED_LIBNSL) check_library_exists_concat("nsl" gethostbyname HAVE_LIBNSL) endif() @@ -563,6 +556,16 @@ if(USE_QUICHE) cmake_pop_check_state() endif() +option(USE_MSH3 "Use msquic library for HTTP/3 support" OFF) +if(USE_MSH3) + if(USE_NGTCP2 OR USE_QUICHE) + message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") + endif() + set(USE_MSH3 ON) + include_directories(${MSH3_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${MSH3_LIBRARIES}) +endif() + if(NOT CURL_DISABLE_LDAP) if(WIN32) option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) diff --git a/COPYING b/COPYING index 48f1447..90f05ad 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2021, Daniel Stenberg, , and many +Copyright (c) 1996 - 2022, Daniel Stenberg, , and many contributors, see the THANKS file. All rights reserved. diff --git a/include/curl/curl.h b/include/curl/curl.h index 7b69ce2..3a2c2ea 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,8 +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__) + (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) #include #endif @@ -82,14 +81,10 @@ #include #endif -#if !defined(CURL_WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#if !defined(CURL_WIN32) #include #endif -#ifdef __BEOS__ -#include -#endif - /* Compatibility for non-Clang compilers */ #ifndef __has_declspec_attribute # define __has_declspec_attribute(x) 0 @@ -529,10 +524,6 @@ typedef enum { CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ CURLE_OUT_OF_MEMORY, /* 27 */ - /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error - instead of a memory allocation error if CURL_DOES_CONVERSIONS - is defined - */ CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ CURLE_OBSOLETE29, /* 29 - NOT USED */ CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ @@ -569,7 +560,7 @@ typedef enum { CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint wasn't verified fine */ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ - CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_OBSOLETE62, /* 62 - NOT IN USE since 7.82.0 */ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind @@ -585,11 +576,7 @@ typedef enum { CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ CURLE_CONV_FAILED, /* 75 - conversion failed */ - CURLE_CONV_REQD, /* 76 - caller must register conversion - callbacks using curl_easy_setopt options - CURLOPT_CONV_FROM_NETWORK_FUNCTION, - CURLOPT_CONV_TO_NETWORK_FUNCTION, and - CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_OBSOLETE76, /* 76 - NOT IN USE since 7.82.0 */ CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing or wrong format */ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ @@ -683,13 +670,13 @@ typedef enum { /* The following were added earlier */ #define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT - #define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR #define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED #define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED - #define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE #define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME +#define CURLE_LDAP_INVALID_URL CURLE_OBSOLETE62 +#define CURLE_CONV_REQD CURLE_OBSOLETE76 /* This was the error code 50 in 7.7.3 and a few earlier versions, this is no longer used by libcurl but is instead #defined here only to not @@ -2062,7 +2049,7 @@ typedef enum { * (in seconds) */ CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288), - /* SASL authorisation identity */ + /* SASL authorization identity */ CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289), /* allow RCPT TO command to fail for some recipients */ @@ -2832,7 +2819,7 @@ typedef enum { CURLCLOSEPOLICY_LAST /* last, never use this */ } curl_closepolicy; -#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */ +#define CURL_GLOBAL_SSL (1<<0) /* no purpose since 7.57.0 */ #define CURL_GLOBAL_WIN32 (1<<1) #define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) #define CURL_GLOBAL_NOTHING 0 @@ -3082,6 +3069,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #include "multi.h" #include "urlapi.h" #include "options.h" +#include "header.h" /* the typechecker doesn't work in C++ (yet) */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ diff --git a/include/curl/curlver.h b/include/curl/curlver.h index fe57e43..f725247 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,16 +26,16 @@ a script at release-time. This was made its own header file in 7.11.2 */ /* This is the global package copyright */ -#define LIBCURL_COPYRIGHT "1996 - 2021 Daniel Stenberg, ." +#define LIBCURL_COPYRIGHT "1996 - 2022 Daniel Stenberg, ." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.81.0-DEV" +#define LIBCURL_VERSION "7.83.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 81 +#define LIBCURL_VERSION_MINOR 83 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier @@ -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 0x075100 +#define LIBCURL_VERSION_NUM 0x075300 /* * This is the date and time when the full source package was created. The diff --git a/include/curl/header.h b/include/curl/header.h new file mode 100644 index 0000000..7715b61 --- /dev/null +++ b/include/curl/header.h @@ -0,0 +1,64 @@ +#ifndef CURLINC_HEADER_H +#define CURLINC_HEADER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2018 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +struct curl_header { + char *name; /* this might not use the same case */ + char *value; + size_t amount; /* number of headers using this name */ + size_t index; /* ... of this instance, 0 or higher */ + unsigned int origin; /* see bits below */ + void *anchor; /* handle privately used by libcurl */ +}; + +/* 'origin' bits */ +#define CURLH_HEADER (1<<0) /* plain server header */ +#define CURLH_TRAILER (1<<1) /* trailers */ +#define CURLH_CONNECT (1<<2) /* CONNECT headers */ +#define CURLH_1XX (1<<3) /* 1xx headers */ +#define CURLH_PSEUDO (1<<4) /* pseudo headers */ + +typedef enum { + CURLHE_OK, + CURLHE_BADINDEX, /* header exists but not with this index */ + CURLHE_MISSING, /* no such header exists */ + CURLHE_NOHEADERS, /* no headers at all exist (yet) */ + CURLHE_NOREQUEST, /* no request with this number was used */ + CURLHE_OUT_OF_MEMORY, /* out of memory while processing */ + CURLHE_BAD_ARGUMENT, /* a function argument was not okay */ + CURLHE_NOT_BUILT_IN /* if API was disabled in the build */ +} CURLHcode; + +CURL_EXTERN CURLHcode curl_easy_header(CURL *easy, + const char *name, + size_t index, + unsigned int origin, + int request, + struct curl_header **hout); + +CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy, + unsigned int origin, + int request, + struct curl_header *prev); + +#endif /* CURLINC_HEADER_H */ diff --git a/include/curl/options.h b/include/curl/options.h index 14373b5..91360b3 100644 --- a/include/curl/options.h +++ b/include/curl/options.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -57,7 +57,7 @@ CURL_EXTERN const struct curl_easyoption * curl_easy_option_by_name(const char *name); CURL_EXTERN const struct curl_easyoption * -curl_easy_option_by_id (CURLoption id); +curl_easy_option_by_id(CURLoption id); CURL_EXTERN const struct curl_easyoption * curl_easy_option_next(const struct curl_easyoption *prev); diff --git a/include/curl/system.h b/include/curl/system.h index faf8fcf..000fea6 100644 --- a/include/curl/system.h +++ b/include/curl/system.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -98,22 +98,6 @@ # define CURL_SUFFIX_CURL_OFF_TU UL # define CURL_TYPEOF_CURL_SOCKLEN_T int -#elif defined(__WATCOMC__) -# if defined(__386__) -# define CURL_TYPEOF_CURL_OFF_T __int64 -# define CURL_FORMAT_CURL_OFF_T "I64d" -# define CURL_FORMAT_CURL_OFF_TU "I64u" -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T int - #elif defined(__POCC__) # if (__POCC__ < 280) # define CURL_TYPEOF_CURL_OFF_T long @@ -137,7 +121,7 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__LCC__) -# if defined(__e2k__) /* MCST eLbrus C Compiler */ +# if defined(__MCST__) /* MCST eLbrus Compiler Collection */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 3e9ddec..1ab0078 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -44,10 +44,10 @@ LIB_VTLS_CFILES = \ vtls/bearssl.c \ vtls/gskit.c \ vtls/gtls.c \ + vtls/hostcheck.c \ vtls/keylog.c \ vtls/mbedtls.c \ vtls/mbedtls_threadlock.c \ - vtls/mesalink.c \ vtls/nss.c \ vtls/openssl.c \ vtls/rustls.c \ @@ -55,30 +55,34 @@ LIB_VTLS_CFILES = \ vtls/schannel_verify.c \ vtls/sectransp.c \ vtls/vtls.c \ - vtls/wolfssl.c + vtls/wolfssl.c \ + vtls/x509asn1.c LIB_VTLS_HFILES = \ vtls/bearssl.h \ vtls/gskit.h \ vtls/gtls.h \ + vtls/hostcheck.h \ vtls/keylog.h \ vtls/mbedtls.h \ vtls/mbedtls_threadlock.h \ - vtls/mesalink.h \ vtls/nssg.h \ vtls/openssl.h \ vtls/rustls.h \ vtls/schannel.h \ vtls/sectransp.h \ vtls/vtls.h \ - vtls/wolfssl.h + vtls/wolfssl.h \ + vtls/x509asn1.h LIB_VQUIC_CFILES = \ + vquic/msh3.c \ vquic/ngtcp2.c \ vquic/quiche.c \ vquic/vquic.c LIB_VQUIC_HFILES = \ + vquic/msh3.h \ vquic/ngtcp2.h \ vquic/quiche.h \ vquic/vquic.h @@ -137,10 +141,11 @@ LIB_CFILES = \ getenv.c \ getinfo.c \ gopher.c \ + h2h3.c \ hash.c \ + headers.c \ hmac.c \ hostasyn.c \ - hostcheck.c \ hostip.c \ hostip4.c \ hostip6.c \ @@ -170,7 +175,6 @@ LIB_CFILES = \ mqtt.c \ multi.c \ netrc.c \ - non-ascii.c \ nonblock.c \ openldap.c \ parsedate.c \ @@ -203,6 +207,7 @@ LIB_CFILES = \ system_win32.c \ telnet.c \ tftp.c \ + timediff.c \ timeval.c \ transfer.c \ url.c \ @@ -210,8 +215,7 @@ LIB_CFILES = \ version.c \ version_win32.c \ warnless.c \ - wildcard.c \ - x509asn1.c + wildcard.c LIB_HFILES = \ altsvc.h \ @@ -268,8 +272,9 @@ LIB_HFILES = \ ftplistparser.h \ getinfo.h \ gopher.h \ + h2h3.h \ hash.h \ - hostcheck.h \ + headers.h \ hostip.h \ hsts.h \ http.h \ @@ -291,7 +296,6 @@ LIB_HFILES = \ multihandle.h \ multiif.h \ netrc.h \ - non-ascii.h \ nonblock.h \ parsedate.h \ pingpong.h \ @@ -324,6 +328,7 @@ LIB_HFILES = \ system_win32.h \ telnet.h \ tftp.h \ + timediff.h \ timeval.h \ transfer.h \ url.h \ @@ -331,8 +336,7 @@ LIB_HFILES = \ urldata.h \ version_win32.h \ warnless.h \ - wildcard.h \ - x509asn1.h + wildcard.h LIB_RCFILES = libcurl.rc diff --git a/lib/altsvc.c b/lib/altsvc.c index 36acc3a..45929a5 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2019 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,7 +21,7 @@ ***************************************************************************/ /* * The Alt-Svc: header is defined in RFC 7838: - * https://tools.ietf.org/html/rfc7838 + * https://datatracker.ietf.org/doc/html/rfc7838 */ #include "curl_setup.h" @@ -54,6 +54,8 @@ #define H3VERSION "h3-29" #elif defined(USE_NGTCP2) && !defined(UNITTESTS) #define H3VERSION "h3-29" +#elif defined(USE_MSH3) && !defined(UNITTESTS) +#define H3VERSION "h3-29" #else #define H3VERSION "h3" #endif @@ -264,7 +266,7 @@ struct altsvcinfo *Curl_altsvc_init(void) /* set default behavior */ asi->flags = CURLALTSVC_H1 -#ifdef USE_NGHTTP2 +#ifdef USE_HTTP2 | CURLALTSVC_H2 #endif #ifdef ENABLE_QUIC diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index fd0bb6c..c885ade 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -65,6 +65,7 @@ #include "connect.h" #include "select.h" #include "progress.h" +#include "timediff.h" # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ defined(WIN32) @@ -290,7 +291,7 @@ int Curl_resolver_getsock(struct Curl_easy *data, timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime, &timebuf); - milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); + milli = (long)curlx_tvtoms(timeout); if(milli == 0) milli += 10; Curl_expire(data, milli, EXPIRE_ASYNC_NAME); diff --git a/lib/base64.c b/lib/base64.c index be6f163..960a1ca 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,7 +34,6 @@ #include "urldata.h" /* for the Curl_easy definition */ #include "warnless.h" #include "curl_base64.h" -#include "non-ascii.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -176,19 +175,15 @@ CURLcode Curl_base64_decode(const char *src, } static CURLcode base64_encode(const char *table64, - struct Curl_easy *data, const char *inputbuff, size_t insize, char **outptr, size_t *outlen) { - CURLcode result; unsigned char ibuf[3]; unsigned char obuf[4]; int i; int inputparts; char *output; char *base64data; - char *convbuf = NULL; - const char *indata = inputbuff; *outptr = NULL; @@ -206,20 +201,6 @@ static CURLcode base64_encode(const char *table64, if(!output) return CURLE_OUT_OF_MEMORY; - /* - * The base64 data needs to be created using the network encoding - * not the host encoding. And we can't change the actual input - * so we copy it to a buffer, translate it, and use that instead. - */ - result = Curl_convert_clone(data, indata, insize, &convbuf); - if(result) { - free(output); - return result; - } - - if(convbuf) - indata = (char *)convbuf; - while(insize > 0) { for(i = inputparts = 0; i < 3; i++) { if(insize > 0) { @@ -270,10 +251,8 @@ static CURLcode base64_encode(const char *table64, /* Return the pointer to the new data (allocated memory) */ *outptr = base64data; - free(convbuf); - /* Return the length of the new data */ - *outlen = strlen(base64data); + *outlen = output - base64data; return CURLE_OK; } @@ -295,11 +274,10 @@ static CURLcode base64_encode(const char *table64, * * @unittest: 1302 */ -CURLcode Curl_base64_encode(struct Curl_easy *data, - const char *inputbuff, size_t insize, +CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen) { - return base64_encode(base64, data, inputbuff, insize, outptr, outlen); + return base64_encode(base64, inputbuff, insize, outptr, outlen); } /* @@ -319,11 +297,10 @@ CURLcode Curl_base64_encode(struct Curl_easy *data, * * @unittest: 1302 */ -CURLcode Curl_base64url_encode(struct Curl_easy *data, - const char *inputbuff, size_t insize, +CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen) { - return base64_encode(base64url, data, inputbuff, insize, outptr, outlen); + return base64_encode(base64url, inputbuff, insize, outptr, outlen); } #endif /* no users so disabled */ diff --git a/lib/c-hyper.c b/lib/c-hyper.c index c253cd3..de09568 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -142,7 +142,7 @@ static int hyper_each_header(void *userdata, return HYPER_ITER_BREAK; } else { - if(Curl_dyn_add(&data->state.headerb, "\r\n")) + if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n"))) return HYPER_ITER_BREAK; } len = Curl_dyn_len(&data->state.headerb); @@ -293,10 +293,8 @@ static CURLcode status_line(struct Curl_easy *data, 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(result) + return result; } data->info.header_size += (long)len; data->req.headerbytecount += (long)len; @@ -416,7 +414,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, else if(h->endtask == task) { /* end of transfer */ *done = TRUE; - infof(data, "hyperstream is done!"); + infof(data, "hyperstream is done"); if(!k->bodywrites) { /* hyper doesn't always call the body write callback */ bool stilldone; @@ -806,7 +804,7 @@ static void http1xx_cb(void *arg, struct hyper_response *resp) } if(data->state.hresult) - infof(data, "ERROR in 1xx, bail out!"); + infof(data, "ERROR in 1xx, bail out"); } /* @@ -906,6 +904,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) hyper_clientconn_options_http2(options, 1); h2 = TRUE; } + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); hyper_clientconn_options_exec(options, h->exec); @@ -1022,7 +1022,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } - p_accept = Curl_checkheaders(data, "Accept")?NULL:"Accept: */*\r\n"; + p_accept = Curl_checkheaders(data, + STRCONST("Accept"))?NULL:"Accept: */*\r\n"; if(p_accept) { result = Curl_hyper_header(data, headers, p_accept); if(result) @@ -1036,8 +1037,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, "Proxy-Connection") && - !Curl_checkProxyheaders(data, conn, "Proxy-Connection")) { + !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && + !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); if(result) goto error; @@ -1045,7 +1046,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) #endif Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, "Referer")) { + if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); if(!data->state.aptr.ref) result = CURLE_OUT_OF_MEMORY; @@ -1055,7 +1056,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } - if(!Curl_checkheaders(data, "Accept-Encoding") && + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { Curl_safefree(data->state.aptr.accept_encoding); data->state.aptr.accept_encoding = diff --git a/lib/conncache.c b/lib/conncache.c index fec1937..aa29620 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -132,13 +132,11 @@ void Curl_conncache_destroy(struct conncache *connc) } /* creates a key to find a bundle for this connection */ -static void hashkey(struct connectdata *conn, char *buf, - size_t len, /* something like 128 is fine */ - const char **hostp) +static void hashkey(struct connectdata *conn, char *buf, size_t len) { const char *hostname; long port = conn->remote_port; - + DEBUGASSERT(len >= HASHKEY_SIZE); #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { hostname = conn->http_proxy.host.name; @@ -151,12 +149,12 @@ static void hashkey(struct connectdata *conn, char *buf, else hostname = conn->host.name; - if(hostp) - /* report back which name we used */ - *hostp = hostname; - - /* put the number first so that the hostname gets cut off if too long */ - msnprintf(buf, len, "%ld%s", port, hostname); + /* put the numbers first so that the hostname gets cut off if too long */ +#ifdef ENABLE_IPV6 + msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname); +#else + msnprintf(buf, len, "%ld/%s", port, hostname); +#endif Curl_strntolower(buf, buf, len); } @@ -179,27 +177,24 @@ size_t Curl_conncache_size(struct Curl_easy *data) struct connectbundle * Curl_conncache_find_bundle(struct Curl_easy *data, struct connectdata *conn, - struct conncache *connc, - const char **hostp) + struct conncache *connc) { struct connectbundle *bundle = NULL; CONNCACHE_LOCK(data); if(connc) { char key[HASHKEY_SIZE]; - hashkey(conn, key, sizeof(key), hostp); + hashkey(conn, key, sizeof(key)); bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); } return bundle; } -static bool conncache_add_bundle(struct conncache *connc, - char *key, - struct connectbundle *bundle) +static void *conncache_add_bundle(struct conncache *connc, + char *key, + struct connectbundle *bundle) { - void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle); - - return p?TRUE:FALSE; + return Curl_hash_add(&connc->hash, key, strlen(key), bundle); } static void conncache_remove_bundle(struct conncache *connc, @@ -235,10 +230,8 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data) DEBUGASSERT(conn); /* *find_bundle() locks the connection cache */ - bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache, - NULL); + bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache); if(!bundle) { - int rc; char key[HASHKEY_SIZE]; result = bundle_create(&bundle); @@ -246,10 +239,9 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data) goto unlock; } - hashkey(conn, key, sizeof(key), NULL); - rc = conncache_add_bundle(data->state.conn_cache, key, bundle); + hashkey(conn, key, sizeof(key)); - if(!rc) { + if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) { bundle_destroy(bundle); result = CURLE_OUT_OF_MEMORY; goto unlock; @@ -410,7 +402,7 @@ bool Curl_conncache_return_conn(struct Curl_easy *data, conn_candidate = Curl_conncache_extract_oldest(data); if(conn_candidate) { /* the winner gets the honour of being disconnected */ - (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); + Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); } } @@ -535,6 +527,7 @@ void Curl_conncache_close_all_connections(struct conncache *connc) { struct connectdata *conn; char buffer[READBUFFER_MIN + 1]; + SIGPIPE_VARIABLE(pipe_st); if(!connc->closure_handle) return; connc->closure_handle->state.buffer = buffer; @@ -542,27 +535,23 @@ void Curl_conncache_close_all_connections(struct conncache *connc) conn = conncache_find_first_connection(connc); while(conn) { - SIGPIPE_VARIABLE(pipe_st); sigpipe_ignore(connc->closure_handle, &pipe_st); /* This will remove the connection from the cache */ connclose(conn, "kill all"); Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE); - (void)Curl_disconnect(connc->closure_handle, conn, FALSE); + Curl_disconnect(connc->closure_handle, conn, FALSE); sigpipe_restore(&pipe_st); conn = conncache_find_first_connection(connc); } connc->closure_handle->state.buffer = NULL; - if(connc->closure_handle) { - SIGPIPE_VARIABLE(pipe_st); - sigpipe_ignore(connc->closure_handle, &pipe_st); + sigpipe_ignore(connc->closure_handle, &pipe_st); - Curl_hostcache_clean(connc->closure_handle, - connc->closure_handle->dns.hostcache); - Curl_close(&connc->closure_handle); - sigpipe_restore(&pipe_st); - } + Curl_hostcache_clean(connc->closure_handle, + connc->closure_handle->dns.hostcache); + Curl_close(&connc->closure_handle); + sigpipe_restore(&pipe_st); } #if 0 diff --git a/lib/conncache.h b/lib/conncache.h index e9c1e32..ef11dcf 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2015 - 2022, Daniel Stenberg, , et al. * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, * * This software is licensed as described in the file COPYING, which @@ -87,8 +87,7 @@ void Curl_conncache_destroy(struct conncache *connc); /* return the correct bundle, to a host or a proxy */ struct connectbundle *Curl_conncache_find_bundle(struct Curl_easy *data, struct connectdata *conn, - struct conncache *connc, - const char **hostp); + struct conncache *connc); /* returns number of connections currently held in the connection cache */ size_t Curl_conncache_size(struct Curl_easy *data); diff --git a/lib/connect.c b/lib/connect.c index 5252f97..9bcf525 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -74,6 +74,7 @@ #include "warnless.h" #include "conncache.h" #include "multihandle.h" +#include "share.h" #include "version_win32.h" #include "quic.h" #include "socks.h" @@ -137,6 +138,14 @@ tcpkeepalive(struct Curl_easy *data, (void *)&optval, sizeof(optval)) < 0) { infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd); } +#elif defined(TCP_KEEPALIVE) + /* Mac OS X style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); + } #endif #ifdef TCP_KEEPINTVL optval = curlx_sltosi(data->set.tcp_keepintvl); @@ -146,15 +155,6 @@ tcpkeepalive(struct Curl_easy *data, infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd); } #endif -#ifdef TCP_KEEPALIVE - /* Mac OS X style */ - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); - } -#endif #endif } } @@ -257,6 +257,9 @@ static CURLcode bindlocal(struct Curl_easy *data, #ifdef IP_BIND_ADDRESS_NO_PORT int on = 1; #endif +#ifndef ENABLE_IPV6 + (void)scope; +#endif /************************************************************* * Select device to bind socket to @@ -314,8 +317,11 @@ static CURLcode bindlocal(struct Curl_easy *data, } #endif - switch(Curl_if2ip(af, scope, conn->scope_id, dev, - myhost, sizeof(myhost))) { + switch(Curl_if2ip(af, +#ifdef ENABLE_IPV6 + scope, conn->scope_id, +#endif + dev, myhost, sizeof(myhost))) { case IF2IP_NOT_FOUND: if(is_interface) { /* Do not fall back to treating it as a host name */ @@ -617,6 +623,7 @@ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, data->info.conn_scheme = conn->handler->scheme; data->info.conn_protocol = conn->handler->protocol; data->info.conn_primary_port = conn->port; + data->info.conn_remote_port = conn->remote_port; data->info.conn_local_port = local_port; } @@ -1481,7 +1488,11 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, find.id_tofind = data->state.lastconnect_id; find.found = NULL; - Curl_conncache_foreach(data, data->multi_easy? + Curl_conncache_foreach(data, + data->share && (data->share->specifier + & (1<< CURL_LOCK_DATA_CONNECT))? + &data->share->conn_cache: + data->multi_easy? &data->multi_easy->conn_cache: &data->multi->conn_cache, &find, conn_is_conn); diff --git a/lib/cookie.c b/lib/cookie.c index d418efa..451881f 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1188,12 +1188,15 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, fp = stdin; fromfile = FALSE; } - else if(file && !*file) { - /* points to a "" string */ + else if(!file || !*file) { + /* points to an empty string or NULL */ fp = NULL; } - else - fp = file?fopen(file, FOPEN_READTEXT):NULL; + else { + fp = fopen(file, FOPEN_READTEXT); + if(!fp) + infof(data, "WARNING: failed to open cookie file \"%s\"", file); + } c->newsession = newsession; /* new session? */ @@ -1227,7 +1230,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, */ remove_expired(c); - if(fromfile) + if(fromfile && fp) fclose(fp); } diff --git a/lib/curl_base64.h b/lib/curl_base64.h index d48edc4..4cb9d73 100644 --- a/lib/curl_base64.h +++ b/lib/curl_base64.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,13 +22,10 @@ * ***************************************************************************/ -CURLcode Curl_base64_encode(struct Curl_easy *data, - const char *inputbuff, size_t insize, +CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen); -CURLcode Curl_base64url_encode(struct Curl_easy *data, - const char *inputbuff, size_t insize, +CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen); - CURLcode Curl_base64_decode(const char *src, unsigned char **outptr, size_t *outlen); diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index d2a0f43..6b55a3c 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -949,6 +949,9 @@ ${SIZEOF_TIME_T_CODE} /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 +/* to enable msh3 */ +#cmakedefine USE_MSH3 1 + /* if Unix domain sockets are enabled */ #cmakedefine USE_UNIX_SOCKETS diff --git a/lib/curl_ctype.c b/lib/curl_ctype.c index d6cd08a..233a69e 100644 --- a/lib/curl_ctype.c +++ b/lib/curl_ctype.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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" -#ifndef CURL_DOES_CONVERSIONS - #undef _U #define _U (1<<0) /* upper case */ #undef _L @@ -130,4 +128,3 @@ int Curl_iscntrl(int c) return (ascii[c] & (_C)); } -#endif /* !CURL_DOES_CONVERSIONS */ diff --git a/lib/curl_ctype.h b/lib/curl_ctype.h index 17dfaa0..2fa749d 100644 --- a/lib/curl_ctype.h +++ b/lib/curl_ctype.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,32 +24,6 @@ #include "curl_setup.h" -#ifdef CURL_DOES_CONVERSIONS - -/* - * Uppercase macro versions of ANSI/ISO is*() functions/macros which - * avoid negative number inputs with argument byte codes > 127. - * - * For non-ASCII platforms the C library character classification routines - * are used despite being locale-dependent, because this is better than - * not to work at all. - */ -#include - -#define ISSPACE(x) (isspace((int) ((unsigned char)x))) -#define ISDIGIT(x) (isdigit((int) ((unsigned char)x))) -#define ISALNUM(x) (isalnum((int) ((unsigned char)x))) -#define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x))) -#define ISGRAPH(x) (isgraph((int) ((unsigned char)x))) -#define ISALPHA(x) (isalpha((int) ((unsigned char)x))) -#define ISPRINT(x) (isprint((int) ((unsigned char)x))) -#define ISUPPER(x) (isupper((int) ((unsigned char)x))) -#define ISLOWER(x) (islower((int) ((unsigned char)x))) -#define ISCNTRL(x) (iscntrl((int) ((unsigned char)x))) -#define ISASCII(x) (isascii((int) ((unsigned char)x))) - -#else - int Curl_isspace(int c); int Curl_isdigit(int c); int Curl_isalnum(int c); @@ -73,8 +47,6 @@ int Curl_iscntrl(int c); #define ISCNTRL(x) (Curl_iscntrl((int) ((unsigned char)x))) #define ISASCII(x) (((x) >= 0) && ((x) <= 0x80)) -#endif - #define ISBLANK(x) (int)((((unsigned char)x) == ' ') || \ (((unsigned char)x) == '\t')) diff --git a/lib/curl_des.c b/lib/curl_des.c index 76185cb..5f28ef4 100644 --- a/lib/curl_des.c +++ b/lib/curl_des.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2021, Steve Holme, . + * Copyright (C) 2015 - 2022, Steve Holme, . * * 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,12 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && !defined(USE_OPENSSL) && !defined(USE_WOLFSSL) +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_NSS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) #include "curl_des.h" @@ -60,4 +65,4 @@ void Curl_des_set_odd_parity(unsigned char *bytes, size_t len) } } -#endif /* USE_NTLM && !USE_OPENSSL */ +#endif diff --git a/lib/curl_des.h b/lib/curl_des.h index 438706a..3d0fd92 100644 --- a/lib/curl_des.h +++ b/lib/curl_des.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2020, Steve Holme, . + * Copyright (C) 2015 - 2022, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,11 +24,16 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && !defined(USE_OPENSSL) +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_NSS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) /* Applies odd parity to the given byte array */ void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); -#endif /* USE_NTLM && !USE_OPENSSL */ +#endif #endif /* HEADER_CURL_DES_H */ diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 8f34056..1543a0f 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2011 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,7 +60,7 @@ OM_uint32 Curl_gss_init_sec_context( #ifdef GSS_C_DELEG_POLICY_FLAG req_flags |= GSS_C_DELEG_POLICY_FLAG; #else - infof(data, "warning: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " + infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " "compiled in"); #endif } diff --git a/lib/curl_multibyte.c b/lib/curl_multibyte.c index e9d2a8c..32c03a5 100644 --- a/lib/curl_multibyte.c +++ b/lib/curl_multibyte.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -104,7 +104,7 @@ int curlx_win32_open(const char *filename, int oflag, ...) #ifdef _UNICODE if(filename_w) { result = _wopen(filename_w, oflag, pmode); - free(filename_w); + curlx_unicodefree(filename_w); } else errno = EINVAL; @@ -124,8 +124,8 @@ FILE *curlx_win32_fopen(const char *filename, const char *mode) result = _wfopen(filename_w, mode_w); else errno = EINVAL; - free(filename_w); - free(mode_w); + curlx_unicodefree(filename_w); + curlx_unicodefree(mode_w); return result; #else return (fopen)(filename, mode); @@ -143,7 +143,7 @@ int curlx_win32_stat(const char *path, struct_stat *buffer) #else result = _wstati64(path_w, buffer); #endif - free(path_w); + curlx_unicodefree(path_w); } else errno = EINVAL; @@ -164,7 +164,7 @@ int curlx_win32_access(const char *path, int mode) wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); if(path_w) { result = _waccess(path_w, mode); - free(path_w); + curlx_unicodefree(path_w); } else errno = EINVAL; diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index ed123d0..f3b8b13 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -108,7 +108,6 @@ #endif #include "urldata.h" -#include "non-ascii.h" #include "strcase.h" #include "curl_ntlm_core.h" #include "curl_md5.h" @@ -402,11 +401,9 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, /* * Set up lanmanager hashed password */ -CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data, - const char *password, +CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, unsigned char *lmbuffer /* 21 bytes */) { - CURLcode result; unsigned char pw[14]; static const unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ @@ -416,14 +413,6 @@ CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data, Curl_strntoupper((char *)pw, password, len); memset(&pw[len], 0, 14 - len); - /* - * The LanManager hashed password needs to be created using the - * password in the network encoding not the host encoding. - */ - result = Curl_convert_to_network(data, (char *)pw, 14); - if(result) - return result; - { /* Create LanManager hashed password. */ @@ -455,7 +444,6 @@ CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data, return CURLE_OK; } -#ifdef USE_NTRESPONSES static void ascii_to_unicode_le(unsigned char *dest, const char *src, size_t srclen) { @@ -466,7 +454,7 @@ static void ascii_to_unicode_le(unsigned char *dest, const char *src, } } -#if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI) +#if !defined(USE_WINDOWS_SSPI) static void ascii_uppercase_to_unicode_le(unsigned char *dest, const char *src, size_t srclen) @@ -478,19 +466,17 @@ static void ascii_uppercase_to_unicode_le(unsigned char *dest, } } -#endif /* USE_NTLM_V2 && !USE_WINDOWS_SSPI */ +#endif /* !USE_WINDOWS_SSPI */ /* * Set up nt hashed passwords * @unittest: 1600 */ -CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, - const char *password, +CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, unsigned char *ntbuffer /* 21 bytes */) { size_t len = strlen(password); unsigned char *pw; - CURLcode result; if(len > SIZE_T_MAX/2) /* avoid integer overflow */ return CURLE_OUT_OF_MEMORY; pw = len ? malloc(len * 2) : (unsigned char *)strdup(""); @@ -499,22 +485,16 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, ascii_to_unicode_le(pw, password, len); - /* - * The NT hashed password needs to be created using the password in the - * network encoding not the host encoding. - */ - result = Curl_convert_to_network(data, (char *)pw, len * 2); - if(!result) { - /* Create NT hashed password. */ - Curl_md4it(ntbuffer, pw, 2 * len); - memset(ntbuffer + 16, 0, 21 - 16); - } + /* Create NT hashed password. */ + Curl_md4it(ntbuffer, pw, 2 * len); + memset(ntbuffer + 16, 0, 21 - 16); + free(pw); - return result; + return CURLE_OK; } -#if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI) +#if !defined(USE_WINDOWS_SSPI) /* Timestamp in tenths of a microsecond since January 1, 1601 00:00:00 UTC. */ struct ms_filetime { @@ -730,8 +710,6 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, return result; } -#endif /* USE_NTLM_V2 && !USE_WINDOWS_SSPI */ - -#endif /* USE_NTRESPONSES */ +#endif /* !USE_WINDOWS_SSPI */ #endif /* USE_CURL_NTLM_CORE */ diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index 02b39d4..5e52bb2 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,22 +42,6 @@ # include #endif -/* Define USE_NTRESPONSES in order to make the type-3 message include - * the NT response message. */ -#define USE_NTRESPONSES - -/* Define USE_NTLM2SESSION in order to make the type-3 message include the - NTLM2Session response message, requires USE_NTRESPONSES defined to 1 */ -#if defined(USE_NTRESPONSES) -#define USE_NTLM2SESSION -#endif - -/* Define USE_NTLM_V2 in order to allow the type-3 message to include the - LMv2 and NTLMv2 response messages, requires USE_NTRESPONSES defined to 1 */ -#if defined(USE_NTRESPONSES) -#define USE_NTLM_V2 -#endif - /* Helpers to generate function byte arguments in little endian order */ #define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ @@ -67,16 +51,13 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results); -CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data, - const char *password, +CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, unsigned char *lmbuffer /* 21 bytes */); -#ifdef USE_NTRESPONSES -CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, - const char *password, +CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, unsigned char *ntbuffer /* 21 bytes */); -#if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI) +#if !defined(USE_WINDOWS_SSPI) CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, const unsigned char *data, unsigned int datalen, @@ -98,9 +79,7 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, unsigned char *challenge_server, unsigned char *lmresp); -#endif /* USE_NTLM_V2 && !USE_WINDOWS_SSPI */ - -#endif /* USE_NTRESPONSES */ +#endif /* !USE_WINDOWS_SSPI */ #endif /* USE_CURL_NTLM_CORE */ diff --git a/lib/curl_path.c b/lib/curl_path.c index 6510618..a1669d1 100644 --- a/lib/curl_path.c +++ b/lib/curl_path.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,7 +40,7 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data, char *working_path; size_t working_path_len; CURLcode result = - Curl_urldecode(data, data->state.up.path, 0, &working_path, + Curl_urldecode(data->state.up.path, 0, &working_path, &working_path_len, REJECT_ZERO); if(result) return result; diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 8d39e4f..48d6625 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -48,7 +48,6 @@ #include "warnless.h" #include "strtok.h" #include "sendf.h" -#include "non-ascii.h" /* included for Curl_convert_... prototypes */ /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -283,8 +282,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, } /* Encode the outgoing SASL message. */ -static CURLcode build_message(struct SASL *sasl, struct Curl_easy *data, - struct bufref *msg) +static CURLcode build_message(struct SASL *sasl, struct bufref *msg) { CURLcode result = CURLE_OK; @@ -297,7 +295,7 @@ static CURLcode build_message(struct SASL *sasl, struct Curl_easy *data, char *base64; size_t base64len; - result = Curl_base64_encode(data, (const char *) Curl_bufref_ptr(msg), + result = Curl_base64_encode((const char *) Curl_bufref_ptr(msg), Curl_bufref_len(msg), &base64, &base64len); if(!result) Curl_bufref_set(msg, base64, base64len, curl_free); @@ -312,10 +310,10 @@ static CURLcode build_message(struct SASL *sasl, struct Curl_easy *data, * * Check if we have enough auth data and capabilities to authenticate. */ -bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn) +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) { /* Have credentials been provided? */ - if(conn->bits.user_passwd) + if(data->state.aptr.user) return TRUE; /* EXTERNAL can authenticate without a user name and/or password */ @@ -367,7 +365,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, if(force_ir || data->set.sasl_ir) result = Curl_auth_create_external_message(conn->user, &resp); } - else if(conn->bits.user_passwd) { + else if(data->state.aptr.user) { #if defined(USE_KERBEROS5) if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && Curl_auth_user_contains_domain(conn->user)) { @@ -494,7 +492,7 @@ 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(sasl, data, &resp); + result = build_message(sasl, &resp); if(sasl->params->maxirlen && strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen) @@ -672,7 +670,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, #endif case SASL_OAUTH2: - /* Create the authorisation message */ + /* Create the authorization message */ if(sasl->authused == SASL_MECH_OAUTHBEARER) { result = Curl_auth_create_oauth_bearer_message(conn->user, hostname, @@ -729,7 +727,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, newstate = SASL_CANCEL; break; case CURLE_OK: - result = build_message(sasl, data, &resp); + result = build_message(sasl, &resp); if(!result) result = sasl->params->contauth(data, sasl->curmech, &resp); break; diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index 91458c7..d377ae7 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -150,7 +150,7 @@ 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); +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data); /* Calculate the required login details for SASL authentication */ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 61384cd..0babb03 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -101,14 +101,6 @@ # include "config-os400.h" #endif -#ifdef TPF -# include "config-tpf.h" -#endif - -#ifdef __VXWORKS__ -# include "config-vxworks.h" -#endif - #ifdef __PLAN9__ # include "config-plan9.h" #endif @@ -274,22 +266,6 @@ # include #endif -#ifdef TPF -# include /* for bzero, strcasecmp, and strncasecmp */ -# include /* for strcpy and strlen */ -# include /* for rand and srand */ -# include /* for select and ioctl*/ -# include /* for in_addr_t definition */ -# include /* for tpf_process_signals */ - /* change which select is used for libcurl */ -# define select(a,b,c,d,e) tpf_select_libcurl(a,b,c,d,e) -#endif - -#ifdef __VXWORKS__ -# include /* for generic BSD socket functions */ -# include /* for basic I/O interface functions */ -#endif - #ifdef __AMIGA__ # include # include @@ -619,14 +595,6 @@ # endif #endif -#ifdef NETWARE -int netware_init(void); -#ifndef __NOVELL_LIBC__ -#include -#include -#endif -#endif - #if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN) /* The lib and header are present */ #define USE_LIBIDN2 @@ -641,7 +609,7 @@ int netware_init(void); #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ defined(USE_MBEDTLS) || \ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_SECTRANSP) || defined(USE_GSKIT) || defined(USE_MESALINK) || \ + defined(USE_SECTRANSP) || defined(USE_GSKIT) || \ defined(USE_BEARSSL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif @@ -804,6 +772,11 @@ endings either CRLF or LF so 't' is appropriate. #define CURLMAX(x,y) ((x)>(y)?(x):(y)) #define CURLMIN(x,y) ((x)<(y)?(x):(y)) +/* A convenience macro to provide both the string literal and the length of + the string literal in one go, useful for functions that take "string,len" + as their argument */ +#define STRCONST(x) x,sizeof(x)-1 + /* Some versions of the Android SDK is missing the declaration */ #if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING) struct passwd; @@ -821,7 +794,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define USE_HTTP2 #endif -#if defined(USE_NGTCP2) || defined(USE_QUICHE) +#if defined(USE_NGTCP2) || defined(USE_QUICHE) || defined(USE_MSH3) #define ENABLE_QUIC #endif diff --git a/lib/curl_sha256.h b/lib/curl_sha256.h index 55dc30a..2b7890a 100644 --- a/lib/curl_sha256.h +++ b/lib/curl_sha256.h @@ -8,7 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2017, Florin Petriuc, - * Copyright (C) 2018 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,7 +32,7 @@ extern const struct HMAC_params Curl_HMAC_SHA256[1]; /* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from * sha.h*/ #include -#include +#include #else #define SHA256_DIGEST_LENGTH 32 #endif diff --git a/lib/dict.c b/lib/dict.c index 5d53b8f..e23e661 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -96,13 +96,13 @@ const struct Curl_handler Curl_handler_dict = { PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ }; -static char *unescape_word(struct Curl_easy *data, const char *inputbuff) +static char *unescape_word(const char *inputbuff) { char *newp = NULL; char *dictp; size_t len; - CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len, + CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len, REJECT_NADA); if(!newp || result) return NULL; @@ -190,10 +190,6 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ - if(conn->bits.user_passwd) { - /* AUTH is missing */ - } - if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { @@ -226,7 +222,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) strategy = (char *)"."; } - eword = unescape_word(data, word); + eword = unescape_word(word); if(!eword) return CURLE_OUT_OF_MEMORY; @@ -274,7 +270,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) database = (char *)"!"; } - eword = unescape_word(data, word); + eword = unescape_word(word); if(!eword) return CURLE_OUT_OF_MEMORY; diff --git a/lib/doh.c b/lib/doh.c index d6a2167..4aef8b2 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -252,7 +252,7 @@ static CURLcode dohprobe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); -#ifdef USE_NGHTTP2 +#ifdef USE_HTTP2 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); #endif #ifndef CURLDEBUG @@ -530,7 +530,7 @@ static DOHcode store_cname(const unsigned char *doh, if(length) { if(Curl_dyn_len(c)) { - if(Curl_dyn_add(c, ".")) + if(Curl_dyn_addn(c, STRCONST("."))) return DOH_OUT_OF_MEM; } if((index + length) > dohlen) @@ -911,7 +911,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); - return data->conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: + return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { diff --git a/lib/dotdot.c b/lib/dotdot.c index 3a1435f..73ef2fa 100644 --- a/lib/dotdot.c +++ b/lib/dotdot.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,7 +32,7 @@ /* * "Remove Dot Segments" - * https://tools.ietf.org/html/rfc3986#section-5.2.4 + * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 */ /* diff --git a/lib/easy.c b/lib/easy.c index 20293a7..65d7464 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -68,7 +68,6 @@ #include "slist.h" #include "mime.h" #include "amigaos.h" -#include "non-ascii.h" #include "warnless.h" #include "multiif.h" #include "sigpipe.h" @@ -168,12 +167,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) } #endif -#ifdef NETWARE - if(netware_init()) { - DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n")); - } -#endif - if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); goto fail; @@ -933,8 +926,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) } #endif /* USE_ARES */ - Curl_convert_setup(outcurl); - Curl_initinfo(outcurl); outcurl->magic = CURLEASY_MAGIC_NUMBER; @@ -1111,7 +1102,7 @@ static CURLcode easy_connection(struct Curl_easy *data, /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ if(!data->set.connect_only) { - failf(data, "CONNECT_ONLY is required!"); + failf(data, "CONNECT_ONLY is required"); return CURLE_UNSUPPORTED_PROTOCOL; } diff --git a/lib/escape.c b/lib/escape.c index 683b6fc..ff58875 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,7 +29,6 @@ #include "urldata.h" #include "warnless.h" -#include "non-ascii.h" #include "escape.h" #include "strdup.h" /* The last 3 #include files should be in this order */ @@ -39,7 +38,7 @@ /* Portable character check (remember EBCDIC). Do not use isalnum() because its behavior is altered by the current locale. - See https://tools.ietf.org/html/rfc3986#section-2.3 + See https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 */ bool Curl_isunreserved(unsigned char in) { @@ -80,8 +79,8 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, int inlength) { size_t length; - CURLcode result; struct dynbuf d; + (void)data; if(inlength < 0) return NULL; @@ -102,16 +101,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, } else { /* encode it */ - char encoded[4]; - result = Curl_convert_to_network(data, (char *)&in, 1); - if(result) { - /* Curl_convert_to_network calls failf if unsuccessful */ - Curl_dyn_free(&d); - return NULL; - } - - msnprintf(encoded, sizeof(encoded), "%%%02X", in); - if(Curl_dyn_add(&d, encoded)) + if(Curl_dyn_addf(&d, "%%%02X", in)) return NULL; } string++; @@ -126,8 +116,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, * Returns a pointer to a malloced string in *ostring with length given in * *olen. If length == 0, the length is assumed to be strlen(string). * - * 'data' can be set to NULL but then this function can't convert network - * data to host for non-ascii. + * 'data' can be set to NULL * * ctrl options: * - REJECT_NADA: accept everything @@ -139,8 +128,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, * invokes that used TRUE/FALSE (0 and 1). */ -CURLcode Curl_urldecode(struct Curl_easy *data, - const char *string, size_t length, +CURLcode Curl_urldecode(const char *string, size_t length, char **ostring, size_t *olen, enum urlreject ctrl) { @@ -148,7 +136,6 @@ CURLcode Curl_urldecode(struct Curl_easy *data, char *ns; size_t strindex = 0; unsigned long hex; - CURLcode result = CURLE_OK; DEBUGASSERT(string); DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ @@ -174,15 +161,6 @@ CURLcode Curl_urldecode(struct Curl_easy *data, in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ - if(data) { - result = Curl_convert_from_network(data, (char *)&in, 1); - if(result) { - /* Curl_convert_from_network calls failf if unsuccessful */ - free(ns); - return result; - } - } - string += 2; alloc -= 2; } @@ -218,10 +196,11 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string, int length, int *olen) { char *str = NULL; + (void)data; if(length >= 0) { size_t inputlen = length; size_t outputlen; - CURLcode res = Curl_urldecode(data, string, inputlen, &str, &outputlen, + CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, REJECT_NADA); if(res) return NULL; diff --git a/lib/escape.h b/lib/escape.h index 46cb590..0266883 100644 --- a/lib/escape.h +++ b/lib/escape.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,8 +32,7 @@ enum urlreject { REJECT_ZERO }; -CURLcode Curl_urldecode(struct Curl_easy *data, - const char *string, size_t length, +CURLcode Curl_urldecode(const char *string, size_t length, char **ostring, size_t *olen, enum urlreject ctrl); diff --git a/lib/file.c b/lib/file.c index 0420db3..3da79a2 100644 --- a/lib/file.c +++ b/lib/file.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -147,7 +147,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) #endif size_t real_path_len; - CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path, + CURLcode result = Curl_urldecode(data->state.up.path, 0, &real_path, &real_path_len, REJECT_ZERO); if(result) return result; diff --git a/lib/formdata.c b/lib/formdata.c index ac7a000..5fefd7a 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,7 +33,6 @@ #include "urldata.h" /* for struct Curl_easy */ #include "mime.h" -#include "non-ascii.h" #include "vtls/vtls.h" #include "strcase.h" #include "sendf.h" @@ -77,10 +76,15 @@ AddHttpPost(char *name, size_t namelength, struct curl_httppost **last_post) { struct curl_httppost *post; + if(!namelength && name) + namelength = strlen(name); + if((bufferlength > LONG_MAX) || (namelength > LONG_MAX)) + /* avoid overflow in typecasts below */ + return NULL; post = calloc(1, sizeof(struct curl_httppost)); if(post) { post->name = name; - post->namelength = (long)(name?(namelength?namelength:strlen(name)):0); + post->namelength = (long)namelength; post->contents = value; post->contentlen = contentslength; post->buffer = buffer; @@ -269,14 +273,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, * Set the Name property. */ case CURLFORM_PTRNAME: -#ifdef CURL_DOES_CONVERSIONS - /* Treat CURLFORM_PTR like CURLFORM_COPYNAME so that libcurl will copy - * the data in all cases so that we'll have safe memory for the eventual - * conversion. - */ -#else current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ -#endif + /* FALLTHROUGH */ case CURLFORM_COPYNAME: if(current_form->name) @@ -901,11 +899,6 @@ CURLcode Curl_getformdata(struct Curl_easy *data, else uclen = (size_t)clen; result = curl_mime_data(part, post->contents, uclen); -#ifdef CURL_DOES_CONVERSIONS - /* Convert textual contents now. */ - if(!result && data && part->datasize) - result = Curl_convert_to_network(data, part->data, part->datasize); -#endif } } diff --git a/lib/ftp.c b/lib/ftp.c index f6921e4..128cdc2 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -76,7 +76,6 @@ #include "speedcheck.h" #include "warnless.h" #include "http_proxy.h" -#include "non-ascii.h" #include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -592,7 +591,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data, * This response code can come at any point so having it treated * generically is a good idea. */ - infof(data, "We got a 421 - timeout!"); + infof(data, "We got a 421 - timeout"); state(data, FTP_STOP); return CURLE_OPERATION_TIMEDOUT; } @@ -1030,8 +1029,11 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(*addr != '\0') { /* attempt to get the address of the given interface name */ switch(Curl_if2ip(conn->ip_addr->ai_family, +#ifdef ENABLE_IPV6 Curl_ipv6_scope(conn->ip_addr->ai_addr), - conn->scope_id, addr, hbuf, sizeof(hbuf))) { + conn->scope_id, +#endif + addr, hbuf, sizeof(hbuf))) { case IF2IP_NOT_FOUND: /* not an interface, use the given string as host name instead */ host = addr; @@ -1163,7 +1165,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* maybe all ports were in use already*/ if(port > port_max) { - failf(data, "bind() failed, we ran out of ports!"); + failf(data, "bind() failed, we ran out of ports"); Curl_closesocket(data, conn, portsock); return CURLE_FTP_PORT_FAILED; } @@ -1460,7 +1462,7 @@ static CURLcode ftp_state_list(struct Curl_easy *data) /* url-decode before evaluation: e.g. paths starting/ending with %2f */ const char *slashPos = NULL; char *rawPath = NULL; - result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, REJECT_CTRL); + result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL); if(result) return result; @@ -2700,7 +2702,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]); if(Curl_sec_login(data, conn)) - infof(data, "Logging in with password in cleartext!"); + infof(data, "Logging in with password in cleartext"); else infof(data, "Authentication successful"); } @@ -3247,7 +3249,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(!result) /* get the url-decoded "raw" path */ - result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, + result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL); if(result) { /* We can limp along anyway (and should try to since we may already be in @@ -3379,7 +3381,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, (ftp->transfer == PPTRANSFER_BODY)) { failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes)", - data->req.bytecount, data->state.infilesize); + data->req.writebytecount, data->state.infilesize); result = CURLE_PARTIAL_FILE; } } @@ -3402,7 +3404,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, else if(!ftpc->dont_check && !data->req.bytecount && (data->req.size>0)) { - failf(data, "No data was received!"); + failf(data, "No data was received"); result = CURLE_FTP_COULDNT_RETR_FILE; } } @@ -4131,9 +4133,11 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) ftpc->cwdfail = FALSE; /* url-decode ftp path before further evaluation */ - result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL); - if(result) + result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL); + if(result) { + failf(data, "path contains control characters"); return result; + } switch(data->set.ftp_filemethod) { case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ @@ -4231,7 +4235,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) if(data->set.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { /* We need a file name when uploading. Return error! */ - failf(data, "Uploading to a URL without a file name!"); + failf(data, "Uploading to a URL without a file name"); free(rawPath); return CURLE_URL_MALFORMAT; } diff --git a/lib/gopher.c b/lib/gopher.c index f61232f..0a3ba8f 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -165,7 +165,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) newp += 2; /* ... and finally unescape */ - result = Curl_urldecode(data, newp, 0, &sel, &len, REJECT_ZERO); + result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO); free(gopherpath); if(result) return result; diff --git a/lib/h2h3.c b/lib/h2h3.c new file mode 100644 index 0000000..c0ed58d --- /dev/null +++ b/lib/h2h3.c @@ -0,0 +1,310 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" +#include "urldata.h" +#include "h2h3.h" +#include "transfer.h" +#include "sendf.h" +#include "strcase.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_pseudo_headers() creates the array with pseudo headers to be + * used in a HTTP/2 or HTTP/3 request. + */ + +#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +/* USHRT_MAX is 65535 == 0xffff */ +#define HEADER_OVERFLOW(x) \ + (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen) + +/* + * Check header memory for the token "trailers". + * Parse the tokens as separated by comma and surrounded by whitespace. + * Returns TRUE if found or FALSE if not. + */ +static bool contains_trailers(const char *p, size_t len) +{ + const char *end = p + len; + for(;;) { + for(; p != end && (*p == ' ' || *p == '\t'); ++p) + ; + if(p == end || (size_t)(end - p) < sizeof("trailers") - 1) + return FALSE; + if(strncasecompare("trailers", p, sizeof("trailers") - 1)) { + p += sizeof("trailers") - 1; + for(; p != end && (*p == ' ' || *p == '\t'); ++p) + ; + if(p == end || *p == ',') + return TRUE; + } + /* skip to next token */ + for(; p != end && *p != ','; ++p) + ; + if(p == end) + return FALSE; + ++p; + } +} + +typedef enum { + /* Send header to server */ + HEADERINST_FORWARD, + /* Don't send header to server */ + HEADERINST_IGNORE, + /* Discard header, and replace it with "te: trailers" */ + HEADERINST_TE_TRAILERS +} header_instruction; + +/* Decides how to treat given header field. */ +static header_instruction inspect_header(const char *name, size_t namelen, + const char *value, size_t valuelen) { + switch(namelen) { + case 2: + if(!strncasecompare("te", name, namelen)) + return HEADERINST_FORWARD; + + return contains_trailers(value, valuelen) ? + HEADERINST_TE_TRAILERS : HEADERINST_IGNORE; + case 7: + return strncasecompare("upgrade", name, namelen) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + case 10: + return (strncasecompare("connection", name, namelen) || + strncasecompare("keep-alive", name, namelen)) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + case 16: + return strncasecompare("proxy-connection", name, namelen) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + case 17: + return strncasecompare("transfer-encoding", name, namelen) ? + HEADERINST_IGNORE : HEADERINST_FORWARD; + default: + return HEADERINST_FORWARD; + } +} + +CURLcode Curl_pseudo_headers(struct Curl_easy *data, + const char *mem, /* the request */ + const size_t len /* size of request */, + struct h2h3req **hp) +{ + struct connectdata *conn = data->conn; + size_t nheader = 0; + size_t i; + size_t authority_idx; + char *hdbuf = (char *)mem; + char *end, *line_end; + struct h2h3pseudo *nva = NULL; + struct h2h3req *hreq = NULL; + char *vptr; + + /* Calculate number of headers contained in [mem, mem + len). Assumes a + correctly generated HTTP header field block. */ + for(i = 1; i < len; ++i) { + if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { + ++nheader; + ++i; + } + } + if(nheader < 2) { + goto fail; + } + /* We counted additional 2 \r\n in the first and last line. We need 3 + new headers: :method, :path and :scheme. Therefore we need one + more space. */ + nheader += 1; + hreq = malloc(sizeof(struct h2h3req) + + sizeof(struct h2h3pseudo) * (nheader - 1)); + if(!hreq) { + goto fail; + } + + nva = &hreq->header[0]; + + /* Extract :method, :path from request line + We do line endings with CRLF so checking for CR is enough */ + line_end = memchr(hdbuf, '\r', len); + if(!line_end) { + goto fail; + } + + /* Method does not contain spaces */ + end = memchr(hdbuf, ' ', line_end - hdbuf); + if(!end || end == hdbuf) + goto fail; + nva[0].name = H2H3_PSEUDO_METHOD; + nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1; + nva[0].value = hdbuf; + nva[0].valuelen = (size_t)(end - hdbuf); + + hdbuf = end + 1; + + /* Path may contain spaces so scan backwards */ + end = NULL; + for(i = (size_t)(line_end - hdbuf); i; --i) { + if(hdbuf[i - 1] == ' ') { + end = &hdbuf[i - 1]; + break; + } + } + if(!end || end == hdbuf) + goto fail; + nva[1].name = H2H3_PSEUDO_PATH; + nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1; + nva[1].value = hdbuf; + nva[1].valuelen = (end - hdbuf); + + nva[2].name = H2H3_PSEUDO_SCHEME; + nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1; + vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME)); + if(vptr) { + vptr += sizeof(H2H3_PSEUDO_SCHEME); + while(*vptr && ISSPACE(*vptr)) + vptr++; + nva[2].value = vptr; + infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr); + } + else { + if(conn->handler->flags & PROTOPT_SSL) + nva[2].value = "https"; + else + nva[2].value = "http"; + } + nva[2].valuelen = strlen((char *)nva[2].value); + + authority_idx = 0; + i = 3; + while(i < nheader) { + size_t hlen; + + hdbuf = line_end + 2; + + /* check for next CR, but only within the piece of data left in the given + buffer */ + line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); + if(!line_end || (line_end == hdbuf)) + goto fail; + + /* header continuation lines are not supported */ + if(*hdbuf == ' ' || *hdbuf == '\t') + goto fail; + + for(end = hdbuf; end < line_end && *end != ':'; ++end) + ; + if(end == hdbuf || end == line_end) + goto fail; + hlen = end - hdbuf; + + if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { + authority_idx = i; + nva[i].name = H2H3_PSEUDO_AUTHORITY; + nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1; + } + else { + nva[i].namelen = (size_t)(end - hdbuf); + /* Lower case the header name for HTTP/3 */ + Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); + nva[i].name = hdbuf; + } + hdbuf = end + 1; + while(*hdbuf == ' ' || *hdbuf == '\t') + ++hdbuf; + end = line_end; + + switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, + end - hdbuf)) { + case HEADERINST_IGNORE: + /* skip header fields prohibited by HTTP/2 specification. */ + --nheader; + continue; + case HEADERINST_TE_TRAILERS: + nva[i].value = "trailers"; + nva[i].valuelen = sizeof("trailers") - 1; + break; + default: + nva[i].value = hdbuf; + nva[i].valuelen = (end - hdbuf); + } + + nva[i].value = hdbuf; + nva[i].valuelen = (end - hdbuf); + + ++i; + } + + /* :authority must come before non-pseudo header fields */ + if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { + struct h2h3pseudo authority = nva[authority_idx]; + for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { + nva[i] = nva[i - 1]; + } + nva[i] = authority; + } + + /* Warn stream may be rejected if cumulative length of headers is too + large. */ +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + { + size_t acc = 0; + + for(i = 0; i < nheader; ++i) { + acc += nva[i].namelen + nva[i].valuelen; + + infof(data, "h2h3 [%.*s: %.*s]", + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + + if(acc > MAX_ACC) { + infof(data, "http_request: Warning: The cumulative length of all " + "headers exceeds %d bytes and that could cause the " + "stream to be rejected.", MAX_ACC); + } + } + + hreq->entries = nheader; + *hp = hreq; + + return CURLE_OK; + + fail: + free(hreq); + return CURLE_OUT_OF_MEMORY; +} + +void Curl_pseudo_free(struct h2h3req *hp) +{ + free(hp); +} + +#endif /* USE_NGHTTP2 or HTTP/3 enabled */ diff --git a/lib/h2h3.h b/lib/h2h3.h new file mode 100644 index 0000000..2225684 --- /dev/null +++ b/lib/h2h3.h @@ -0,0 +1,59 @@ +#ifndef HEADER_CURL_H2H3_H +#define HEADER_CURL_H2H3_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curl_setup.h" + +#define H2H3_PSEUDO_METHOD ":method" +#define H2H3_PSEUDO_SCHEME ":scheme" +#define H2H3_PSEUDO_AUTHORITY ":authority" +#define H2H3_PSEUDO_PATH ":path" +#define H2H3_PSEUDO_STATUS ":status" + +struct h2h3pseudo { + const char *name; + size_t namelen; + const char *value; + size_t valuelen; +}; + +struct h2h3req { + size_t entries; + struct h2h3pseudo header[1]; /* the array is allocated to contain entries */ +}; + +/* + * Curl_pseudo_headers() creates the array with pseudo headers to be + * used in a HTTP/2 or HTTP/3 request. Returns an allocated struct. + * Free it with Curl_pseudo_free(). + */ +CURLcode Curl_pseudo_headers(struct Curl_easy *data, + const char *request, + const size_t len, + struct h2h3req **hp); + +/* + * Curl_pseudo_free() frees a h2h3req struct. + */ +void Curl_pseudo_free(struct h2h3req *hp); + +#endif /* HEADER_CURL_H2H3_H */ diff --git a/lib/headers.c b/lib/headers.c new file mode 100644 index 0000000..226c696 --- /dev/null +++ b/lib/headers.c @@ -0,0 +1,324 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include "urldata.h" +#include "strdup.h" +#include "strcase.h" +#include "headers.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HEADERS_API) + +/* Generate the curl_header struct for the user. This function MUST assign all + struct fields in the output struct. */ +static void copy_header_external(struct Curl_easy *data, + struct Curl_header_store *hs, + size_t index, + size_t amount, + struct Curl_llist_element *e, + struct curl_header **hout) +{ + struct curl_header *h = *hout = &data->state.headerout; + h->name = hs->name; + h->value = hs->value; + h->amount = amount; + h->index = index; + /* this will randomly OR a reserved bit for the sole purpose of making it + impossible for applications to do == comparisons, as that would otherwise + be very tempting and then lead to the reserved bits not being reserved + anymore. */ + h->origin = hs->type | (1<<27); + h->anchor = e; +} + +/* public API */ +CURLHcode curl_easy_header(CURL *easy, + const char *name, + size_t nameindex, + unsigned int type, + int request, + struct curl_header **hout) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *e_pick = NULL; + struct Curl_easy *data = easy; + size_t match = 0; + size_t amount = 0; + struct Curl_header_store *hs = NULL; + struct Curl_header_store *pick = NULL; + if(!name || !hout || !data || + (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX)) || + !type || (request < -1)) + return CURLHE_BAD_ARGUMENT; + if(!Curl_llist_count(&data->state.httphdrs)) + return CURLHE_NOHEADERS; /* no headers available */ + if(request > data->state.requests) + return CURLHE_NOREQUEST; + if(request == -1) + request = data->state.requests; + + /* we need a first round to count amount of this header */ + for(e = data->state.httphdrs.head; e; e = e->next) { + hs = e->ptr; + if(strcasecompare(hs->name, name) && + (hs->type & type) && + (hs->request == request)) { + amount++; + pick = hs; + e_pick = e; + } + } + if(!amount) + return CURLHE_MISSING; + else if(nameindex >= amount) + return CURLHE_BADINDEX; + + if(nameindex == amount - 1) + /* if the last or only occurrence is what's asked for, then we know it */ + hs = pick; + else { + for(e = data->state.httphdrs.head; e; e = e->next) { + hs = e->ptr; + if(strcasecompare(hs->name, name) && + (hs->type & type) && + (hs->request == request) && + (match++ == nameindex)) { + e_pick = e; + break; + } + } + if(!e) /* this shouldn't happen */ + return CURLHE_MISSING; + } + /* this is the name we want */ + copy_header_external(data, hs, nameindex, amount, e_pick, hout); + return CURLHE_OK; +} + +/* public API */ +struct curl_header *curl_easy_nextheader(CURL *easy, + unsigned int type, + int request, + struct curl_header *prev) +{ + struct Curl_easy *data = easy; + struct Curl_llist_element *pick; + struct Curl_llist_element *e; + struct Curl_header_store *hs; + struct curl_header *hout; + size_t amount = 0; + size_t index = 0; + + if(request > data->state.requests) + return NULL; + if(request == -1) + request = data->state.requests; + + if(prev) { + pick = prev->anchor; + if(!pick) + /* something is wrong */ + return NULL; + pick = pick->next; + } + else + pick = data->state.httphdrs.head; + + if(pick) { + /* make sure it is the next header of the desired type */ + do { + hs = pick->ptr; + if((hs->type & type) && (hs->request == request)) + break; + pick = pick->next; + } while(pick); + } + + if(!pick) + /* no more headers available */ + return NULL; + + hs = pick->ptr; + + /* count number of occurrences of this name within the mask and figure out + the index for the currently selected entry */ + for(e = data->state.httphdrs.head; e; e = e->next) { + struct Curl_header_store *check = e->ptr; + if(strcasecompare(hs->name, check->name) && + (check->request == request) && + (check->type & type)) + amount++; + if(e == pick) + index = amount - 1; + } + + copy_header_external(data, hs, index, amount, pick, &hout); + return hout; +} + +static CURLcode namevalue(char *header, size_t hlen, unsigned int type, + char **name, char **value) +{ + char *end = header + hlen - 1; /* point to the last byte */ + DEBUGASSERT(hlen); + *name = header; + + if(type == CURLH_PSEUDO) { + if(*header != ':') + return CURLE_BAD_FUNCTION_ARGUMENT; + header++; + } + + /* Find the end of the header name */ + while(*header && (*header != ':')) + ++header; + + if(*header) + /* Skip over colon, null it */ + *header++ = 0; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + + /* skip all leading space letters */ + while(*header && ISSPACE(*header)) + header++; + + *value = header; + + /* skip all trailing space letters */ + while((end > header) && ISSPACE(*end)) + *end-- = 0; /* nul terminate */ + return CURLE_OK; +} + +/* + * Curl_headers_push() gets passed a full HTTP header to store. It gets called + * immediately before the header callback. The header is CRLF terminated. + */ +CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, + unsigned char type) +{ + char *value = NULL; + char *name = NULL; + char *end; + size_t hlen; /* length of the incoming header */ + struct Curl_header_store *hs; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if((header[0] == '\r') || (header[0] == '\n')) + /* ignore the body separator */ + return CURLE_OK; + + end = strchr(header, '\r'); + if(!end) { + end = strchr(header, '\n'); + if(!end) + return CURLE_BAD_FUNCTION_ARGUMENT; + } + hlen = end - header + 1; + + hs = calloc(1, sizeof(*hs) + hlen); + if(!hs) + return CURLE_OUT_OF_MEMORY; + memcpy(hs->buffer, header, hlen); + hs->buffer[hlen] = 0; /* nul terminate */ + + result = namevalue(hs->buffer, hlen, type, &name, &value); + if(result) + goto fail; + + hs->name = name; + hs->value = value; + hs->type = type; + hs->request = data->state.requests; + + /* insert this node into the list of headers */ + Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, + hs, &hs->node); + + return CURLE_OK; + fail: + free(hs); + return result; +} + +/* + * Curl_headers_init(). Init the headers subsystem. + */ +static void headers_init(struct Curl_easy *data) +{ + Curl_llist_init(&data->state.httphdrs, NULL); +} + +/* + * Curl_headers_cleanup(). Free all stored headers and associated memory. + */ +CURLcode Curl_headers_cleanup(struct Curl_easy *data) +{ + struct Curl_llist_element *e; + struct Curl_llist_element *n; + + for(e = data->state.httphdrs.head; e; e = n) { + struct Curl_header_store *hs = e->ptr; + n = e->next; + free(hs); + } + headers_init(data); + return CURLE_OK; +} + +#else /* HTTP-disabled builds below */ + +CURLHcode curl_easy_header(CURL *easy, + const char *name, + size_t index, + unsigned int origin, + int request, + struct curl_header **hout) +{ + (void)easy; + (void)name; + (void)index; + (void)origin; + (void)request; + (void)hout; + return CURLHE_NOT_BUILT_IN; +} + +struct curl_header *curl_easy_nextheader(CURL *easy, + unsigned int type, + int request, + struct curl_header *prev) +{ + (void)easy; + (void)type; + (void)request; + (void)prev; + return NULL; +} +#endif diff --git a/lib/headers.h b/lib/headers.h new file mode 100644 index 0000000..48c013b --- /dev/null +++ b/lib/headers.h @@ -0,0 +1,53 @@ +#ifndef HEADER_CURL_HEADER_H +#define HEADER_CURL_HEADER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curl_setup.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HEADERS_API) + +struct Curl_header_store { + struct Curl_llist_element node; + char *name; /* points into 'buffer' */ + char *value; /* points into 'buffer */ + int request; /* 0 is the first request, then 1.. 2.. */ + unsigned char type; /* CURLH_* defines */ + char buffer[1]; /* this is the raw header blob */ +}; + +/* + * Curl_headers_push() gets passed a full header to store. + */ +CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, + unsigned char type); + +/* + * Curl_headers_cleanup(). Free all stored headers and associated memory. + */ +CURLcode Curl_headers_cleanup(struct Curl_easy *data); + +#else +#define Curl_headers_push(x,y,z) CURLE_OK +#define Curl_headers_cleanup(x) Curl_nop_stmt +#endif + +#endif /* HEADER_CURL_HEADER_H */ diff --git a/lib/hmac.c b/lib/hmac.c index 590abe6..85b175d 100644 --- a/lib/hmac.c +++ b/lib/hmac.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -39,8 +39,8 @@ * Generic HMAC algorithm. * * This module computes HMAC digests based on any hash function. Parameters - * and computing procedures are set-up dynamically at HMAC computation - * context initialisation. + * and computing procedures are set-up dynamically at HMAC computation context + * initialization. */ static const unsigned char hmac_ipad = 0x36; diff --git a/lib/hostcheck.c b/lib/hostcheck.c deleted file mode 100644 index cd45bd0..0000000 --- a/lib/hostcheck.c +++ /dev/null @@ -1,142 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_OPENSSL) \ - || defined(USE_GSKIT) \ - || defined(USE_SCHANNEL) -/* these backends use functions from this file */ - -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_NETINET_IN6_H -#include -#endif - -#include "hostcheck.h" -#include "strcase.h" -#include "hostip.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * Match a hostname against a wildcard pattern. - * E.g. - * "foo.host.com" matches "*.host.com". - * - * We use the matching rule described in RFC6125, section 6.4.3. - * https://tools.ietf.org/html/rfc6125#section-6.4.3 - * - * In addition: ignore trailing dots in the host names and wildcards, so that - * the names are used normalized. This is what the browsers do. - * - * Do not allow wildcard matching on IP numbers. There are apparently - * certificates being used with an IP address in the CN field, thus making no - * apparent distinction between a name and an IP. We need to detect the use of - * an IP address and not wildcard match on such names. - * - * NOTE: hostmatch() gets called with copied buffers so that it can modify the - * contents at will. - */ - -static int hostmatch(char *hostname, char *pattern) -{ - const char *pattern_label_end, *pattern_wildcard, *hostname_label_end; - int wildcard_enabled; - size_t prefixlen, suffixlen; - - /* normalize pattern and hostname by stripping off trailing dots */ - size_t len = strlen(hostname); - if(hostname[len-1]=='.') - hostname[len-1] = 0; - len = strlen(pattern); - if(pattern[len-1]=='.') - pattern[len-1] = 0; - - pattern_wildcard = strchr(pattern, '*'); - if(!pattern_wildcard) - return strcasecompare(pattern, hostname) ? - CURL_HOST_MATCH : CURL_HOST_NOMATCH; - - /* detect IP address as hostname and fail the match if so */ - if(Curl_host_is_ipnum(hostname)) - return CURL_HOST_NOMATCH; - - /* We require at least 2 dots in pattern to avoid too wide wildcard - match. */ - wildcard_enabled = 1; - pattern_label_end = strchr(pattern, '.'); - if(!pattern_label_end || !strchr(pattern_label_end + 1, '.') || - pattern_wildcard > pattern_label_end || - strncasecompare(pattern, "xn--", 4)) { - wildcard_enabled = 0; - } - if(!wildcard_enabled) - return strcasecompare(pattern, hostname) ? - CURL_HOST_MATCH : CURL_HOST_NOMATCH; - - hostname_label_end = strchr(hostname, '.'); - if(!hostname_label_end || - !strcasecompare(pattern_label_end, hostname_label_end)) - return CURL_HOST_NOMATCH; - - /* The wildcard must match at least one character, so the left-most - label of the hostname is at least as large as the left-most label - of the pattern. */ - if(hostname_label_end - hostname < pattern_label_end - pattern) - return CURL_HOST_NOMATCH; - - prefixlen = pattern_wildcard - pattern; - suffixlen = pattern_label_end - (pattern_wildcard + 1); - return strncasecompare(pattern, hostname, prefixlen) && - strncasecompare(pattern_wildcard + 1, hostname_label_end - suffixlen, - suffixlen) ? - CURL_HOST_MATCH : CURL_HOST_NOMATCH; -} - -int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) -{ - int res = 0; - if(!match_pattern || !*match_pattern || - !hostname || !*hostname) /* sanity check */ - ; - else { - char *matchp = strdup(match_pattern); - if(matchp) { - char *hostp = strdup(hostname); - if(hostp) { - if(hostmatch(hostp, matchp) == CURL_HOST_MATCH) - res = 1; - free(hostp); - } - free(matchp); - } - } - - return res; -} - -#endif /* OPENSSL, GSKIT or schannel+wince */ diff --git a/lib/hostcheck.h b/lib/hostcheck.h deleted file mode 100644 index 52155f4..0000000 --- a/lib/hostcheck.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HEADER_CURL_HOSTCHECK_H -#define HEADER_CURL_HOSTCHECK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include - -#define CURL_HOST_NOMATCH 0 -#define CURL_HOST_MATCH 1 -int Curl_cert_hostcheck(const char *match_pattern, const char *hostname); - -#endif /* HEADER_CURL_HOSTCHECK_H */ diff --git a/lib/hostip.c b/lib/hostip.c index 911d5ed..7f6bbac 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -945,7 +945,7 @@ clean_up: less than 1! */ alarm(1); rc = CURLRESOLV_TIMEDOUT; - failf(data, "Previous alarm fired off!"); + failf(data, "Previous alarm fired off"); } else alarm((unsigned int)alarm_set); @@ -1131,7 +1131,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) ai = Curl_str2addr(address, port); if(!ai) { - infof(data, "Resolve address '%s' found illegal!", address); + infof(data, "Resolve address '%s' found illegal", address); goto err; } @@ -1150,7 +1150,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) error = false; err: if(error) { - failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!", + failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'", hostp->data); Curl_freeaddrinfo(head); return CURLE_SETOPT_OPTION_SYNTAX; @@ -1167,8 +1167,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); if(dns) { - infof(data, "RESOLVE %s:%d is - old addresses discarded!", - hostname, port); + infof(data, "RESOLVE %s:%d is - old addresses discarded", + hostname, port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. 2. even if entry with correct addresses is already in the cache, @@ -1220,6 +1220,7 @@ CURLcode Curl_resolv_check(struct Curl_easy *data, struct Curl_dns_entry **dns) { #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) + (void)data; (void)dns; #endif #ifndef CURL_DISABLE_DOH diff --git a/lib/hsts.c b/lib/hsts.c index 052dc11..03fcc9e 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2020 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,7 +21,7 @@ ***************************************************************************/ /* * The Strict-Transport-Security header is defined in RFC 6797: - * https://tools.ietf.org/html/rfc6797 + * https://datatracker.ietf.org/doc/html/rfc6797 */ #include "curl_setup.h" diff --git a/lib/http.c b/lib/http.c index f08a343..0d5c449 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -77,7 +77,6 @@ #include "content_encoding.h" #include "http_proxy.h" #include "warnless.h" -#include "non-ascii.h" #include "http2.h" #include "connect.h" #include "strdup.h" @@ -216,10 +215,10 @@ static CURLcode http_setup_conn(struct Curl_easy *data, */ char *Curl_checkProxyheaders(struct Curl_easy *data, const struct connectdata *conn, - const char *thisheader) + const char *thisheader, + const size_t thislen) { struct curl_slist *head; - size_t thislen = strlen(thisheader); for(head = (conn->bits.proxy && data->set.sep_headers) ? data->set.proxyheaders : data->set.headers; @@ -233,7 +232,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, } #else /* disabled */ -#define Curl_checkProxyheaders(x,y,z) NULL +#define Curl_checkProxyheaders(x,y,z,a) NULL #endif /* @@ -327,7 +326,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) if(!out) return CURLE_OUT_OF_MEMORY; - result = Curl_base64_encode(data, out, strlen(out), &authorization, &size); + result = Curl_base64_encode(out, strlen(out), &authorization, &size); if(result) goto fail; @@ -588,7 +587,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.authproblem) return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK; - if((conn->bits.user_passwd || data->set.str[STRING_BEARER]) && + if((data->state.aptr.user || data->set.str[STRING_BEARER]) && ((data->req.httpcode == 401) || (conn->bits.authneg && data->req.httpcode < 300))) { pickhost = pickoneauth(&data->state.authhost, authmask); @@ -667,6 +666,7 @@ output_auth_headers(struct Curl_easy *data, { const char *auth = NULL; CURLcode result = CURLE_OK; + (void)conn; #ifdef CURL_DISABLE_CRYPTO_AUTH (void)request; @@ -725,10 +725,10 @@ output_auth_headers(struct Curl_easy *data, if( #ifndef CURL_DISABLE_PROXY (proxy && conn->bits.proxy_user_passwd && - !Curl_checkProxyheaders(data, conn, "Proxy-authorization")) || + !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || #endif - (!proxy && conn->bits.user_passwd && - !Curl_checkheaders(data, "Authorization"))) { + (!proxy && data->state.aptr.user && + !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; result = http_output_basic(data, proxy); if(result) @@ -742,7 +742,7 @@ output_auth_headers(struct Curl_easy *data, if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ if((!proxy && data->set.str[STRING_BEARER] && - !Curl_checkheaders(data, "Authorization"))) { + !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Bearer"; result = http_output_bearer(data); if(result) @@ -775,6 +775,21 @@ output_auth_headers(struct Curl_easy *data, return CURLE_OK; } +/* + * Curl_allow_auth_to_host() tells if authentication, cookies or other + * "sensitive data" can (still) be sent to this host. + */ +bool Curl_allow_auth_to_host(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + return (!data->state.this_is_a_follow || + data->set.allow_auth_to_other_hosts || + (data->state.first_host && + strcasecompare(data->state.first_host, conn->host.name) && + (data->state.first_remote_port == conn->remote_port) && + (data->state.first_remote_protocol == conn->handler->protocol))); +} + /** * Curl_http_output_auth() setups the authentication headers for the * host/proxy and the correct authentication @@ -811,7 +826,7 @@ Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || #endif - conn->bits.user_passwd || data->set.str[STRING_BEARER]) + data->state.aptr.user || data->set.str[STRING_BEARER]) /* continue please */; else { authhost->done = TRUE; @@ -847,17 +862,14 @@ Curl_http_output_auth(struct Curl_easy *data, with it */ authproxy->done = TRUE; - /* To prevent the user+password to get sent to other than the original - host due to a location-follow, we do some weirdo checks here */ - if(!data->state.this_is_a_follow || + /* To prevent the user+password to get sent to other than the original host + due to a location-follow */ + if(Curl_allow_auth_to_host(data) #ifndef CURL_DISABLE_NETRC - conn->bits.netrc || + || conn->bits.netrc #endif - !data->state.first_host || - data->set.allow_auth_to_other_hosts || - strcasecompare(data->state.first_host, conn->host.name)) { + ) result = output_auth_headers(data, conn, authhost, request, path, FALSE); - } else authhost->done = TRUE; @@ -1143,7 +1155,7 @@ static bool http_should_fail(struct Curl_easy *data) ** Either we're not authenticating, or we're supposed to ** be authenticating something else. This is an error. */ - if((httpcode == 401) && !data->conn->bits.user_passwd) + if((httpcode == 401) && !data->state.aptr.user) return TRUE; #ifndef CURL_DISABLE_PROXY if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) @@ -1251,14 +1263,6 @@ CURLcode Curl_buffer_send(struct dynbuf *in, DEBUGASSERT(size > (size_t)included_body_bytes); - result = Curl_convert_to_network(data, ptr, headersize); - /* Curl_convert_to_network calls failf if unsuccessful */ - if(result) { - /* conversion failed, free memory and return to the caller */ - Curl_dyn_free(in); - return result; - } - if((conn->handler->flags & PROTOPT_SSL #ifndef CURL_DISABLE_PROXY || conn->http_proxy.proxytype == CURLPROXY_HTTPS @@ -1425,18 +1429,22 @@ CURLcode Curl_buffer_send(struct dynbuf *in, bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ - const char *content) /* content string to find */ + const size_t hlen, /* len of the keyword in bytes */ + const char *content, /* content string to find */ + const size_t clen) /* len of the content in bytes */ { /* RFC2616, section 4.2 says: "Each header field consists of a name followed * by a colon (":") and the field value. Field names are case-insensitive. * The field value MAY be preceded by any amount of LWS, though a single SP * is preferred." */ - size_t hlen = strlen(header); - size_t clen; size_t len; const char *start; const char *end; + DEBUGASSERT(hlen); + DEBUGASSERT(clen); + DEBUGASSERT(header); + DEBUGASSERT(content); if(!strncasecompare(headerline, header, hlen)) return FALSE; /* doesn't start with header */ @@ -1460,7 +1468,6 @@ Curl_compareheader(const char *headerline, /* line to check */ } len = end-start; /* length of the content part of the input line */ - clen = strlen(content); /* length of the word to find */ /* find the content string in the rest of the line */ for(; len >= clen; len--, start++) { @@ -1546,7 +1553,7 @@ static CURLcode add_haproxy_protocol_header(struct Curl_easy *data) #ifdef USE_UNIX_SOCKETS if(data->conn->unix_domain_socket) /* the buffer is large enough to hold this! */ - result = Curl_dyn_add(&req, "PROXY UNKNOWN\r\n"); + result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n")); else { #endif /* Emit the correct prefix for IPv6 */ @@ -1713,13 +1720,13 @@ static CURLcode expect100(struct Curl_easy *data, /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an Expect: 100-continue to the headers which actually speeds up post operations (as there is one packet coming back from the web server) */ - const char *ptr = Curl_checkheaders(data, "Expect"); + const char *ptr = Curl_checkheaders(data, STRCONST("Expect")); if(ptr) { data->state.expect100header = - Curl_compareheader(ptr, "Expect:", "100-continue"); + Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); } else { - result = Curl_dyn_add(req, "Expect: 100-continue\r\n"); + result = Curl_dyn_addn(req, STRCONST("Expect: 100-continue\r\n")); if(!result) data->state.expect100header = TRUE; } @@ -1772,7 +1779,7 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, return result; } else - infof(handle, "Malformatted trailing header ! Skipping trailer."); + infof(handle, "Malformatted trailing header, skipping trailer"); trailers = trailers->next; } result = Curl_dyn_add(b, endofline_network); @@ -1867,7 +1874,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, ptr = optr; } } - if(ptr) { + if(ptr && (ptr != headers->data)) { /* we require a colon for this to be a true header */ ptr++; /* pass the colon */ @@ -1910,10 +1917,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, checkprefix("Cookie:", compare)) && /* be careful of sending this potentially sensitive header to other hosts */ - (data->state.this_is_a_follow && - data->state.first_host && - !data->set.allow_auth_to_other_hosts && - !strcasecompare(data->state.first_host, conn->host.name))) + !Curl_allow_auth_to_host(data)) ; else { #ifdef USE_HYPER @@ -1949,6 +1953,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, CURLcode result; char datestr[80]; const char *condp; + size_t len; if(data->set.timecondition == CURL_TIMECOND_NONE) /* no condition was asked for */ @@ -1967,16 +1972,19 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, case CURL_TIMECOND_IFMODSINCE: condp = "If-Modified-Since"; + len = 17; break; case CURL_TIMECOND_IFUNMODSINCE: condp = "If-Unmodified-Since"; + len = 19; break; case CURL_TIMECOND_LASTMOD: condp = "Last-Modified"; + len = 13; break; } - if(Curl_checkheaders(data, condp)) { + if(Curl_checkheaders(data, condp, len)) { /* A custom header was specified; it will be sent instead. */ return CURLE_OK; } @@ -2065,7 +2073,7 @@ CURLcode Curl_http_useragent(struct Curl_easy *data) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, "User-Agent")) { + if(Curl_checkheaders(data, STRCONST("User-Agent"))) { free(data->state.aptr.uagent); data->state.aptr.uagent = NULL; } @@ -2085,10 +2093,11 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) return CURLE_OUT_OF_MEMORY; data->state.first_remote_port = conn->remote_port; + data->state.first_remote_protocol = conn->handler->protocol; } Curl_safefree(data->state.aptr.host); - ptr = Curl_checkheaders(data, "Host"); + ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || strcasecompare(data->state.first_host, conn->host.name))) { #if !defined(CURL_DISABLE_COOKIES) @@ -2305,7 +2314,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, #ifndef CURL_DISABLE_MIME if(http->sendit) { - const char *cthdr = Curl_checkheaders(data, "Content-Type"); + const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); /* Read and seek body only. */ http->sendit->flags |= MIME_BODY_ONLY; @@ -2330,11 +2339,12 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, } #endif - ptr = Curl_checkheaders(data, "Transfer-Encoding"); + ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); if(ptr) { /* Some kind of TE is requested, check if 'chunked' is chosen */ data->req.upload_chunky = - Curl_compareheader(ptr, "Transfer-Encoding:", "chunked"); + Curl_compareheader(ptr, + STRCONST("Transfer-Encoding:"), STRCONST("chunked")); } else { if((conn->handler->protocol & PROTO_FAMILY_HTTP) && @@ -2394,7 +2404,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, http->postsize = data->state.infilesize; if((http->postsize != -1) && !data->req.upload_chunky && - (conn->bits.authneg || !Curl_checkheaders(data, "Content-Length"))) { + (conn->bits.authneg || + !Curl_checkheaders(data, STRCONST("Content-Length")))) { /* only add Content-Length if not uploading chunked */ result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", http->postsize); @@ -2409,7 +2420,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, } /* end of headers */ - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; @@ -2434,7 +2445,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, /* This is form posting using mime data. */ if(conn->bits.authneg) { /* nothing to post! */ - result = Curl_dyn_add(r, "Content-Length: 0\r\n\r\n"); + result = Curl_dyn_addn(r, STRCONST("Content-Length: 0\r\n\r\n")); if(result) return result; @@ -2454,7 +2465,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, we don't upload data chunked, as RFC2616 forbids us to set both kinds of headers (Transfer-Encoding: chunked and Content-Length) */ if(http->postsize != -1 && !data->req.upload_chunky && - (conn->bits.authneg || !Curl_checkheaders(data, "Content-Length"))) { + (conn->bits.authneg || + !Curl_checkheaders(data, STRCONST("Content-Length")))) { /* we allow replacing this header if not during auth negotiation, although it isn't very wise to actually set your own */ result = Curl_dyn_addf(r, @@ -2481,10 +2493,10 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, the somewhat bigger ones we allow the app to disable it. Just make sure that the expect100header is always set to the preferred value here. */ - ptr = Curl_checkheaders(data, "Expect"); + ptr = Curl_checkheaders(data, STRCONST("Expect")); if(ptr) { data->state.expect100header = - Curl_compareheader(ptr, "Expect:", "100-continue"); + Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); } else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { result = expect100(data, conn, r); @@ -2495,7 +2507,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, data->state.expect100header = FALSE; /* make the request end in a true CRLF */ - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; @@ -2534,7 +2546,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, we don't upload data chunked, as RFC2616 forbids us to set both kinds of headers (Transfer-Encoding: chunked and Content-Length) */ if((http->postsize != -1) && !data->req.upload_chunky && - (conn->bits.authneg || !Curl_checkheaders(data, "Content-Length"))) { + (conn->bits.authneg || + !Curl_checkheaders(data, STRCONST("Content-Length")))) { /* we allow replacing this header if not during auth negotiation, although it isn't very wise to actually set your own */ result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T @@ -2543,9 +2556,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; } - if(!Curl_checkheaders(data, "Content-Type")) { - result = Curl_dyn_add(r, "Content-Type: application/" - "x-www-form-urlencoded\r\n"); + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(r, STRCONST("Content-Type: application/" + "x-www-form-urlencoded\r\n")); if(result) return result; } @@ -2554,10 +2567,10 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, the somewhat bigger ones we allow the app to disable it. Just make sure that the expect100header is always set to the preferred value here. */ - ptr = Curl_checkheaders(data, "Expect"); + ptr = Curl_checkheaders(data, STRCONST("Expect")); if(ptr) { data->state.expect100header = - Curl_compareheader(ptr, "Expect:", "100-continue"); + Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); } else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { result = expect100(data, conn, r); @@ -2584,7 +2597,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, get the data duplicated with malloc() and family. */ /* end of headers! */ - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; @@ -2606,12 +2619,12 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, result = Curl_dyn_addn(r, data->set.postfields, (size_t)http->postsize); if(!result) - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); included_body += 2; } } if(!result) { - result = Curl_dyn_add(r, "\x30\x0d\x0a\x0d\x0a"); + result = Curl_dyn_addn(r, STRCONST("\x30\x0d\x0a\x0d\x0a")); /* 0 CR LF CR LF */ included_body += 5; } @@ -2634,7 +2647,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, Curl_pgrsSetUploadSize(data, http->postsize); /* end of headers! */ - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; } @@ -2643,14 +2656,14 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, #endif { /* end of headers! */ - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; if(data->req.upload_chunky && conn->bits.authneg) { /* Chunky upload is selected and we're negotiating auth still, send end-of-data only */ - result = Curl_dyn_add(r, (char *)"\x30\x0d\x0a\x0d\x0a"); + result = Curl_dyn_addn(r, (char *)STRCONST("\x30\x0d\x0a\x0d\x0a")); /* 0 CR LF CR LF */ if(result) return result; @@ -2678,7 +2691,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, break; default: - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; @@ -2702,7 +2715,8 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, { CURLcode result = CURLE_OK; char *addcookies = NULL; - if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie")) + if(data->set.str[STRING_COOKIE] && + !Curl_checkheaders(data, STRCONST("Cookie"))) addcookies = data->set.str[STRING_COOKIE]; if(data->cookies || addcookies) { @@ -2728,7 +2742,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, while(co) { if(co->value) { if(0 == count) { - result = Curl_dyn_add(r, "Cookie: "); + result = Curl_dyn_addn(r, STRCONST("Cookie: ")); if(result) break; } @@ -2744,14 +2758,14 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, } if(addcookies && !result) { if(!count) - result = Curl_dyn_add(r, "Cookie: "); + result = Curl_dyn_addn(r, STRCONST("Cookie: ")); if(!result) { result = Curl_dyn_addf(r, "%s%s", count?"; ":"", addcookies); count++; } } if(count && !result) - result = Curl_dyn_add(r, "\r\n"); + result = Curl_dyn_addn(r, STRCONST("\r\n")); if(result) return result; @@ -2770,14 +2784,14 @@ CURLcode Curl_http_range(struct Curl_easy *data, * ones if any such are specified. */ if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && - !Curl_checkheaders(data, "Range")) { + !Curl_checkheaders(data, STRCONST("Range"))) { /* if a line like this was already allocated, free the previous one */ free(data->state.aptr.rangeline); data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n", data->state.range); } else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && - !Curl_checkheaders(data, "Content-Range")) { + !Curl_checkheaders(data, STRCONST("Content-Range"))) { /* if a line like this was already allocated, free the previous one */ free(data->state.aptr.rangeline); @@ -2923,7 +2937,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, /* The resume point is at the end of file, consider this fine even if it doesn't allow resume from here. */ infof(data, "The entire document is already downloaded"); - connclose(conn, "already downloaded"); + streamclose(conn, "already downloaded"); /* Abort download */ k->keepon &= ~KEEP_RECV; *done = TRUE; @@ -2948,10 +2962,10 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, /* We're simulating a http 304 from server so we return what should have been returned from the server */ data->info.httpcode = 304; - infof(data, "Simulate a HTTP 304 response!"); + infof(data, "Simulate a HTTP 304 response"); /* we abort the transfer before it is completed == we ruin the re-use ability. Close the connection */ - connclose(conn, "Simulated 304 handling"); + streamclose(conn, "Simulated 304 handling"); return CURLE_OK; } } /* we have a time condition */ @@ -2962,14 +2976,14 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, #ifdef HAVE_LIBZ CURLcode Curl_transferencode(struct Curl_easy *data) { - if(!Curl_checkheaders(data, "TE") && + if(!Curl_checkheaders(data, STRCONST("TE")) && data->set.http_transfer_encoding) { /* When we are to insert a TE: header in the request, we must also insert TE in a Connection: header, so we need to merge the custom provided Connection: header and prevent the original to get sent. Note that if the user has inserted his/her own TE: header we don't do this magic but then assume that the user will handle it all! */ - char *cptr = Curl_checkheaders(data, "Connection"); + char *cptr = Curl_checkheaders(data, STRCONST("Connection")); #define TE_HEADER "TE: gzip\r\n" Curl_safefree(data->state.aptr.te); @@ -3089,13 +3103,13 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, "Referer")) { + if(data->state.referer && !Curl_checkheaders(data, STRCONST("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_checkheaders(data, "Accept-Encoding") && + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { Curl_safefree(data->state.aptr.accept_encoding); data->state.aptr.accept_encoding = @@ -3117,7 +3131,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result) return result; - p_accept = Curl_checkheaders(data, "Accept")?NULL:"Accept: */*\r\n"; + p_accept = Curl_checkheaders(data, + STRCONST("Accept"))?NULL:"Accept: */*\r\n"; result = Curl_http_resume(data, conn, httpreq); if(result) @@ -3147,7 +3162,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } #ifndef CURL_DISABLE_ALTSVC - if(conn->bits.altused && !Curl_checkheaders(data, "Alt-Used")) { + if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { altused = aprintf("Alt-Used: %s:%d\r\n", conn->conn_to_host.name, conn->conn_to_port); if(!altused) { @@ -3194,8 +3209,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) #ifndef CURL_DISABLE_PROXY (conn->bits.httpproxy && !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, "Proxy-Connection") && - !Curl_checkProxyheaders(data, conn, "Proxy-Connection"))? + !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && + !Curl_checkProxyheaders(data, + conn, + STRCONST("Proxy-Connection")))? "Proxy-Connection: Keep-Alive\r\n":"", #else "", @@ -3308,20 +3325,6 @@ checkhttpprefix(struct Curl_easy *data, struct curl_slist *head = data->set.http200aliases; statusline rc = STATUS_BAD; statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN; -#ifdef CURL_DOES_CONVERSIONS - /* convert from the network encoding using a scratch area */ - char *scratch = strdup(s); - if(!scratch) { - failf(data, "Failed to allocate memory for conversion!"); - return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */ - } - if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s) + 1)) { - /* Curl_convert_from_network calls failf if unsuccessful */ - free(scratch); - return FALSE; /* can't return CURLE_foobar so return FALSE */ - } - s = scratch; -#endif /* CURL_DOES_CONVERSIONS */ while(head) { if(checkprefixmax(head->data, s, len)) { @@ -3334,9 +3337,6 @@ checkhttpprefix(struct Curl_easy *data, if((rc != STATUS_DONE) && (checkprefixmax("HTTP/", s, len))) rc = onmatch; -#ifdef CURL_DOES_CONVERSIONS - free(scratch); -#endif /* CURL_DOES_CONVERSIONS */ return rc; } @@ -3347,26 +3347,9 @@ checkrtspprefix(struct Curl_easy *data, { statusline result = STATUS_BAD; statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN; - -#ifdef CURL_DOES_CONVERSIONS - /* convert from the network encoding using a scratch area */ - char *scratch = strdup(s); - if(!scratch) { - failf(data, "Failed to allocate memory for conversion!"); - return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */ - } - if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s) + 1)) { - /* Curl_convert_from_network calls failf if unsuccessful */ - result = FALSE; /* can't return CURLE_foobar so return FALSE */ - } - else if(checkprefixmax("RTSP/", scratch, len)) - result = onmatch; - free(scratch); -#else (void)data; /* unused */ if(checkprefixmax("RTSP/", s, len)) result = onmatch; -#endif /* CURL_DOES_CONVERSIONS */ return result; } @@ -3412,7 +3395,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, return CURLE_FILESIZE_EXCEEDED; } streamclose(conn, "overflow content-length"); - infof(data, "Overflow Content-Length: value!"); + infof(data, "Overflow Content-Length: value"); } else { /* negative or just rubbish - bad HTTP */ @@ -3436,7 +3419,9 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, #ifndef CURL_DISABLE_PROXY else if((conn->httpversion == 10) && conn->bits.httpproxy && - Curl_compareheader(headp, "Proxy-Connection:", "keep-alive")) { + Curl_compareheader(headp, + STRCONST("Proxy-Connection:"), + STRCONST("keep-alive"))) { /* * When a HTTP/1.0 reply comes when using a proxy, the * 'Proxy-Connection: keep-alive' line tells us the @@ -3444,21 +3429,25 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, * Default action for 1.0 is to close. */ connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */ - infof(data, "HTTP/1.0 proxy connection set to keep alive!"); + infof(data, "HTTP/1.0 proxy connection set to keep alive"); } else if((conn->httpversion == 11) && conn->bits.httpproxy && - Curl_compareheader(headp, "Proxy-Connection:", "close")) { + Curl_compareheader(headp, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) { /* * We get a HTTP/1.1 response from a proxy and it says it'll * close down after this transfer. */ connclose(conn, "Proxy-Connection: asked to close after done"); - infof(data, "HTTP/1.1 proxy connection set close!"); + infof(data, "HTTP/1.1 proxy connection set close"); } #endif else if((conn->httpversion == 10) && - Curl_compareheader(headp, "Connection:", "keep-alive")) { + Curl_compareheader(headp, + STRCONST("Connection:"), + STRCONST("keep-alive"))) { /* * A HTTP/1.0 reply with the 'Connection: keep-alive' line * tells us the connection will be kept alive for our @@ -3466,9 +3455,10 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, * * [RFC2068, section 19.7.1] */ connkeep(conn, "Connection keep-alive"); - infof(data, "HTTP/1.0 connection set to keep alive!"); + infof(data, "HTTP/1.0 connection set to keep alive"); } - else if(Curl_compareheader(headp, "Connection:", "close")) { + else if(Curl_compareheader(headp, + STRCONST("Connection:"), STRCONST("close"))) { /* * [RFC 2616, section 8.1.2.1] * "Connection: close" is HTTP/1.1 language and means that @@ -3795,6 +3785,29 @@ CURLcode Curl_http_size(struct Curl_easy *data) return CURLE_OK; } +static CURLcode verify_header(struct Curl_easy *data) +{ + struct SingleRequest *k = &data->req; + const char *header = Curl_dyn_ptr(&data->state.headerb); + size_t hlen = Curl_dyn_len(&data->state.headerb); + char *ptr = memchr(header, 0x00, hlen); + if(ptr) { + /* this is bad, bail out */ + failf(data, "Nul byte in header"); + return CURLE_WEIRD_SERVER_REPLY; + } + if(k->headerline < 2) + /* the first "header" is the status-line and it has no colon */ + return CURLE_OK; + ptr = memchr(header, ':', hlen); + if(!ptr) { + /* this is bad, bail out */ + failf(data, "Header without colon"); + return CURLE_WEIRD_SERVER_REPLY; + } + return CURLE_OK; +} + /* * Read any HTTP header lines from the server and pass them to the client app. */ @@ -3903,21 +3916,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, size_t headerlen; /* Zero-length header line means end of headers! */ -#ifdef CURL_DOES_CONVERSIONS - if(0x0d == *headp) { - *headp = '\r'; /* replace with CR in host encoding */ - headp++; /* pass the CR byte */ - } - if(0x0a == *headp) { - *headp = '\n'; /* replace with LF in host encoding */ - headp++; /* pass the LF byte */ - } -#else if('\r' == *headp) headp++; /* pass the \r byte */ if('\n' == *headp) headp++; /* pass the \n byte */ -#endif /* CURL_DOES_CONVERSIONS */ if(100 <= k->httpcode && 199 >= k->httpcode) { /* "A user agent MAY ignore unexpected 1xx status responses." */ @@ -4028,9 +4030,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* now, only output this if the header AND body are requested: */ - writetype = CLIENTWRITE_HEADER; - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; + writetype = CLIENTWRITE_HEADER | + (data->set.include_header ? CLIENTWRITE_BODY : 0) | + ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); headerlen = Curl_dyn_len(&data->state.headerb); result = Curl_client_write(data, writetype, @@ -4127,7 +4129,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(conn->bits.rewindaftersend) { /* We rewind after a complete send, so thus we continue sending now */ - infof(data, "Keep sending data to get tossed away!"); + infof(data, "Keep sending data to get tossed away"); k->keepon |= KEEP_SEND; } } @@ -4183,36 +4185,18 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * Checks for special headers coming up. */ + writetype = CLIENTWRITE_HEADER; if(!k->headerline++) { /* This is the first header, it MUST be the error code line or else we consider this to be the body right away! */ int httpversion_major; int rtspversion_major; int nc = 0; -#ifdef CURL_DOES_CONVERSIONS -#define HEADER1 scratch -#define SCRATCHSIZE 21 - CURLcode res; - char scratch[SCRATCHSIZE + 1]; /* "HTTP/major.minor 123" */ - /* We can't really convert this yet because we don't know if it's the - 1st header line or the body. So we do a partial conversion into a - scratch area, leaving the data at 'headp' as-is. - */ - strncpy(&scratch[0], headp, SCRATCHSIZE); - scratch[SCRATCHSIZE] = 0; /* null terminate */ - res = Curl_convert_from_network(data, - &scratch[0], - SCRATCHSIZE); - if(res) - /* Curl_convert_from_network calls failf if unsuccessful */ - return res; -#else #define HEADER1 headp /* no conversion needed, just use headp */ -#endif /* CURL_DOES_CONVERSIONS */ if(conn->handler->protocol & PROTO_FAMILY_HTTP) { /* - * https://tools.ietf.org/html/rfc7230#section-3.1.2 + * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 * * The response code is always a three-digit number in HTTP as the spec * says. We allow any three-digit number here, but we cannot make @@ -4254,10 +4238,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, switch(httpversion) { case 10: case 11: -#if defined(USE_NGHTTP2) || defined(USE_HYPER) +#ifdef USE_HTTP2 case 20: #endif -#if defined(ENABLE_QUIC) +#ifdef ENABLE_QUIC case 30: #endif conn->httpversion = (unsigned char)httpversion; @@ -4326,6 +4310,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, result = Curl_http_statusline(data, conn); if(result) return result; + writetype |= CLIENTWRITE_STATUS; } else { k->header = FALSE; /* this is not a header line */ @@ -4333,8 +4318,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } - result = Curl_convert_from_network(data, headp, strlen(headp)); - /* Curl_convert_from_network calls failf if unsuccessful */ + result = verify_header(data); if(result) return result; @@ -4345,10 +4329,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* * End of header-checks. Write them to the client. */ - - writetype = CLIENTWRITE_HEADER; if(data->set.include_header) writetype |= CLIENTWRITE_BODY; + if(k->httpcode/100 == 1) + writetype |= CLIENTWRITE_1XX; Curl_debug(data, CURLINFO_HEADER_IN, headp, Curl_dyn_len(&data->state.headerb)); diff --git a/lib/http.h b/lib/http.h index b4aaba2..c4ab3c2 100644 --- a/lib/http.h +++ b/lib/http.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,10 @@ typedef enum { #include #endif +#if defined(_WIN32) && defined(ENABLE_QUIC) +#include +#endif + extern const struct Curl_handler Curl_handler_http; #ifdef USE_SSL @@ -47,13 +51,16 @@ extern const struct Curl_handler Curl_handler_https; /* Header specific functions */ bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ - const char *content); /* content string to find */ + const size_t hlen, /* len of the keyword in bytes */ + const char *content, /* content string to find */ + const size_t clen); /* len of the content in bytes */ char *Curl_copy_header_value(const char *header); char *Curl_checkProxyheaders(struct Curl_easy *data, const struct connectdata *conn, - const char *thisheader); + const char *thisheader, + const size_t thislen); CURLcode Curl_buffer_send(struct dynbuf *in, struct Curl_easy *data, curl_off_t *bytes_written, @@ -160,6 +167,29 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); struct h3out; /* see ngtcp2 */ #endif +#ifdef USE_MSH3 +#ifdef _WIN32 +#define msh3_lock CRITICAL_SECTION +#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) +#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) +#define msh3_lock_acquire(lock) EnterCriticalSection(lock) +#define msh3_lock_release(lock) LeaveCriticalSection(lock) +#else /* !_WIN32 */ +#include +#define msh3_lock pthread_mutex_t +#define msh3_lock_initialize(lock) { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(lock, &attr); \ + pthread_mutexattr_destroy(&attr); \ +} +#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) +#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) +#define msh3_lock_release(lock) pthread_mutex_unlock(lock) +#endif /* _WIN32 */ +#endif /* USE_MSH3 */ + /**************************************************************************** * HTTP unique setup ***************************************************************************/ @@ -225,11 +255,13 @@ struct HTTP { #endif #ifdef ENABLE_QUIC +#ifndef USE_MSH3 /*********** for HTTP/3 we store stream-local data here *************/ int64_t stream3_id; /* stream we are interested in */ bool firstheader; /* FALSE until headers arrive */ bool firstbody; /* FALSE until body arrives */ bool h3req; /* FALSE until request is issued */ +#endif bool upload_done; #endif #ifdef USE_NGHTTP3 @@ -237,6 +269,21 @@ struct HTTP { struct h3out *h3out; /* per-stream buffers for upload */ struct dynbuf overflow; /* excess data received during a single Curl_read */ #endif +#ifdef USE_MSH3 + struct MSH3_REQUEST *req; + msh3_lock recv_lock; + /* Receive Buffer (Headers and Data) */ + uint8_t* recv_buf; + size_t recv_buf_alloc; + /* Receive Headers */ + size_t recv_header_len; + bool recv_header_complete; + /* Receive Data */ + size_t recv_data_len; + bool recv_data_complete; + /* General Receive Error */ + CURLcode recv_error; +#endif }; #ifdef USE_NGHTTP2 @@ -317,4 +364,10 @@ Curl_http_output_auth(struct Curl_easy *data, bool proxytunnel); /* TRUE if this is the request setting up the proxy tunnel */ +/* + * Curl_allow_auth_to_host() tells if authentication, cookies or other + * "sensitive data" can (still) be sent to this host. + */ +bool Curl_allow_auth_to_host(struct Curl_easy *data); + #endif /* HEADER_CURL_HTTP_H */ diff --git a/lib/http2.c b/lib/http2.c index e74400a..0120b86 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,7 +36,10 @@ #include "connect.h" #include "strtoofft.h" #include "strdup.h" +#include "transfer.h" #include "dynbuf.h" +#include "h2h3.h" +#include "headers.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -64,7 +67,6 @@ #define H2BUGF(x) do { } while(0) #endif - static ssize_t http2_recv(struct Curl_easy *data, int sockindex, char *mem, size_t len, CURLcode *err); static bool http2_connisdead(struct Curl_easy *data, @@ -200,9 +202,9 @@ static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn) nread = ((Curl_recv *)httpc->recv_underlying)( data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); if(nread != -1) { - infof(data, - "%d bytes stray data read before trying h2 connection", - (int)nread); + H2BUGF(infof(data, + "%d bytes stray data read before trying h2 connection", + (int)nread)); httpc->nread_inbuf = 0; httpc->inbuflen = nread; if(h2_process_pending_input(data, httpc, &result) < 0) @@ -513,7 +515,7 @@ static int set_transfer_url(struct Curl_easy *data, if(!u) return 5; - v = curl_pushheader_byname(hp, ":scheme"); + v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME); if(v) { uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); if(uc) { @@ -522,7 +524,7 @@ static int set_transfer_url(struct Curl_easy *data, } } - v = curl_pushheader_byname(hp, ":authority"); + v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY); if(v) { uc = curl_url_set(u, CURLUPART_HOST, v, 0); if(uc) { @@ -531,7 +533,7 @@ static int set_transfer_url(struct Curl_easy *data, } } - v = curl_pushheader_byname(hp, ":path"); + v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH); if(v) { uc = curl_url_set(u, CURLUPART_PATH, v, 0); if(uc) { @@ -560,7 +562,7 @@ static int push_promise(struct Curl_easy *data, const nghttp2_push_promise *frame) { int rv; /* one of the CURL_PUSH_* defines */ - H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!", + H2BUGF(infof(data, "PUSH_PROMISE received, stream %u", frame->promised_stream_id)); if(data->multi->push_cb) { struct HTTP *stream; @@ -580,11 +582,11 @@ static int push_promise(struct Curl_easy *data, heads.data = data; heads.frame = frame; /* ask the application */ - H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!")); + H2BUGF(infof(data, "Got PUSH_PROMISE, ask application")); stream = data->req.p.http; if(!stream) { - failf(data, "Internal NULL stream!"); + failf(data, "Internal NULL stream"); (void)Curl_close(&newhandle); rv = CURL_PUSH_DENY; goto fail; @@ -651,7 +653,7 @@ static int push_promise(struct Curl_easy *data, Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS); } else { - H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!")); + H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it")); rv = CURL_PUSH_DENY; } fail: @@ -757,7 +759,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, stream->status_code = -1; } - result = Curl_dyn_add(&stream->header_recvbuf, "\r\n"); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -800,7 +802,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, } break; default: - H2BUGF(infof(data_s, "Got frame type %x for stream %u!", + H2BUGF(infof(data_s, "Got frame type %x for stream %u", frame->hd.type, stream_id)); break; } @@ -823,10 +825,14 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* get the stream from the hash based on Stream ID */ data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) - /* Receiving a Stream ID not in the hash should not happen, this is an - internal error more than anything else! */ - return NGHTTP2_ERR_CALLBACK_FAILURE; + if(!data_s) { + /* Receiving a Stream ID not in the hash should not happen - unless + we have aborted a transfer artificially and there were more data + in the pipeline. Silently ignore. */ + H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n", + stream_id)); + return 0; + } stream = data_s->req.p.http; if(!stream) @@ -907,15 +913,15 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, /* remove the entry from the hash as the stream is now gone */ rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); if(rv) { - infof(data_s, "http/2: failed to clear user_data for stream %d!", + infof(data_s, "http/2: failed to clear user_data for stream %d", stream_id); DEBUGASSERT(0); } if(stream_id == httpc->pause_stream_id) { - H2BUGF(infof(data_s, "Stopped the pause stream!")); + H2BUGF(infof(data_s, "Stopped the pause stream")); httpc->pause_stream_id = 0; } - H2BUGF(infof(data_s, "Removed stream %u hash!", stream_id)); + H2BUGF(infof(data_s, "Removed stream %u hash", stream_id)); stream->stream_id = 0; /* cleared */ } return 0; @@ -1000,7 +1006,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, stream = data_s->req.p.http; if(!stream) { - failf(data_s, "Internal NULL stream!"); + failf(data_s, "Internal NULL stream"); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1009,7 +1015,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { char *h; - if(!strcmp(":authority", (const char *)name)) { + if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) { /* pseudo headers are lower case */ int rc = 0; char *check = aprintf("%s:%d", conn->host.name, conn->remote_port); @@ -1072,22 +1078,27 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, return 0; } - if(namelen == sizeof(":status") - 1 && - memcmp(":status", name, namelen) == 0) { + if(namelen == sizeof(H2H3_PSEUDO_STATUS) - 1 && + memcmp(H2H3_PSEUDO_STATUS, name, namelen) == 0) { /* nghttp2 guarantees :status is received first and only once, and value is 3 digits status code, and decode_status_code always succeeds. */ + char buffer[32]; stream->status_code = decode_status_code(value, valuelen); DEBUGASSERT(stream->status_code != -1); - - result = Curl_dyn_add(&stream->header_recvbuf, "HTTP/2 "); + msnprintf(buffer, sizeof(buffer), H2H3_PSEUDO_STATUS ":%u\r", + stream->status_code); + result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); + if(result) + return NGHTTP2_ERR_CALLBACK_FAILURE; + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 ")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* the space character after the status code is mandatory */ - result = Curl_dyn_add(&stream->header_recvbuf, " \r\n"); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(" \r\n")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ @@ -1105,13 +1116,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_add(&stream->header_recvbuf, ": "); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": ")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_add(&stream->header_recvbuf, "\r\n"); + result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n")); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ @@ -1227,17 +1238,18 @@ void Curl_http2_done(struct Curl_easy *data, bool premature) !httpc->h2) /* not HTTP/2 ? */ return; - if(premature) { + /* do this before the reset handling, as that might clear ->stream_id */ + if(http->stream_id == httpc->pause_stream_id) { + H2BUGF(infof(data, "DONE the pause stream (%x)", http->stream_id)); + httpc->pause_stream_id = 0; + } + if(premature || (!http->closed && http->stream_id)) { /* RST_STREAM */ set_transfer(httpc, data); /* set the transfer */ + H2BUGF(infof(data, "RST stream %x", http->stream_id)); if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id, NGHTTP2_STREAM_CLOSED)) (void)nghttp2_session_send(httpc->h2); - - if(http->stream_id == httpc->pause_stream_id) { - infof(data, "stopped the pause stream!"); - httpc->pause_stream_id = 0; - } } if(data->state.drain) @@ -1248,7 +1260,7 @@ void Curl_http2_done(struct Curl_easy *data, bool premature) int rv = nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0); if(rv) { - infof(data, "http/2: failed to clear user_data for stream %d!", + infof(data, "http/2: failed to clear user_data for stream %d", http->stream_id); DEBUGASSERT(0); } @@ -1273,7 +1285,7 @@ static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn) rc = nghttp2_session_callbacks_new(&callbacks); if(rc) { - failf(data, "Couldn't initialize nghttp2 callbacks!"); + failf(data, "Couldn't initialize nghttp2 callbacks"); return CURLE_OUT_OF_MEMORY; /* most likely at least */ } @@ -1302,7 +1314,7 @@ static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn) nghttp2_session_callbacks_del(callbacks); if(rc) { - failf(data, "Couldn't initialize nghttp2!"); + failf(data, "Couldn't initialize nghttp2"); return CURLE_OUT_OF_MEMORY; /* most likely at least */ } } @@ -1337,7 +1349,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, } conn->proto.httpc.binlen = binlen; - result = Curl_base64url_encode(data, (const char *)binsettings, binlen, + result = Curl_base64url_encode((const char *)binsettings, binlen, &base64, &blen); if(result) { Curl_dyn_free(req); @@ -1507,7 +1519,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ stream->closed = FALSE; if(stream->error == NGHTTP2_REFUSED_STREAM) { - H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!", + H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection", stream->stream_id)); connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */ data->state.refused_stream = TRUE; @@ -1666,7 +1678,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex, )); if((data->state.drain) && stream->memlen) { - H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)", + H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)", stream->memlen, stream->stream_id, stream->mem, mem)); if(mem != stream->mem) { @@ -1816,80 +1828,6 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex, return -1; } -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -/* USHRT_MAX is 65535 == 0xffff */ -#define HEADER_OVERFLOW(x) \ - (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen) - -/* - * Check header memory for the token "trailers". - * Parse the tokens as separated by comma and surrounded by whitespace. - * Returns TRUE if found or FALSE if not. - */ -static bool contains_trailers(const char *p, size_t len) -{ - const char *end = p + len; - for(;;) { - for(; p != end && (*p == ' ' || *p == '\t'); ++p) - ; - if(p == end || (size_t)(end - p) < sizeof("trailers") - 1) - return FALSE; - if(strncasecompare("trailers", p, sizeof("trailers") - 1)) { - p += sizeof("trailers") - 1; - for(; p != end && (*p == ' ' || *p == '\t'); ++p) - ; - if(p == end || *p == ',') - return TRUE; - } - /* skip to next token */ - for(; p != end && *p != ','; ++p) - ; - if(p == end) - return FALSE; - ++p; - } -} - -typedef enum { - /* Send header to server */ - HEADERINST_FORWARD, - /* Don't send header to server */ - HEADERINST_IGNORE, - /* Discard header, and replace it with "te: trailers" */ - HEADERINST_TE_TRAILERS -} header_instruction; - -/* Decides how to treat given header field. */ -static header_instruction inspect_header(const char *name, size_t namelen, - const char *value, size_t valuelen) { - switch(namelen) { - case 2: - if(!strncasecompare("te", name, namelen)) - return HEADERINST_FORWARD; - - return contains_trailers(value, valuelen) ? - HEADERINST_TE_TRAILERS : HEADERINST_IGNORE; - case 7: - return strncasecompare("upgrade", name, namelen) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - case 10: - return (strncasecompare("connection", name, namelen) || - strncasecompare("keep-alive", name, namelen)) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - case 16: - return strncasecompare("proxy-connection", name, namelen) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - case 17: - return strncasecompare("transfer-encoding", name, namelen) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - default: - return HEADERINST_FORWARD; - } -} - static ssize_t http2_send(struct Curl_easy *data, int sockindex, const void *mem, size_t len, CURLcode *err) { @@ -1904,14 +1842,12 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, struct HTTP *stream = data->req.p.http; nghttp2_nv *nva = NULL; size_t nheader; - size_t i; - size_t authority_idx; - char *hdbuf = (char *)mem; - char *end, *line_end; nghttp2_data_provider data_prd; int32_t stream_id; nghttp2_session *h2 = httpc->h2; nghttp2_priority_spec pri_spec; + CURLcode result; + struct h2h3req *hreq; (void)sockindex; @@ -1977,174 +1913,29 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, return len; } - /* Calculate number of headers contained in [mem, mem + len) */ - /* Here, we assume the curl http code generate *correct* HTTP header - field block */ - nheader = 0; - for(i = 1; i < len; ++i) { - if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { - ++nheader; - ++i; - } + result = Curl_pseudo_headers(data, mem, len, &hreq); + if(result) { + *err = result; + return -1; } - if(nheader < 2) - goto fail; + nheader = hreq->entries; - /* We counted additional 2 \r\n in the first and last line. We need 3 - new headers: :method, :path and :scheme. Therefore we need one - more space. */ - nheader += 1; nva = malloc(sizeof(nghttp2_nv) * nheader); if(!nva) { + Curl_pseudo_free(hreq); *err = CURLE_OUT_OF_MEMORY; return -1; } - - /* Extract :method, :path from request line - We do line endings with CRLF so checking for CR is enough */ - line_end = memchr(hdbuf, '\r', len); - if(!line_end) - goto fail; - - /* Method does not contain spaces */ - end = memchr(hdbuf, ' ', line_end - hdbuf); - if(!end || end == hdbuf) - goto fail; - nva[0].name = (unsigned char *)":method"; - nva[0].namelen = strlen((char *)nva[0].name); - nva[0].value = (unsigned char *)hdbuf; - nva[0].valuelen = (size_t)(end - hdbuf); - nva[0].flags = NGHTTP2_NV_FLAG_NONE; - if(HEADER_OVERFLOW(nva[0])) { - failf(data, "Failed sending HTTP request: Header overflow"); - goto fail; - } - - hdbuf = end + 1; - - /* Path may contain spaces so scan backwards */ - end = NULL; - for(i = (size_t)(line_end - hdbuf); i; --i) { - if(hdbuf[i - 1] == ' ') { - end = &hdbuf[i - 1]; - break; - } - } - if(!end || end == hdbuf) - goto fail; - nva[1].name = (unsigned char *)":path"; - nva[1].namelen = strlen((char *)nva[1].name); - nva[1].value = (unsigned char *)hdbuf; - nva[1].valuelen = (size_t)(end - hdbuf); - nva[1].flags = NGHTTP2_NV_FLAG_NONE; - if(HEADER_OVERFLOW(nva[1])) { - failf(data, "Failed sending HTTP request: Header overflow"); - goto fail; - } - - nva[2].name = (unsigned char *)":scheme"; - nva[2].namelen = strlen((char *)nva[2].name); - if(conn->handler->flags & PROTOPT_SSL) - nva[2].value = (unsigned char *)"https"; - else - nva[2].value = (unsigned char *)"http"; - nva[2].valuelen = strlen((char *)nva[2].value); - nva[2].flags = NGHTTP2_NV_FLAG_NONE; - if(HEADER_OVERFLOW(nva[2])) { - failf(data, "Failed sending HTTP request: Header overflow"); - goto fail; - } - - authority_idx = 0; - i = 3; - while(i < nheader) { - size_t hlen; - - hdbuf = line_end + 2; - - /* check for next CR, but only within the piece of data left in the given - buffer */ - line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); - if(!line_end || (line_end == hdbuf)) - goto fail; - - /* header continuation lines are not supported */ - if(*hdbuf == ' ' || *hdbuf == '\t') - goto fail; - - for(end = hdbuf; end < line_end && *end != ':'; ++end) - ; - if(end == hdbuf || end == line_end) - goto fail; - hlen = end - hdbuf; - - if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { - authority_idx = i; - nva[i].name = (unsigned char *)":authority"; - nva[i].namelen = strlen((char *)nva[i].name); - } - else { - nva[i].namelen = (size_t)(end - hdbuf); - /* Lower case the header name for HTTP/2 */ - Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); - nva[i].name = (unsigned char *)hdbuf; - } - hdbuf = end + 1; - while(*hdbuf == ' ' || *hdbuf == '\t') - ++hdbuf; - end = line_end; - - switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, - end - hdbuf)) { - case HEADERINST_IGNORE: - /* skip header fields prohibited by HTTP/2 specification. */ - --nheader; - continue; - case HEADERINST_TE_TRAILERS: - nva[i].value = (uint8_t*)"trailers"; - nva[i].valuelen = sizeof("trailers") - 1; - break; - default: - nva[i].value = (unsigned char *)hdbuf; - nva[i].valuelen = (size_t)(end - hdbuf); - } - - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - if(HEADER_OVERFLOW(nva[i])) { - failf(data, "Failed sending HTTP request: Header overflow"); - goto fail; - } - ++i; - } - - /* :authority must come before non-pseudo header fields */ - if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { - nghttp2_nv authority = nva[authority_idx]; - for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { - nva[i] = nva[i - 1]; - } - nva[i] = authority; - } - - /* Warn stream may be rejected if cumulative length of headers is too large. - It appears nghttp2 will not send a header frame larger than 64KB. */ -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; - - for(i = 0; i < nheader; ++i) { - acc += nva[i].namelen + nva[i].valuelen; - - H2BUGF(infof(data, "h2 header: %.*s:%.*s", - nva[i].namelen, nva[i].name, - nva[i].valuelen, nva[i].value)); - } - - if(acc > MAX_ACC) { - infof(data, "http2_send: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].namelen = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].valuelen = hreq->header[i].valuelen; + nva[i].flags = NGHTTP2_NV_FLAG_NONE; } + Curl_pseudo_free(hreq); } h2_pri_spec(data, &pri_spec); @@ -2213,11 +2004,6 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, nghttp2_session_resume_data(h2, stream->stream_id); return len; - -fail: - free(nva); - *err = CURLE_SEND_ERROR; - return -1; } CURLcode Curl_http2_setup(struct Curl_easy *data, @@ -2271,8 +2057,6 @@ CURLcode Curl_http2_setup(struct Curl_easy *data, httpc->pause_stream_id = 0; httpc->drain_total = 0; - infof(data, "Connection state changed (HTTP/2 confirmed)"); - return CURLE_OK; } @@ -2310,7 +2094,7 @@ CURLcode Curl_http2_switched(struct Curl_easy *data, stream->stream_id, data); if(rv) { - infof(data, "http/2: failed to set user_data for stream %d!", + infof(data, "http/2: failed to set user_data for stream %d", stream->stream_id); DEBUGASSERT(0); } diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 751e5af..210c3db 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -87,7 +87,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) struct tm tm; char timestamp[17]; char date[9]; - const char *content_type = Curl_checkheaders(data, "Content-Type"); + const char *content_type = Curl_checkheaders(data, STRCONST("Content-Type")); char *canonical_headers = NULL; char *signed_headers = NULL; Curl_HttpReq httpreq; @@ -110,7 +110,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) DEBUGASSERT(!proxy); (void)proxy; - if(Curl_checkheaders(data, "Authorization")) { + if(Curl_checkheaders(data, STRCONST("Authorization"))) { /* Authorization already present, Bailing out */ return CURLE_OK; } diff --git a/lib/http_chunks.c b/lib/http_chunks.c index beb9695..6bafcd9 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,7 +29,6 @@ #include "dynbuf.h" #include "content_encoding.h" #include "http.h" -#include "non-ascii.h" /* for Curl_convert_to_network prototype */ #include "strtoofft.h" #include "warnless.h" @@ -74,18 +73,7 @@ */ -#ifdef CURL_DOES_CONVERSIONS -/* Check for an ASCII hex digit. - We avoid the use of ISXDIGIT to accommodate non-ASCII hosts. */ -static bool isxdigit_ascii(char digit) -{ - return (digit >= 0x30 && digit <= 0x39) /* 0-9 */ - || (digit >= 0x41 && digit <= 0x46) /* A-F */ - || (digit >= 0x61 && digit <= 0x66); /* a-f */ -} -#else #define isxdigit_ascii(x) Curl_isxdigit(x) -#endif void Curl_httpchunk_init(struct Curl_easy *data) { @@ -157,14 +145,6 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, /* length and datap are unmodified */ ch->hexbuffer[ch->hexindex] = 0; - /* convert to host encoding before calling strtoul */ - result = Curl_convert_from_network(data, ch->hexbuffer, ch->hexindex); - if(result) { - /* Curl_convert_from_network calls failf if unsuccessful */ - /* Treat it as a bad hex character */ - return CHUNKE_ILLEGAL_HEX; - } - if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) return CHUNKE_ILLEGAL_HEX; ch->state = CHUNK_LF; /* now wait for the CRLF */ @@ -234,21 +214,16 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, if(tr) { size_t trlen; - result = Curl_dyn_add(&conn->trailer, (char *)"\x0d\x0a"); + result = Curl_dyn_addn(&conn->trailer, (char *)STRCONST("\x0d\x0a")); if(result) return CHUNKE_OUT_OF_MEMORY; tr = Curl_dyn_ptr(&conn->trailer); trlen = Curl_dyn_len(&conn->trailer); - /* Convert to host encoding before calling Curl_client_write */ - result = Curl_convert_from_network(data, tr, trlen); - if(result) - /* Curl_convert_from_network calls failf if unsuccessful */ - /* Treat it as a bad chunk */ - return CHUNKE_BAD_CHUNK; - if(!data->set.http_te_skip) { - result = Curl_client_write(data, CLIENTWRITE_HEADER, tr, trlen); + result = Curl_client_write(data, + CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, + tr, trlen); if(result) { *extrap = result; return CHUNKE_PASSTHRU_ERROR; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 5f764dc..888d3b2 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -161,7 +161,7 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, return result; } - result = Curl_auth_create_spnego_message(data, neg_ctx, &base64, &len); + result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len); if(result) return result; diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index a6526db..bb7e536 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -213,8 +213,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); - result = Curl_base64_encode(data, - (const char *) Curl_bufref_ptr(&ntlmmsg), + result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); if(!result) { free(*allocuserpwd); @@ -233,8 +232,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, ntlm, &ntlmmsg); if(!result && Curl_bufref_len(&ntlmmsg)) { - result = Curl_base64_encode(data, - (const char *) Curl_bufref_ptr(&ntlmmsg), + result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); if(!result) { free(*allocuserpwd); diff --git a/lib/http_proxy.c b/lib/http_proxy.c index e13f485..863cbbb 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,7 +35,6 @@ #include "url.h" #include "select.h" #include "progress.h" -#include "non-ascii.h" #include "connect.h" #include "curlx.h" #include "vtls/vtls.h" @@ -173,7 +172,7 @@ static CURLcode connect_init(struct Curl_easy *data, bool reinit) s = calloc(1, sizeof(struct http_connect_state)); if(!s) return CURLE_OUT_OF_MEMORY; - infof(data, "allocate connect buffer!"); + infof(data, "allocate connect buffer"); conn->connect_state = s; Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); @@ -221,7 +220,7 @@ void Curl_connect_done(struct Curl_easy *data) #ifdef USE_HYPER data->state.hconnect = FALSE; #endif - infof(data, "CONNECT phase completed!"); + infof(data, "CONNECT phase completed"); } } @@ -245,7 +244,7 @@ static CURLcode CONNECT_host(struct Curl_easy *data, if(!hostheader) return CURLE_OUT_OF_MEMORY; - if(!Curl_checkProxyheaders(data, conn, "Host")) { + if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) { host = aprintf("Host: %s\r\n", hostheader); if(!host) { free(hostheader); @@ -324,25 +323,29 @@ static CURLcode CONNECT(struct Curl_easy *data, data->state.aptr.proxyuserpwd? data->state.aptr.proxyuserpwd:""); - if(!result && !Curl_checkProxyheaders(data, conn, "User-Agent") && + if(!result && !Curl_checkProxyheaders(data, + conn, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) result = Curl_dyn_addf(req, "User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); - if(!result && !Curl_checkProxyheaders(data, conn, "Proxy-Connection")) - result = Curl_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n"); + if(!result && !Curl_checkProxyheaders(data, conn, + STRCONST("Proxy-Connection"))) + result = Curl_dyn_addn(req, + STRCONST("Proxy-Connection: Keep-Alive\r\n")); if(!result) result = Curl_add_custom_headers(data, TRUE, req); if(!result) /* CRLF terminate the request */ - result = Curl_dyn_add(req, "\r\n"); + result = Curl_dyn_addn(req, STRCONST("\r\n")); if(!result) { /* Send the connect request to the proxy */ result = Curl_buffer_send(req, data, &data->info.request_size, 0, sockindex); + s->headerlines = 0; } if(result) failf(data, "Failed sending CONNECT to proxy"); @@ -470,7 +473,7 @@ static CURLcode CONNECT(struct Curl_easy *data, } if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) { - failf(data, "CONNECT response too large!"); + failf(data, "CONNECT response too large"); return CURLE_RECV_ERROR; } @@ -478,23 +481,18 @@ static CURLcode CONNECT(struct Curl_easy *data, if(byte != 0x0a) continue; + s->headerlines++; linep = Curl_dyn_ptr(&s->rcvbuf); perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */ - /* convert from the network encoding */ - result = Curl_convert_from_network(data, linep, perline); - /* Curl_convert_from_network calls failf if unsuccessful */ - if(result) - return result; - /* output debug if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); if(!data->set.suppress_connect_headers) { /* send the header to the callback */ - int writetype = CLIENTWRITE_HEADER; - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; + int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | + (data->set.include_header ? CLIENTWRITE_BODY : 0) | + (s->headerlines == 1 ? CLIENTWRITE_STATUS : 0); result = Curl_client_write(data, writetype, linep, perline); if(result) @@ -596,7 +594,8 @@ static CURLcode CONNECT(struct Curl_easy *data, strlen("Content-Length:"), NULL, 10, &s->cl); } } - else if(Curl_compareheader(linep, "Connection:", "close")) + else if(Curl_compareheader(linep, + STRCONST("Connection:"), STRCONST("close"))) s->close_connection = TRUE; else if(checkprefix("Transfer-Encoding:", linep)) { if(k->httpcode/100 == 2) { @@ -607,14 +606,17 @@ static CURLcode CONNECT(struct Curl_easy *data, "CONNECT %03d response", k->httpcode); } else if(Curl_compareheader(linep, - "Transfer-Encoding:", "chunked")) { + STRCONST("Transfer-Encoding:"), + STRCONST("chunked"))) { infof(data, "CONNECT responded chunked"); s->chunked_encoding = TRUE; /* init our chunky engine */ Curl_httpchunk_init(data); } } - else if(Curl_compareheader(linep, "Proxy-Connection:", "close")) + else if(Curl_compareheader(linep, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) s->close_connection = TRUE; else if(2 == sscanf(linep, "HTTP/1.%d %d", &subversion, @@ -765,6 +767,9 @@ static CURLcode CONNECT(struct Curl_easy *data, } options = hyper_clientconn_options_new(); + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); + if(!options) { failf(data, "Couldn't create hyper client options"); result = CURLE_OUT_OF_MEMORY; @@ -877,7 +882,7 @@ static CURLcode CONNECT(struct Curl_easy *data, goto error; } - if(!Curl_checkProxyheaders(data, conn, "User-Agent") && + if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { struct dynbuf ua; Curl_dyn_init(&ua, DYN_HTTP_REQUEST); @@ -891,7 +896,7 @@ static CURLcode CONNECT(struct Curl_easy *data, Curl_dyn_free(&ua); } - if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection")) { + if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); if(result) diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 2820e11..67543b5 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -59,6 +59,7 @@ struct http_connect_state { struct dynbuf rcvbuf; struct dynbuf req; size_t nsend; + size_t headerlines; enum keeponval { KEEPON_DONE, KEEPON_CONNECT, diff --git a/lib/idn_win32.c b/lib/idn_win32.c index 1d475a4..0914e1f 100644 --- a/lib/idn_win32.c +++ b/lib/idn_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -76,11 +76,15 @@ bool curl_win32_idn_to_ascii(const char *in, char **out) if(in_w) { wchar_t punycode[IDN_MAX_LENGTH]; int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); - free(in_w); + curlx_unicodefree(in_w); if(chars) { - *out = curlx_convert_wchar_to_UTF8(punycode); - if(*out) - success = TRUE; + char *mstr = curlx_convert_wchar_to_UTF8(punycode); + if(mstr) { + *out = strdup(mstr); + curlx_unicodefree(mstr); + if(*out) + success = TRUE; + } } } @@ -97,11 +101,15 @@ bool curl_win32_ascii_to_idn(const char *in, char **out) wchar_t unicode[IDN_MAX_LENGTH]; int chars = IdnToUnicode(0, in_w, curlx_uztosi(in_len), unicode, IDN_MAX_LENGTH); - free(in_w); + curlx_unicodefree(in_w); if(chars) { - *out = curlx_convert_wchar_to_UTF8(unicode); - if(*out) - success = TRUE; + char *mstr = curlx_convert_wchar_to_UTF8(unicode); + if(mstr) { + *out = strdup(mstr); + curlx_unicodefree(mstr); + if(*out) + success = TRUE; + } } } diff --git a/lib/if2ip.c b/lib/if2ip.c index 132b3ee..1d34531 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,12 +60,10 @@ /* ------------------------------------------------------------------ */ +#ifdef ENABLE_IPV6 /* Return the scope of the given address. */ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) { -#ifndef ENABLE_IPV6 - (void) sa; -#else if(sa->sa_family == AF_INET6) { const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa; const unsigned char *b = sa6->sin6_addr.s6_addr; @@ -88,27 +86,25 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) break; } } -#endif - return IPV6_SCOPE_GLOBAL; } - +#endif #if defined(HAVE_GETIFADDRS) -if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - unsigned int local_scope_id, const char *interf, +if2ip_result_t Curl_if2ip(int af, +#ifdef ENABLE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, char *buf, int buf_size) { struct ifaddrs *iface, *head; if2ip_result_t res = IF2IP_NOT_FOUND; -#ifndef ENABLE_IPV6 - (void) remote_scope; -#endif - -#if !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) || \ - !defined(ENABLE_IPV6) +#if defined(ENABLE_IPV6) && \ + !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) (void) local_scope_id; #endif @@ -181,8 +177,12 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, #elif defined(HAVE_IOCTL_SIOCGIFADDR) -if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - unsigned int local_scope_id, const char *interf, +if2ip_result_t Curl_if2ip(int af, +#ifdef ENABLE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, char *buf, int buf_size) { struct ifreq req; @@ -192,8 +192,10 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, size_t len; const char *r; +#ifdef ENABLE_IPV6 (void)remote_scope; (void)local_scope_id; +#endif if(!interf || (af != AF_INET)) return IF2IP_NOT_FOUND; @@ -230,13 +232,19 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, #else -if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - unsigned int local_scope_id, const char *interf, +if2ip_result_t Curl_if2ip(int af, +#ifdef ENABLE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, char *buf, int buf_size) { (void) af; +#ifdef ENABLE_IPV6 (void) remote_scope; (void) local_scope_id; +#endif (void) interf; (void) buf; (void) buf_size; diff --git a/lib/if2ip.h b/lib/if2ip.h index e074e47..a360d4a 100644 --- a/lib/if2ip.h +++ b/lib/if2ip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, 2022, Daniel Stenberg, , 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,7 +30,11 @@ #define IPV6_SCOPE_UNIQUELOCAL 3 /* Unique local */ #define IPV6_SCOPE_NODELOCAL 4 /* Loopback. */ +#ifdef ENABLE_IPV6 unsigned int Curl_ipv6_scope(const struct sockaddr *sa); +#else +#define Curl_ipv6_scope(x) 0 +#endif typedef enum { IF2IP_NOT_FOUND = 0, /* Interface not found */ @@ -38,8 +42,12 @@ typedef enum { IF2IP_FOUND = 2 /* The address has been stored in "buf" */ } if2ip_result_t; -if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - unsigned int local_scope_id, const char *interf, +if2ip_result_t Curl_if2ip(int af, +#ifdef ENABLE_IPV6 + unsigned int remote_scope, + unsigned int local_scope_id, +#endif + const char *interf, char *buf, int buf_size); #ifdef __INTERIX diff --git a/lib/imap.c b/lib/imap.c index 958ad14..817513b 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -507,7 +507,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!data->state.aptr.user) { state(data, IMAP_STOP); return result; @@ -608,7 +608,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, /* Check if already authenticated OR if there is enough data to authenticate with and end the connect phase if we don't */ if(imapc->preauth || - !Curl_sasl_can_authenticate(&imapc->sasl, conn)) { + !Curl_sasl_can_authenticate(&imapc->sasl, data)) { state(data, IMAP_STOP); return result; } @@ -624,7 +624,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, result = imap_perform_login(data, conn); else { /* Other mechanisms not supported */ - infof(data, "No known authentication mechanisms supported!"); + infof(data, "No known authentication mechanisms supported"); result = CURLE_LOGIN_DENIED; } } @@ -777,7 +777,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) NULL, MIMESTRATEGY_MAIL); if(!result) - if(!Curl_checkheaders(data, "Mime-Version")) + if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) result = Curl_mime_add_header(&data->set.mimepost.curlheaders, "Mime-Version: 1.0"); @@ -874,7 +874,7 @@ static CURLcode imap_state_servergreet_resp(struct Curl_easy *data, /* PREAUTH */ struct imap_conn *imapc = &conn->proto.imapc; imapc->preauth = TRUE; - infof(data, "PREAUTH connection, already authenticated!"); + infof(data, "PREAUTH connection, already authenticated"); } else if(imapcode != IMAP_RESP_OK) { failf(data, "Got unexpected imap-server response"); @@ -1986,7 +1986,7 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data) if(end > begin && end[-1] == '/') end--; - result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL, + result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL, REJECT_CTRL); if(result) return result; @@ -2009,7 +2009,7 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data) return CURLE_URL_MALFORMAT; /* Decode the name parameter */ - result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, + result = Curl_urldecode(begin, ptr - begin, &name, NULL, REJECT_CTRL); if(result) return result; @@ -2020,7 +2020,7 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data) ptr++; /* Decode the value parameter */ - result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, + result = Curl_urldecode(begin, ptr - begin, &value, &valuelen, REJECT_CTRL); if(result) { free(name); @@ -2108,7 +2108,7 @@ static CURLcode imap_parse_custom_request(struct Curl_easy *data) if(custom) { /* URL decode the custom request */ - result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, REJECT_CTRL); + result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL); /* Extract the parameters if specified */ if(!result) { diff --git a/lib/krb5.c b/lib/krb5.c index 54edb47..dee94c9 100644 --- a/lib/krb5.c +++ b/lib/krb5.c @@ -2,7 +2,7 @@ * * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (c) 2004 - 2021 Daniel Stenberg + * Copyright (c) 2004 - 2022 Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,6 @@ #include "sendf.h" #include "curl_krb5.h" #include "warnless.h" -#include "non-ascii.h" #include "strcase.h" #include "strdup.h" @@ -81,11 +80,6 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, write_len += 2; bytes_written = 0; - result = Curl_convert_to_network(data, s, write_len); - /* Curl_convert_to_network calls failf if unsuccessful */ - if(result) - return result; - for(;;) { #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; @@ -298,7 +292,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) if(output_buffer.length) { char *cmd; - result = Curl_base64_encode(data, (char *)output_buffer.value, + result = Curl_base64_encode((char *)output_buffer.value, output_buffer.length, &p, &base64_sz); if(result) { infof(data, "base64-encoding: %s", curl_easy_strerror(result)); @@ -612,7 +606,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, return; /* error */ if(iscmd) { - error = Curl_base64_encode(data, buffer, curlx_sitouz(bytes), + error = Curl_base64_encode(buffer, curlx_sitouz(bytes), &cmd_buffer, &cmd_size); if(error) { free(buffer); diff --git a/lib/ldap.c b/lib/ldap.c index 3154db5..03ea14e 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -306,8 +306,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) rc = _ldap_url_parse(data, conn, &ludp); #endif if(rc) { - failf(data, "LDAP local: %s", ldap_err2string(rc)); - result = CURLE_LDAP_INVALID_URL; + failf(data, "Bad LDAP URL: %s", ldap_err2string(rc)); + result = CURLE_URL_MALFORMAT; goto quit; } @@ -328,7 +328,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) host = conn->host.name; #endif - if(conn->bits.user_passwd) { + if(data->state.aptr.user) { user = conn->user; passwd = conn->passwd; } @@ -361,7 +361,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) (strcasecompare(data->set.ssl.cert_type, "DER"))) cert_type = LDAPSSL_CERT_FILETYPE_DER; if(!ldap_ca) { - failf(data, "LDAP local: ERROR %s CA cert not set!", + failf(data, "LDAP local: ERROR %s CA cert not set", (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM")); result = CURLE_SSL_CERTPROBLEM; goto quit; @@ -400,12 +400,12 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) /* OpenLDAP SDK supports BASE64 files. */ if((data->set.ssl.cert_type) && (!strcasecompare(data->set.ssl.cert_type, "PEM"))) { - failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type!"); + failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); result = CURLE_SSL_CERTPROBLEM; goto quit; } if(!ldap_ca) { - failf(data, "LDAP local: ERROR PEM CA cert not set!"); + failf(data, "LDAP local: ERROR PEM CA cert not set"); result = CURLE_SSL_CERTPROBLEM; goto quit; } @@ -636,11 +636,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if((attr_len > 7) && (strcmp(";binary", (char *) attr + (attr_len - 7)) == 0)) { /* Binary attribute, encode to base64. */ - result = Curl_base64_encode(data, - vals[i]->bv_val, - vals[i]->bv_len, - &val_b64, - &val_b64_sz); + result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len, + &val_b64, &val_b64_sz); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); @@ -870,7 +867,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, LDAP_TRACE(("DN '%s'\n", dn)); /* Unescape the DN */ - result = Curl_urldecode(data, dn, 0, &unescaped, NULL, REJECT_ZERO); + result = Curl_urldecode(dn, 0, &unescaped, NULL, REJECT_ZERO); if(result) { rc = LDAP_NO_MEMORY; @@ -935,7 +932,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i])); /* Unescape the attribute */ - result = Curl_urldecode(data, attributes[i], 0, &unescaped, NULL, + result = Curl_urldecode(attributes[i], 0, &unescaped, NULL, REJECT_ZERO); if(result) { free(attributes); @@ -1005,7 +1002,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, LDAP_TRACE(("filter '%s'\n", filter)); /* Unescape the filter */ - result = Curl_urldecode(data, filter, 0, &unescaped, NULL, REJECT_ZERO); + result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_ZERO); if(result) { rc = LDAP_NO_MEMORY; diff --git a/lib/md5.c b/lib/md5.c index c6923e0..d2ca240 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,22 +60,22 @@ /* The last #include file should be: */ #include "memdebug.h" -typedef struct md5_ctx MD5_CTX; +typedef struct md5_ctx my_md5_ctx; -static CURLcode MD5_Init(MD5_CTX *ctx) +static CURLcode my_md5_init(my_md5_ctx *ctx) { md5_init(ctx); return CURLE_OK; } -static void MD5_Update(MD5_CTX *ctx, - const unsigned char *input, - unsigned int inputLen) +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int inputLen) { md5_update(ctx, inputLen, input); } -static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) { md5_digest(ctx, 16, digest); } @@ -83,11 +83,38 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) #elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5) /* When OpenSSL or wolfSSL is available, we use their MD5 functions. */ +#if defined(USE_OPENSSL_MD5) #include +#elif defined(USE_WOLFSSL_MD5) +#include +#endif + #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" +typedef MD5_CTX my_md5_ctx; + +static CURLcode my_md5_init(my_md5_ctx *ctx) +{ + if(!MD5_Init(ctx)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int len) +{ + (void)MD5_Update(ctx, input, len); +} + +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +{ + (void)MD5_Final(digest, ctx); +} + #elif defined(USE_MBEDTLS) #include @@ -97,21 +124,25 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) /* The last #include file should be: */ #include "memdebug.h" -typedef mbedtls_md5_context MD5_CTX; +typedef mbedtls_md5_context my_md5_ctx; -static CURLcode MD5_Init(MD5_CTX *ctx) +static CURLcode my_md5_init(my_md5_ctx *ctx) { -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - (void) mbedtls_md5_starts(ctx); +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + if(mbedtls_md5_starts(ctx)) + return CURLE_OUT_OF_MEMORY; +#elif defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) + if(mbedtls_md5_starts_ret(ctx)) + return CURLE_OUT_OF_MEMORY; #else - (void) mbedtls_md5_starts_ret(ctx); + (void)mbedtls_md5_starts(ctx); #endif return CURLE_OK; } -static void MD5_Update(MD5_CTX *ctx, - const unsigned char *data, - unsigned int length) +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *data, + unsigned int length) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_md5_update(ctx, data, length); @@ -120,7 +151,7 @@ static void MD5_Update(MD5_CTX *ctx, #endif } -static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_md5_finish(ctx, digest); @@ -143,25 +174,27 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) Declaring the functions as static like this seems to be a bit more reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ # include -# define MD5_CTX CC_MD5_CTX +# define my_md5_ctx CC_MD5_CTX #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" -static CURLcode MD5_Init(MD5_CTX *ctx) +static CURLcode my_md5_init(my_md5_ctx *ctx) { - CC_MD5_Init(ctx); + if(!CC_MD5_Init(ctx)) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; } -static void MD5_Update(MD5_CTX *ctx, - const unsigned char *input, - unsigned int inputLen) +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int inputLen) { CC_MD5_Update(ctx, input, inputLen); } -static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) { CC_MD5_Final(digest, ctx); } @@ -177,25 +210,30 @@ struct md5_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; }; -typedef struct md5_ctx MD5_CTX; +typedef struct md5_ctx my_md5_ctx; -static CURLcode MD5_Init(MD5_CTX *ctx) +static CURLcode my_md5_init(my_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); + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return CURLE_OUT_OF_MEMORY; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + return CURLE_OUT_OF_MEMORY; } + return CURLE_OK; } -static void MD5_Update(MD5_CTX *ctx, - const unsigned char *input, - unsigned int inputLen) +static void my_md5_update(my_md5_ctx *ctx, + const unsigned char *input, + unsigned int inputLen) { CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); } -static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) +static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) { unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); @@ -263,11 +301,12 @@ struct md5_ctx { unsigned char buffer[64]; MD5_u32plus block[16]; }; -typedef struct md5_ctx MD5_CTX; +typedef struct md5_ctx my_md5_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); +static CURLcode my_md5_init(my_md5_ctx *ctx); +static void my_md5_update(my_md5_ctx *ctx, const void *data, + unsigned long size); +static void my_md5_final(unsigned char *result, my_md5_ctx *ctx); /* * The basic MD5 functions. @@ -318,7 +357,7 @@ static void MD5_Final(unsigned char *result, MD5_CTX *ctx); * This processes one or more 64-byte data blocks, but does NOT update * the bit counters. There are no alignment requirements. */ -static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +static const void *body(my_md5_ctx *ctx, const void *data, unsigned long size) { const unsigned char *ptr; MD5_u32plus a, b, c, d; @@ -426,7 +465,7 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) return ptr; } -static CURLcode MD5_Init(MD5_CTX *ctx) +static CURLcode my_md5_init(my_md5_ctx *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -439,7 +478,8 @@ static CURLcode MD5_Init(MD5_CTX *ctx) return CURLE_OK; } -static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +static void my_md5_update(my_md5_ctx *ctx, const void *data, + unsigned long size) { MD5_u32plus saved_lo; unsigned long used; @@ -474,7 +514,7 @@ static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) memcpy(ctx->buffer, data, size); } -static void MD5_Final(unsigned char *result, MD5_CTX *ctx) +static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) { unsigned long used, available; @@ -530,13 +570,13 @@ static void MD5_Final(unsigned char *result, MD5_CTX *ctx) const struct HMAC_params Curl_HMAC_MD5[] = { { /* Hash initialization function. */ - CURLX_FUNCTION_CAST(HMAC_hinit_func, MD5_Init), + CURLX_FUNCTION_CAST(HMAC_hinit_func, my_md5_init), /* Hash update function. */ - CURLX_FUNCTION_CAST(HMAC_hupdate_func, MD5_Update), + CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_md5_update), /* Hash computation end function. */ - CURLX_FUNCTION_CAST(HMAC_hfinal_func, MD5_Final), + CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_md5_final), /* Size of hash context structure. */ - sizeof(MD5_CTX), + sizeof(my_md5_ctx), /* Maximum key length. */ 64, /* Result size. */ @@ -547,13 +587,13 @@ const struct HMAC_params Curl_HMAC_MD5[] = { const struct MD5_params Curl_DIGEST_MD5[] = { { /* Digest initialization function */ - CURLX_FUNCTION_CAST(Curl_MD5_init_func, MD5_Init), + CURLX_FUNCTION_CAST(Curl_MD5_init_func, my_md5_init), /* Digest update function */ - CURLX_FUNCTION_CAST(Curl_MD5_update_func, MD5_Update), + CURLX_FUNCTION_CAST(Curl_MD5_update_func, my_md5_update), /* Digest computation end function */ - CURLX_FUNCTION_CAST(Curl_MD5_final_func, MD5_Final), + CURLX_FUNCTION_CAST(Curl_MD5_final_func, my_md5_final), /* Size of digest context struct */ - sizeof(MD5_CTX), + sizeof(my_md5_ctx), /* Result size */ 16 } @@ -564,15 +604,17 @@ const struct MD5_params Curl_DIGEST_MD5[] = { * Returns CURLE_OK on success. */ CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, - const size_t len) + const size_t len) { - MD5_CTX ctx; + CURLcode result; + my_md5_ctx ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, input, curlx_uztoui(len)); - MD5_Final(outbuffer, &ctx); - - return CURLE_OK; + result = my_md5_init(&ctx); + if(!result) { + my_md5_update(&ctx, input, curlx_uztoui(len)); + my_md5_final(outbuffer, &ctx); + } + return result; } struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) @@ -594,7 +636,11 @@ struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) ctxt->md5_hash = md5params; - (*md5params->md5_init_func)(ctxt->md5_hashctx); + if((*md5params->md5_init_func)(ctxt->md5_hashctx)) { + free(ctxt->md5_hashctx); + free(ctxt); + return NULL; + } return ctxt; } diff --git a/lib/mime.c b/lib/mime.c index 7783b89..d6985d3 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,7 +25,6 @@ #include #include "mime.h" -#include "non-ascii.h" #include "warnless.h" #include "urldata.h" #include "sendf.h" @@ -315,7 +314,7 @@ static char *escape_string(struct Curl_easy *data, Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH); - for(result = Curl_dyn_add(&db, ""); !result && *src; src++) { + for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) { for(p = table; *p && **p != *src; p++) ; @@ -340,9 +339,9 @@ static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len) } /* Get a header from an slist. */ -static char *search_header(struct curl_slist *hdrlist, const char *hdr) +static char *search_header(struct curl_slist *hdrlist, + const char *hdr, size_t len) { - size_t len = strlen(hdr); char *value = NULL; for(; !value && hdrlist; hdrlist = hdrlist->next) @@ -506,15 +505,6 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, } } -#ifdef CURL_DOES_CONVERSIONS - /* This is now textual data, Convert character codes. */ - if(part->easy && cursize) { - CURLcode result = Curl_convert_to_network(part->easy, buffer, cursize); - if(result) - return READ_ERROR; - } -#endif - return cursize; } @@ -768,7 +758,7 @@ static void mime_file_free(void *ptr) static size_t readback_bytes(struct mime_state *state, char *buffer, size_t bufsize, const char *bytes, size_t numbytes, - const char *trail) + const char *trail, size_t traillen) { size_t sz; size_t offset = curlx_sotouz(state->offset); @@ -778,13 +768,11 @@ static size_t readback_bytes(struct mime_state *state, bytes += offset; } else { - size_t tsz = strlen(trail); - sz = offset - numbytes; - if(sz >= tsz) + if(sz >= traillen) return 0; bytes = trail + sz; - sz = tsz - sz; + sz = traillen - sz; } if(sz > bufsize) @@ -925,9 +913,6 @@ static size_t readback_part(curl_mimepart *part, char *buffer, size_t bufsize, bool *hasread) { size_t cursize = 0; -#ifdef CURL_DOES_CONVERSIONS - char *convbuf = buffer; -#endif /* Readback from part. */ @@ -956,26 +941,18 @@ static size_t readback_part(curl_mimepart *part, mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders); else { sz = readback_bytes(&part->state, buffer, bufsize, - hdr->data, strlen(hdr->data), "\r\n"); + hdr->data, strlen(hdr->data), STRCONST("\r\n")); if(!sz) mimesetstate(&part->state, part->state.state, hdr->next); } break; case MIMESTATE_EOH: - sz = readback_bytes(&part->state, buffer, bufsize, "\r\n", 2, ""); + sz = readback_bytes(&part->state, buffer, bufsize, STRCONST("\r\n"), + STRCONST("")); if(!sz) mimesetstate(&part->state, MIMESTATE_BODY, NULL); break; case MIMESTATE_BODY: -#ifdef CURL_DOES_CONVERSIONS - if(part->easy && convbuf < buffer) { - CURLcode result = Curl_convert_to_network(part->easy, convbuf, - buffer - convbuf); - if(result) - return READ_ERROR; - convbuf = buffer; - } -#endif cleanup_encoder_state(&part->encstate); mimesetstate(&part->state, MIMESTATE_CONTENT, NULL); break; @@ -1012,16 +989,6 @@ static size_t readback_part(curl_mimepart *part, bufsize -= sz; } -#ifdef CURL_DOES_CONVERSIONS - if(part->easy && convbuf < buffer && - part->state.state < MIMESTATE_BODY) { - CURLcode result = Curl_convert_to_network(part->easy, convbuf, - buffer - convbuf); - if(result) - return READ_ERROR; - } -#endif - return cursize; } @@ -1031,10 +998,6 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, { curl_mime *mime = (curl_mime *) instream; size_t cursize = 0; -#ifdef CURL_DOES_CONVERSIONS - char *convbuf = buffer; -#endif - (void) size; /* Always 1. */ while(nitems) { @@ -1043,9 +1006,6 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, switch(mime->state.state) { case MIMESTATE_BEGIN: case MIMESTATE_BODY: -#ifdef CURL_DOES_CONVERSIONS - convbuf = buffer; -#endif mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart); /* The first boundary always follows the header termination empty line, so is always preceded by a CRLF. We can then spare 2 characters @@ -1053,23 +1013,19 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, mime->state.offset += 2; break; case MIMESTATE_BOUNDARY1: - sz = readback_bytes(&mime->state, buffer, nitems, "\r\n--", 4, ""); + sz = readback_bytes(&mime->state, buffer, nitems, STRCONST("\r\n--"), + STRCONST("")); if(!sz) mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part); break; case MIMESTATE_BOUNDARY2: - sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, - strlen(mime->boundary), part? "\r\n": "--\r\n"); + if(part) + sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, + MIME_BOUNDARY_LEN, STRCONST("\r\n")); + else + sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, + MIME_BOUNDARY_LEN, STRCONST("--\r\n")); if(!sz) { -#ifdef CURL_DOES_CONVERSIONS - if(mime->easy && convbuf < buffer) { - CURLcode result = Curl_convert_to_network(mime->easy, convbuf, - buffer - convbuf); - if(result) - return READ_ERROR; - convbuf = buffer; - } -#endif mimesetstate(&mime->state, MIMESTATE_CONTENT, part); } break; @@ -1086,9 +1042,6 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, case STOP_FILLING: return cursize? cursize: sz; case 0: -#ifdef CURL_DOES_CONVERSIONS - convbuf = buffer; -#endif mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart); break; } @@ -1105,16 +1058,6 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, nitems -= sz; } -#ifdef CURL_DOES_CONVERSIONS - if(mime->easy && convbuf < buffer && - mime->state.state <= MIMESTATE_CONTENT) { - CURLcode result = Curl_convert_to_network(mime->easy, convbuf, - buffer - convbuf); - if(result) - return READ_ERROR; - } -#endif - return cursize; } @@ -1341,8 +1284,9 @@ curl_mime *curl_mime_init(struct Curl_easy *easy) mime->firstpart = NULL; mime->lastpart = NULL; - memset(mime->boundary, '-', 24); - if(Curl_rand_hex(easy, (unsigned char *) &mime->boundary[24], + memset(mime->boundary, '-', MIME_BOUNDARY_DASHES); + if(Curl_rand_hex(easy, + (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES], MIME_RAND_BOUNDARY_CHARS + 1)) { /* failed to get random separator, bail out */ free(mime); @@ -1619,7 +1563,7 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, root = root->parent->parent; if(subparts == root) { if(part->easy) - failf(part->easy, "Can't add itself as a subpart!"); + failf(part->easy, "Can't add itself as a subpart"); return CURLE_BAD_FUNCTION_ARGUMENT; } } @@ -1675,10 +1619,9 @@ CURLcode Curl_mime_rewind(curl_mimepart *part) /* Compute header list size. */ static size_t slist_size(struct curl_slist *s, - size_t overhead, const char *skip) + size_t overhead, const char *skip, size_t skiplen) { size_t size = 0; - size_t skiplen = skip? strlen(skip): 0; for(; s; s = s->next) if(!skip || !match_header(s, skip, skiplen)) @@ -1696,7 +1639,7 @@ static curl_off_t multipart_size(curl_mime *mime) if(!mime) return 0; /* Not present -> empty. */ - boundarysize = 4 + strlen(mime->boundary) + 2; + boundarysize = 4 + MIME_BOUNDARY_LEN + 2; size = boundarysize; /* Final boundary - CRLF after headers. */ for(part = mime->firstpart; part; part = part->nextpart) { @@ -1727,8 +1670,8 @@ curl_off_t Curl_mime_size(curl_mimepart *part) if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) { /* Compute total part size. */ - size += slist_size(part->curlheaders, 2, NULL); - size += slist_size(part->userheaders, 2, "Content-Type"); + size += slist_size(part->curlheaders, 2, NULL, 0); + size += slist_size(part->userheaders, 2, STRCONST("Content-Type")); size += 2; /* CRLF after headers. */ } return size; @@ -1804,10 +1747,9 @@ const char *Curl_mime_contenttype(const char *filename) return NULL; } -static bool content_type_match(const char *contenttype, const char *target) +static bool content_type_match(const char *contenttype, + const char *target, size_t len) { - size_t len = strlen(target); - if(contenttype && strncasecompare(contenttype, target, len)) switch(contenttype[len]) { case '\0': @@ -1843,7 +1785,7 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, /* Check if content type is specified. */ customct = part->mimetype; if(!customct) - customct = search_header(part->userheaders, "Content-Type"); + customct = search_header(part->userheaders, STRCONST("Content-Type")); if(customct) contenttype = customct; @@ -1872,12 +1814,12 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, boundary = mime->boundary; } else if(contenttype && !customct && - content_type_match(contenttype, "text/plain")) + content_type_match(contenttype, STRCONST("text/plain"))) if(strategy == MIMESTRATEGY_MAIL || !part->filename) contenttype = NULL; /* Issue content-disposition header only if not already set by caller. */ - if(!search_header(part->userheaders, "Content-Disposition")) { + if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { if(!disposition) if(part->filename || part->name || (contenttype && !strncasecompare(contenttype, "multipart/", 10))) @@ -1924,7 +1866,8 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, } /* Content-Transfer-Encoding header. */ - if(!search_header(part->userheaders, "Content-Transfer-Encoding")) { + if(!search_header(part->userheaders, + STRCONST("Content-Transfer-Encoding"))) { if(part->encoder) cte = part->encoder->name; else if(contenttype && strategy == MIMESTRATEGY_MAIL && @@ -1948,7 +1891,7 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, curl_mimepart *subpart; disposition = NULL; - if(content_type_match(contenttype, "multipart/form-data")) + if(content_type_match(contenttype, STRCONST("multipart/form-data"))) disposition = "form-data"; for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy); diff --git a/lib/mime.h b/lib/mime.h index 56642ae..f2fc434 100644 --- a/lib/mime.h +++ b/lib/mime.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,6 +24,7 @@ #include "curl_setup.h" +#define MIME_BOUNDARY_DASHES 24 /* leading boundary dashes */ #define MIME_RAND_BOUNDARY_CHARS 16 /* Nb. of random boundary chars. */ #define MAX_ENCODED_LINE_LENGTH 76 /* Maximum encoded line length. */ #define ENCODING_BUFFER_SIZE 256 /* Encoding temp buffers size. */ @@ -91,8 +92,8 @@ struct mime_state { curl_off_t offset; /* State-dependent offset. */ }; -/* minimum buffer size for the boundary string */ -#define MIME_BOUNDARY_LEN (24 + MIME_RAND_BOUNDARY_CHARS + 1) +/* Boundary string length. */ +#define MIME_BOUNDARY_LEN (MIME_BOUNDARY_DASHES + MIME_RAND_BOUNDARY_CHARS) /* A mime multipart. */ struct curl_mime { @@ -100,7 +101,7 @@ struct curl_mime { curl_mimepart *parent; /* Parent part. */ curl_mimepart *firstpart; /* First part. */ curl_mimepart *lastpart; /* Last part. */ - char boundary[MIME_BOUNDARY_LEN]; /* The part boundary. */ + char boundary[MIME_BOUNDARY_LEN + 1]; /* The part boundary. */ struct mime_state state; /* Current readback state. */ }; diff --git a/lib/mprintf.c b/lib/mprintf.c index 0fd3afc..1381201 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1999 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1999 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -65,7 +65,6 @@ */ #if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \ - (defined(__WATCOMC__) && defined(__386__)) || \ (defined(__POCC__) && defined(_MSC_VER)) || \ (defined(_WIN32_WCE)) || \ (defined(__MINGW32__)) || \ @@ -830,6 +829,8 @@ static int dprintf_formatf( } else if(prec != -1) len = (size_t)prec; + else if(*str == '\0') + len = 0; else len = strlen(str); diff --git a/lib/mqtt.c b/lib/mqtt.c index fcd40b4..9bcbaa1 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2020 - 2022, Daniel Stenberg, , et al. * Copyright (C) 2019, Björn Stenberg, * * This software is licensed as described in the file COPYING, which @@ -60,6 +60,8 @@ */ static CURLcode mqtt_do(struct Curl_easy *data, bool *done); +static CURLcode mqtt_done(struct Curl_easy *data, + CURLcode status, bool premature); static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); static int mqtt_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *sock); @@ -74,7 +76,7 @@ const struct Curl_handler Curl_handler_mqtt = { "MQTT", /* scheme */ mqtt_setup_conn, /* setup_connection */ mqtt_do, /* do_it */ - ZERO_NULL, /* done */ + mqtt_done, /* done */ ZERO_NULL, /* do_more */ ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ @@ -344,7 +346,9 @@ end: static CURLcode mqtt_disconnect(struct Curl_easy *data) { CURLcode result = CURLE_OK; + struct MQTT *mq = data->req.p.mqtt; result = mqtt_send(data, (char *)"\xe0\x00", 2); + Curl_safefree(mq->sendleftovers); return result; } @@ -384,8 +388,7 @@ static CURLcode mqtt_get_topic(struct Curl_easy *data, { char *path = data->state.up.path; if(strlen(path) > 1) - return Curl_urldecode(data, path + 1, 0, topic, topiclen, - REJECT_NADA); + return Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA); failf(data, "No MQTT topic found. Forgot to URL encode it?"); return CURLE_URL_MALFORMAT; } @@ -692,6 +695,16 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done) return CURLE_OK; } +static CURLcode mqtt_done(struct Curl_easy *data, + CURLcode status, bool premature) +{ + struct MQTT *mq = data->req.p.mqtt; + (void)status; + (void)premature; + Curl_safefree(mq->sendleftovers); + return CURLE_OK; +} + static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -719,8 +732,14 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) case MQTT_FIRST: /* Read the initial byte only */ result = Curl_read(data, sockfd, (char *)&mq->firstbyte, 1, &nread); - if(!nread) + if(result) break; + else if(!nread) { + failf(data, "Connection disconnected"); + *done = TRUE; + result = CURLE_RECV_ERROR; + break; + } Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1); /* remember the first byte */ mq->npacket = 0; diff --git a/lib/multi.c b/lib/multi.c index f8dcc63..466425d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -687,16 +687,10 @@ static CURLcode multi_done(struct Curl_easy *data, #endif ) || conn->bits.close || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { - CURLcode res2; connclose(conn, "disconnecting"); Curl_conncache_remove_conn(data, conn, FALSE); CONNCACHE_UNLOCK(data); - res2 = Curl_disconnect(data, conn, premature); - - /* If we had an error already, make sure we return that one. But - if we got a new error, return that. */ - if(!result && res2) - result = res2; + Curl_disconnect(data, conn, premature); } else { char buffer[256]; @@ -709,14 +703,15 @@ static CURLcode multi_done(struct Curl_easy *data, conn->bits.conn_to_host ? conn->conn_to_host.dispname : conn->host.dispname; /* create string before returning the connection */ + long connection_id = conn->connection_id; msnprintf(buffer, sizeof(buffer), "Connection #%ld to host %s left intact", - conn->connection_id, host); + connection_id, host); /* the connection is no longer in use by this transfer */ CONNCACHE_UNLOCK(data); if(Curl_conncache_return_conn(data, conn)) { /* remember the most recently used connection */ - data->state.lastconnect_id = conn->connection_id; + data->state.lastconnect_id = connection_id; infof(data, "%s", buffer); } else @@ -724,7 +719,6 @@ static CURLcode multi_done(struct Curl_easy *data, } Curl_safefree(data->state.buffer); - Curl_free_request_state(data); return result; } @@ -1759,6 +1753,10 @@ CURLcode Curl_preconnect(struct Curl_easy *data) return CURLE_OK; } +static void set_in_callback(struct Curl_multi *multi, bool value) +{ + multi->in_callback = value; +} static CURLMcode multi_runsingle(struct Curl_multi *multi, struct curltime *nowp, @@ -1795,7 +1793,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, rc = CURLM_OK; if(multi_ischanged(multi, TRUE)) { - DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!")); + DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue")); process_pending_handles(multi); /* multiplexed */ } @@ -2169,8 +2167,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_posttransfer(data); drc = multi_done(data, result, FALSE); - /* When set to retry the connection, we must to go back to - * the CONNECT state */ + /* When set to retry the connection, we must go back to the CONNECT + * state */ if(newurl) { if(!drc || (drc == CURLE_SEND_ERROR)) { follow = FOLLOW_RETRY; @@ -2382,7 +2380,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLcode ret = Curl_retry_request(data, &newurl); if(!ret) { - infof(data, "Downgrades to HTTP/1.1!"); + infof(data, "Downgrades to HTTP/1.1"); streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1"); data->state.httpwant = CURL_HTTP_VERSION_1_1; /* clear the error message bit too as we ignore the one we got */ @@ -2872,8 +2870,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi, continue; if(multi->socket_cb) { + set_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; @@ -2914,8 +2914,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi, entry->readers--; if(!entry->users) { if(multi->socket_cb) { + set_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; @@ -2969,9 +2971,12 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) if(entry) { int rc = 0; - if(multi->socket_cb) + if(multi->socket_cb) { + set_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); + set_in_callback(multi, FALSE); + } /* now remove it from the socket hash */ sh_delentry(entry, &multi->sockhash, s); @@ -3343,7 +3348,9 @@ CURLMcode 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 */ + set_in_callback(multi, TRUE); rc = multi->timer_cb(multi, -1, multi->timer_userp); + set_in_callback(multi, FALSE); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; @@ -3362,7 +3369,9 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) multi->timer_lastcall = multi->timetree->key; + set_in_callback(multi, TRUE); rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); + set_in_callback(multi, FALSE); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; @@ -3561,9 +3570,6 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, { struct Curl_sh_entry *there = NULL; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - there = sh_getentry(&multi->sockhash, s); if(!there) diff --git a/lib/non-ascii.c b/lib/non-ascii.c deleted file mode 100644 index 3b77ae9..0000000 --- a/lib/non-ascii.c +++ /dev/null @@ -1,336 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef CURL_DOES_CONVERSIONS - -#include - -#include "non-ascii.h" -#include "formdata.h" -#include "sendf.h" -#include "urldata.h" -#include "multiif.h" -#include "strerror.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -#ifdef HAVE_ICONV -#include -/* set default codesets for iconv */ -#ifndef CURL_ICONV_CODESET_OF_NETWORK -#define CURL_ICONV_CODESET_OF_NETWORK "ISO8859-1" -#endif -#ifndef CURL_ICONV_CODESET_FOR_UTF8 -#define CURL_ICONV_CODESET_FOR_UTF8 "UTF-8" -#endif -#define ICONV_ERROR (size_t)-1 -#endif /* HAVE_ICONV */ - -/* - * Curl_convert_clone() returns a malloced copy of the source string (if - * returning CURLE_OK), with the data converted to network format. - */ -CURLcode Curl_convert_clone(struct Curl_easy *data, - const char *indata, - size_t insize, - char **outbuf) -{ - char *convbuf; - CURLcode result; - - convbuf = malloc(insize); - if(!convbuf) - return CURLE_OUT_OF_MEMORY; - - memcpy(convbuf, indata, insize); - result = Curl_convert_to_network(data, convbuf, insize); - if(result) { - free(convbuf); - return result; - } - - *outbuf = convbuf; /* return the converted buffer */ - - return CURLE_OK; -} - -/* - * Curl_convert_to_network() is an internal function for performing ASCII - * conversions on non-ASCII platforms. It converts the buffer _in place_. - */ -CURLcode Curl_convert_to_network(struct Curl_easy *data, - char *buffer, size_t length) -{ - if(data && data->set.convtonetwork) { - /* use translation callback */ - CURLcode result; - Curl_set_in_callback(data, true); - result = data->set.convtonetwork(buffer, length); - Curl_set_in_callback(data, false); - if(result) { - failf(data, - "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s", - (int)result, curl_easy_strerror(result)); - } - - return result; - } - else { -#ifdef HAVE_ICONV - /* do the translation ourselves */ - iconv_t tmpcd = (iconv_t) -1; - iconv_t *cd = &tmpcd; - char *input_ptr, *output_ptr; - size_t in_bytes, out_bytes, rc; - char ebuffer[STRERROR_LEN]; - - /* open an iconv conversion descriptor if necessary */ - if(data) - cd = &data->outbound_cd; - if(*cd == (iconv_t)-1) { - *cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, - CURL_ICONV_CODESET_OF_HOST); - if(*cd == (iconv_t)-1) { - failf(data, - "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", - CURL_ICONV_CODESET_OF_NETWORK, - CURL_ICONV_CODESET_OF_HOST, - errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer))); - return CURLE_CONV_FAILED; - } - } - /* call iconv */ - input_ptr = output_ptr = buffer; - in_bytes = out_bytes = length; - rc = iconv(*cd, &input_ptr, &in_bytes, - &output_ptr, &out_bytes); - if(!data) - iconv_close(tmpcd); - if((rc == ICONV_ERROR) || (in_bytes)) { - failf(data, - "The Curl_convert_to_network iconv call failed with errno %i: %s", - errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer))); - return CURLE_CONV_FAILED; - } -#else - failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback required"); - return CURLE_CONV_REQD; -#endif /* HAVE_ICONV */ - } - - return CURLE_OK; -} - -/* - * Curl_convert_from_network() is an internal function for performing ASCII - * conversions on non-ASCII platforms. It converts the buffer _in place_. - */ -CURLcode Curl_convert_from_network(struct Curl_easy *data, - char *buffer, size_t length) -{ - if(data && data->set.convfromnetwork) { - /* use translation callback */ - CURLcode result; - Curl_set_in_callback(data, true); - result = data->set.convfromnetwork(buffer, length); - Curl_set_in_callback(data, false); - if(result) { - failf(data, - "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s", - (int)result, curl_easy_strerror(result)); - } - - return result; - } - else { -#ifdef HAVE_ICONV - /* do the translation ourselves */ - iconv_t tmpcd = (iconv_t) -1; - iconv_t *cd = &tmpcd; - char *input_ptr, *output_ptr; - size_t in_bytes, out_bytes, rc; - char ebuffer[STRERROR_LEN]; - - /* open an iconv conversion descriptor if necessary */ - if(data) - cd = &data->inbound_cd; - if(*cd == (iconv_t)-1) { - *cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, - CURL_ICONV_CODESET_OF_NETWORK); - if(*cd == (iconv_t)-1) { - failf(data, - "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", - CURL_ICONV_CODESET_OF_HOST, - CURL_ICONV_CODESET_OF_NETWORK, - errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer))); - return CURLE_CONV_FAILED; - } - } - /* call iconv */ - input_ptr = output_ptr = buffer; - in_bytes = out_bytes = length; - rc = iconv(*cd, &input_ptr, &in_bytes, - &output_ptr, &out_bytes); - if(!data) - iconv_close(tmpcd); - if((rc == ICONV_ERROR) || (in_bytes)) { - failf(data, - "Curl_convert_from_network iconv call failed with errno %i: %s", - errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer))); - return CURLE_CONV_FAILED; - } -#else - failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback required"); - return CURLE_CONV_REQD; -#endif /* HAVE_ICONV */ - } - - return CURLE_OK; -} - -/* - * Curl_convert_from_utf8() is an internal function for performing UTF-8 - * conversions on non-ASCII platforms. - */ -CURLcode Curl_convert_from_utf8(struct Curl_easy *data, - char *buffer, size_t length) -{ - if(data && data->set.convfromutf8) { - /* use translation callback */ - CURLcode result; - Curl_set_in_callback(data, true); - result = data->set.convfromutf8(buffer, length); - Curl_set_in_callback(data, false); - if(result) { - failf(data, - "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s", - (int)result, curl_easy_strerror(result)); - } - - return result; - } - else { -#ifdef HAVE_ICONV - /* do the translation ourselves */ - iconv_t tmpcd = (iconv_t) -1; - iconv_t *cd = &tmpcd; - char *input_ptr; - char *output_ptr; - size_t in_bytes, out_bytes, rc; - char ebuffer[STRERROR_LEN]; - - /* open an iconv conversion descriptor if necessary */ - if(data) - cd = &data->utf8_cd; - if(*cd == (iconv_t)-1) { - *cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, - CURL_ICONV_CODESET_FOR_UTF8); - if(*cd == (iconv_t)-1) { - failf(data, - "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s", - CURL_ICONV_CODESET_OF_HOST, - CURL_ICONV_CODESET_FOR_UTF8, - errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer))); - return CURLE_CONV_FAILED; - } - } - /* call iconv */ - input_ptr = output_ptr = buffer; - in_bytes = out_bytes = length; - rc = iconv(*cd, &input_ptr, &in_bytes, - &output_ptr, &out_bytes); - if(!data) - iconv_close(tmpcd); - if((rc == ICONV_ERROR) || (in_bytes)) { - failf(data, - "The Curl_convert_from_utf8 iconv call failed with errno %i: %s", - errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer))); - return CURLE_CONV_FAILED; - } - if(output_ptr < input_ptr) { - /* null terminate the now shorter output string */ - *output_ptr = 0x00; - } -#else - failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback required"); - return CURLE_CONV_REQD; -#endif /* HAVE_ICONV */ - } - - return CURLE_OK; -} - -/* - * Init conversion stuff for a Curl_easy - */ -void Curl_convert_init(struct Curl_easy *data) -{ -#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) - /* conversion descriptors for iconv calls */ - data->outbound_cd = (iconv_t)-1; - data->inbound_cd = (iconv_t)-1; - data->utf8_cd = (iconv_t)-1; -#else - (void)data; -#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ -} - -/* - * Setup conversion stuff for a Curl_easy - */ -void Curl_convert_setup(struct Curl_easy *data) -{ - data->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, - CURL_ICONV_CODESET_OF_NETWORK); - data->outbound_cd = iconv_open(CURL_ICONV_CODESET_OF_NETWORK, - CURL_ICONV_CODESET_OF_HOST); - data->utf8_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST, - CURL_ICONV_CODESET_FOR_UTF8); -} - -/* - * Close conversion stuff for a Curl_easy - */ - -void Curl_convert_close(struct Curl_easy *data) -{ -#ifdef HAVE_ICONV - /* close iconv conversion descriptors */ - if(data->inbound_cd != (iconv_t)-1) { - iconv_close(data->inbound_cd); - } - if(data->outbound_cd != (iconv_t)-1) { - iconv_close(data->outbound_cd); - } - if(data->utf8_cd != (iconv_t)-1) { - iconv_close(data->utf8_cd); - } -#else - (void)data; -#endif /* HAVE_ICONV */ -} - -#endif /* CURL_DOES_CONVERSIONS */ diff --git a/lib/non-ascii.h b/lib/non-ascii.h deleted file mode 100644 index 458e8ef..0000000 --- a/lib/non-ascii.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef HEADER_CURL_NON_ASCII_H -#define HEADER_CURL_NON_ASCII_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ -#include "curl_setup.h" - -#ifdef CURL_DOES_CONVERSIONS - -#include "urldata.h" - -/* - * Curl_convert_clone() returns a malloced copy of the source string (if - * returning CURLE_OK), with the data converted to network format. - * - * If no conversion was needed *outbuf may be NULL. - */ -CURLcode Curl_convert_clone(struct Curl_easy *data, - const char *indata, - size_t insize, - char **outbuf); - -void Curl_convert_init(struct Curl_easy *data); -void Curl_convert_setup(struct Curl_easy *data); -void Curl_convert_close(struct Curl_easy *data); - -CURLcode Curl_convert_to_network(struct Curl_easy *data, - char *buffer, size_t length); -CURLcode Curl_convert_from_network(struct Curl_easy *data, - char *buffer, size_t length); -CURLcode Curl_convert_from_utf8(struct Curl_easy *data, - char *buffer, size_t length); -#else -#define Curl_convert_clone(a,b,c,d) ((void)a, CURLE_OK) -#define Curl_convert_init(x) Curl_nop_stmt -#define Curl_convert_setup(x) Curl_nop_stmt -#define Curl_convert_close(x) Curl_nop_stmt -#define Curl_convert_to_network(a,b,c) ((void)a, CURLE_OK) -#define Curl_convert_from_network(a,b,c) ((void)a, CURLE_OK) -#define Curl_convert_from_utf8(a,b,c) ((void)a, CURLE_OK) -#endif - -#endif /* HEADER_CURL_NON_ASCII_H */ diff --git a/lib/nonblock.c b/lib/nonblock.c index fda2e9a..28f6e75 100644 --- a/lib/nonblock.c +++ b/lib/nonblock.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -75,7 +75,7 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ #elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) - /* BeOS */ + /* Orbis OS */ long b = nonblock ? 1L : 0L; return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); diff --git a/lib/nwlib.c b/lib/nwlib.c deleted file mode 100644 index 7693268..0000000 --- a/lib/nwlib.c +++ /dev/null @@ -1,327 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef NETWARE /* Novell NetWare */ - -#ifdef __NOVELL_LIBC__ -/* For native LibC-based NLM we need to register as a real lib. */ -#include -#include -#include -#include -#include - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -struct libthreaddata { - int _errno; - void *twentybytes; -}; - -struct libdata { - int x; - int y; - int z; - void *tenbytes; - NXKey_t perthreadkey; /* if -1, no key obtained... */ - NXMutex_t *lock; -}; - -int gLibId = -1; -void *gLibHandle = (void *) NULL; -rtag_t gAllocTag = (rtag_t) NULL; -NXMutex_t *gLibLock = (NXMutex_t *) NULL; - -/* internal library function prototypes... */ -int DisposeLibraryData(void *); -void DisposeThreadData(void *); -int GetOrSetUpData(int id, struct libdata **data, - struct libthreaddata **threaddata); - - -int _NonAppStart(void *NLMHandle, - void *errorScreen, - const char *cmdLine, - const char *loadDirPath, - size_t uninitializedDataLength, - void *NLMFileHandle, - int (*readRoutineP)(int conn, - void *fileHandle, size_t offset, - size_t nbytes, - size_t *bytesRead, - void *buffer), - size_t customDataOffset, - size_t customDataSize, - int messageCount, - const char **messages) -{ - NX_LOCK_INFO_ALLOC(liblock, "Per-Application Data Lock", 0); - -#ifndef __GNUC__ -#pragma unused(cmdLine) -#pragma unused(loadDirPath) -#pragma unused(uninitializedDataLength) -#pragma unused(NLMFileHandle) -#pragma unused(readRoutineP) -#pragma unused(customDataOffset) -#pragma unused(customDataSize) -#pragma unused(messageCount) -#pragma unused(messages) -#endif - - /* - * Here we process our command line, post errors (to the error screen), - * perform initializations and anything else we need to do before being able - * to accept calls into us. If we succeed, we return non-zero and the NetWare - * Loader will leave us up, otherwise we fail to load and get dumped. - */ - gAllocTag = AllocateResourceTag(NLMHandle, - " memory allocations", - AllocSignature); - - if(!gAllocTag) { - OutputToScreen(errorScreen, "Unable to allocate resource tag for " - "library memory allocations.\n"); - return -1; - } - - gLibId = register_library(DisposeLibraryData); - - if(gLibId < -1) { - OutputToScreen(errorScreen, "Unable to register library with kernel.\n"); - return -1; - } - - gLibHandle = NLMHandle; - - gLibLock = NXMutexAlloc(0, 0, &liblock); - - if(!gLibLock) { - OutputToScreen(errorScreen, "Unable to allocate library data lock.\n"); - return -1; - } - - return 0; -} - -/* - * Here we clean up any resources we allocated. Resource tags is a big part - * of what we created, but NetWare doesn't ask us to free those. - */ -void _NonAppStop(void) -{ - (void) unregister_library(gLibId); - NXMutexFree(gLibLock); -} - -/* - * This function cannot be the first in the file for if the file is linked - * first, then the check-unload function's offset will be nlmname.nlm+0 - * which is how to tell that there isn't one. When the check function is - * first in the linked objects, it is ambiguous. For this reason, we will - * put it inside this file after the stop function. - * - * Here we check to see if it's alright to ourselves to be unloaded. If not, - * we return a non-zero value. Right now, there isn't any reason not to allow - * it. - */ -int _NonAppCheckUnload(void) -{ - return 0; -} - -int GetOrSetUpData(int id, struct libdata **appData, - struct libthreaddata **threadData) -{ - int err; - struct libdata *app_data; - struct libthreaddata *thread_data; - NXKey_t key; - NX_LOCK_INFO_ALLOC(liblock, "Application Data Lock", 0); - - err = 0; - thread_data = (struct libthreaddata_t *) NULL; - - /* - * Attempt to get our data for the application calling us. This is where we - * store whatever application-specific information we need to carry in - * support of calling applications. - */ - app_data = (struct libdata *) get_app_data(id); - - if(!app_data) { - /* - * This application hasn't called us before; set up application AND - * per-thread data. Of course, just in case a thread from this same - * application is calling us simultaneously, we better lock our application - * data-creation mutex. We also need to recheck for data after we acquire - * the lock because WE might be that other thread that was too late to - * create the data and the first thread in will have created it. - */ - NXLock(gLibLock); - - app_data = (struct libdata *) get_app_data(id); - if(!app_data) { - app_data = calloc(1, sizeof(struct libdata)); - - if(app_data) { - app_data->tenbytes = malloc(10); - app_data->lock = NXMutexAlloc(0, 0, &liblock); - - if(!app_data->tenbytes || !app_data->lock) { - if(app_data->lock) - NXMutexFree(app_data->lock); - free(app_data->tenbytes); - free(app_data); - app_data = (libdata_t *) NULL; - err = ENOMEM; - } - - if(app_data) { - /* - * Here we burn in the application data that we were trying to get - * by calling get_app_data(). Next time we call the first function, - * we'll get this data we're just now setting. We also go on here to - * establish the per-thread data for the calling thread, something - * we'll have to do on each application thread the first time - * it calls us. - */ - err = set_app_data(gLibId, app_data); - - if(err) { - if(app_data->lock) - NXMutexFree(app_data->lock); - free(app_data->tenbytes); - free(app_data); - app_data = (libdata_t *) NULL; - err = ENOMEM; - } - else { - /* create key for thread-specific data... */ - err = NXKeyCreate(DisposeThreadData, (void *) NULL, &key); - - if(err) /* (no more keys left?) */ - key = -1; - - app_data->perthreadkey = key; - } - } - } - } - - NXUnlock(gLibLock); - } - - if(app_data) { - key = app_data->perthreadkey; - - if(key != -1 /* couldn't create a key? no thread data */ - && !(err = NXKeyGetValue(key, (void **) &thread_data)) - && !thread_data) { - /* - * Allocate the per-thread data for the calling thread. Regardless of - * whether there was already application data or not, this may be the - * first call by a new thread. The fact that we allocation 20 bytes on - * a pointer is not very important, this just helps to demonstrate that - * we can have arbitrarily complex per-thread data. - */ - thread_data = malloc(sizeof(struct libthreaddata)); - - if(thread_data) { - thread_data->_errno = 0; - thread_data->twentybytes = malloc(20); - - if(!thread_data->twentybytes) { - free(thread_data); - thread_data = (struct libthreaddata *) NULL; - err = ENOMEM; - } - - err = NXKeySetValue(key, thread_data); - if(err) { - free(thread_data->twentybytes); - free(thread_data); - thread_data = (struct libthreaddata *) NULL; - } - } - } - } - - if(appData) - *appData = app_data; - - if(threadData) - *threadData = thread_data; - - return err; -} - -int DisposeLibraryData(void *data) -{ - if(data) { - void *tenbytes = ((libdata_t *) data)->tenbytes; - - free(tenbytes); - free(data); - } - - return 0; -} - -void DisposeThreadData(void *data) -{ - if(data) { - void *twentybytes = ((struct libthreaddata *) data)->twentybytes; - - free(twentybytes); - free(data); - } -} - -#else /* __NOVELL_LIBC__ */ -/* For native CLib-based NLM seems we can do a bit more simple. */ -#include - -int main(void) -{ - /* initialize any globals here... */ - - /* do this if any global initializing was done - SynchronizeStart(); - */ - ExitThread(TSR_THREAD, 0); - return 0; -} - -#endif /* __NOVELL_LIBC__ */ - -#else /* NETWARE */ - -#ifdef __POCC__ -# pragma warn(disable:2024) /* Disable warning #2024: Empty input file */ -#endif - -#endif /* NETWARE */ diff --git a/lib/nwos.c b/lib/nwos.c deleted file mode 100644 index 8894031..0000000 --- a/lib/nwos.c +++ /dev/null @@ -1,88 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef NETWARE /* Novell NetWare */ - -#ifdef __NOVELL_LIBC__ -/* For native LibC-based NLM we need to do nothing. */ -int netware_init(void) -{ - return 0; -} - -#else /* __NOVELL_LIBC__ */ - -/* For native CLib-based NLM we need to initialize the LONG namespace. */ -#include -#include -#include -/* Make the CLIB Ctx stuff link */ -#include -NETDB_DEFINE_CONTEXT -/* Make the CLIB Inet stuff link */ -#include -#include -NETINET_DEFINE_CONTEXT - -int netware_init(void) -{ - int rc = 0; - unsigned int myHandle = GetNLMHandle(); - /* import UnAugmentAsterisk dynamically for NW4.x compatibility */ - void (*pUnAugmentAsterisk)(int) = (void(*)(int)) - ImportSymbol(myHandle, "UnAugmentAsterisk"); - /* import UseAccurateCaseForPaths dynamically for NW3.x compatibility */ - void (*pUseAccurateCaseForPaths)(int) = (void(*)(int)) - ImportSymbol(myHandle, "UseAccurateCaseForPaths"); - if(pUnAugmentAsterisk) - pUnAugmentAsterisk(1); - if(pUseAccurateCaseForPaths) - pUseAccurateCaseForPaths(1); - UnimportSymbol(myHandle, "UnAugmentAsterisk"); - UnimportSymbol(myHandle, "UseAccurateCaseForPaths"); - /* set long name space */ - if((SetCurrentNameSpace(4) == 255)) { - rc = 1; - } - if((SetTargetNameSpace(4) == 255)) { - rc = rc + 2; - } - return rc; -} - -/* dummy function to satisfy newer prelude */ -int __init_environment(void) -{ - return 0; -} - -/* dummy function to satisfy newer prelude */ -int __deinit_environment(void) -{ - return 0; -} - -#endif /* __NOVELL_LIBC__ */ - -#endif /* NETWARE */ diff --git a/lib/openldap.c b/lib/openldap.c index 0ffb6a3..4e92567 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2011 - 2022, Daniel Stenberg, , et al. * Copyright (C) 2010, Howard Chu, * * This software is licensed as described in the file COPYING, which @@ -46,6 +46,8 @@ #include "curl_ldap.h" #include "curl_base64.h" #include "connect.h" +#include "curl_sasl.h" +#include "strcase.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -76,6 +78,8 @@ typedef enum { OLDAP_SSL, /* Performing SSL handshake. */ OLDAP_STARTTLS, /* STARTTLS request sent. */ OLDAP_TLS, /* Performing TLS handshake. */ + OLDAP_MECHS, /* Get SASL authentication mechanisms. */ + OLDAP_SASL, /* SASL binding reply. */ OLDAP_BIND, /* Simple bind reply. */ OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */ OLDAP_LAST /* Never used */ @@ -96,6 +100,13 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); static CURLcode oldap_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead); +static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); + static Curl_recv oldap_recv; /* @@ -154,10 +165,26 @@ const struct Curl_handler Curl_handler_ldaps = { }; #endif +/* SASL parameters for the ldap protocol */ +static const struct SASLproto saslldap = { + "ldap", /* The service name */ + oldap_perform_auth, /* Send authentication command */ + oldap_continue_auth, /* Send authentication continuation */ + oldap_cancel_auth, /* Send authentication cancellation */ + oldap_get_message, /* Get SASL response message */ + 0, /* Maximum initial response length (no max) */ + LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ + LDAP_SUCCESS, /* Code to receive upon authentication success */ + SASL_AUTH_NONE, /* Default mechanisms */ + 0 /* Configuration flags */ +}; + struct ldapconninfo { + struct SASL sasl; /* SASL-related parameters */ LDAP *ld; /* Openldap connection handle. */ Curl_recv *recv; /* For stacking SSL handler */ Curl_send *send; + struct berval *servercred; /* SASL data from server. */ ldapstate state; /* Current machine state. */ int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */ int msgid; /* Current message id. */ @@ -184,6 +211,8 @@ static void state(struct Curl_easy *data, ldapstate newstate) "SSL", "STARTTLS", "TLS", + "MECHS", + "SASL", "BIND", "BINDV2", /* LAST */ @@ -251,6 +280,37 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) return result; } +/* Parse the login options. */ +static CURLcode oldap_parse_login_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = conn->proto.ldapc; + const char *ptr = conn->options; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(checkprefix("AUTH=", key)) + result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value); + else + result = CURLE_SETOPT_OPTION_SYNTAX; + + if(*ptr == ';') + ptr++; + } + + return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result; +} + static CURLcode oldap_setup_connection(struct Curl_easy *data, struct connectdata *conn) { @@ -271,14 +331,94 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data, conn->proto.ldapc = li; connkeep(conn, "OpenLDAP default"); + /* Initialize the SASL storage */ + Curl_sasl_init(&li->sasl, data, &saslldap); + /* Clear the TLS upgraded flag */ conn->bits.tls_upgraded = FALSE; + + result = oldap_parse_login_options(conn); } } return result; } +/* + * Get the SASL authentication challenge from the server credential buffer. + */ +static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out) +{ + struct berval *servercred = data->conn->proto.ldapc->servercred; + + if(!servercred || !servercred->bv_val) + return CURLE_WEIRD_SERVER_REPLY; + Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL); + return CURLE_OK; +} + +/* + * Sends an initial SASL bind request to the server. + */ +static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval cred; + struct berval *pcred = &cred; + int rc; + + cred.bv_val = (char *) Curl_bufref_ptr(initresp); + cred.bv_len = Curl_bufref_len(initresp); + if(!cred.bv_val) + pcred = NULL; + rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* + * Sends SASL continuation. + */ +static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + struct berval cred; + struct berval *pcred = &cred; + int rc; + + cred.bv_val = (char *) Curl_bufref_ptr(resp); + cred.bv_len = Curl_bufref_len(resp); + if(!cred.bv_val) + pcred = NULL; + rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + +/* + * Sends SASL bind cancellation. + */ +static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct ldapconninfo *li = data->conn->proto.ldapc; + CURLcode result = CURLE_OK; + int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL, + &li->msgid); + + (void)mech; + if(rc != LDAP_SUCCESS) + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); + return result; +} + /* Starts LDAP simple bind. */ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) { @@ -292,7 +432,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(conn->bits.user_passwd) { + if(data->state.aptr.user) { binddn = conn->user; passwd.bv_val = conn->passwd; passwd.bv_len = strlen(passwd.bv_val); @@ -304,11 +444,45 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) state(data, newstate); else result = oldap_map_error(rc, - conn->bits.user_passwd? + data->state.aptr.user? CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND); return result; } +/* Query the supported SASL authentication mechanisms. */ +static CURLcode oldap_perform_mechs(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = data->conn->proto.ldapc; + int rc; + static const char * const supportedSASLMechanisms[] = { + "supportedSASLMechanisms", + NULL + }; + + rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", + (char **) supportedSASLMechanisms, 0, + NULL, NULL, NULL, 0, &li->msgid); + if(rc == LDAP_SUCCESS) + state(data, OLDAP_MECHS); + else + result = oldap_map_error(rc, CURLE_LOGIN_DENIED); + return result; +} + +/* Starts SASL bind. */ +static CURLcode oldap_perform_sasl(struct Curl_easy *data) +{ + saslprogress progress = SASL_IDLE; + struct ldapconninfo *li = data->conn->proto.ldapc; + CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress); + + state(data, OLDAP_SASL); + if(!result && progress != SASL_INPROGRESS) + result = CURLE_LOGIN_DENIED; + return result; +} + #ifdef USE_SSL static Sockbuf_IO ldapsb_tls; @@ -414,11 +588,106 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) } #endif + if(li->sasl.prefmech != SASL_AUTH_NONE) + return oldap_perform_mechs(data); + /* 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); } +/* Handle the supported SASL mechanisms query response */ +static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, + LDAPMessage *msg, int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + int rc; + BerElement *ber = NULL; + CURLcode result = CURLE_OK; + struct berval bv, *bvals; + + switch(ldap_msgtype(msg)) { + case LDAP_RES_SEARCH_ENTRY: + /* Got a list of supported SASL mechanisms. */ + if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED) + return CURLE_LOGIN_DENIED; + + rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); + if(rc < 0) + return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING); + for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { + int i; + + if(!bv.bv_val) + break; + + if(bvals) { + for(i = 0; bvals[i].bv_val; i++) { + size_t llen; + unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val, + bvals[i].bv_len, &llen); + if(bvals[i].bv_len == llen) + li->sasl.authmechs |= mech; + } + ber_memfree(bvals); + } + } + ber_free(ber, 0); + break; + + case LDAP_RES_SEARCH_RESULT: + switch(code) { + case LDAP_SIZELIMIT_EXCEEDED: + infof(data, "Too many authentication mechanisms\n"); + /* FALLTHROUGH */ + case LDAP_SUCCESS: + case LDAP_NO_RESULTS_RETURNED: + if(Curl_sasl_can_authenticate(&li->sasl, data)) + result = oldap_perform_sasl(data); + else + result = CURLE_LOGIN_DENIED; + break; + default: + result = oldap_map_error(code, CURLE_LOGIN_DENIED); + break; + } + break; + default: + break; + } + return result; +} + +/* Handle a SASL bind response. */ +static CURLcode oldap_state_sasl_resp(struct Curl_easy *data, + LDAPMessage *msg, int code) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + CURLcode result = CURLE_OK; + saslprogress progress; + int rc; + + li->servercred = NULL; + rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc)); + result = oldap_map_error(rc, CURLE_LOGIN_DENIED); + } + else { + result = Curl_sasl_continue(&li->sasl, data, code, &progress); + if(!result && progress != SASL_INPROGRESS) + state(data, OLDAP_STOP); + } + + if(li->servercred) + ber_bvfree(li->servercred); + return result; +} + /* Handle a simple bind response. */ static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg, int code) @@ -459,12 +728,20 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) 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); + switch(rc) { + case 0: /* Timed out. */ + return CURLE_OK; + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + break; + default: + li->msgid = 0; /* Nothing to abandon upon error. */ + if(rc < 0) { + failf(data, "LDAP local: connecting ldap_result %s", + ldap_err2string(rc)); + return oldap_map_error(rc, CURLE_COULDNT_CONNECT); + } + break; } /* Get error code from message. */ @@ -477,11 +754,11 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) } /* If protocol version 3 is not supported, fallback to version 2. */ - if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 + if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 && #ifdef USE_SSL - && (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) + (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) && #endif - ) { + li->sasl.prefmech == SASL_AUTH_NONE) { static const int version = LDAP_VERSION2; ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); @@ -496,13 +773,19 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) #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); + if(!result && ssl_installed(conn)) { + if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else + 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 if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); else result = oldap_perform_bind(data, OLDAP_BIND); break; @@ -514,7 +797,9 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) result = oldap_map_error(code, CURLE_USE_SSL_FAILED); else if(ssl_installed(conn)) { conn->bits.tls_upgraded = TRUE; - if(conn->bits.user_passwd) + if(li->sasl.prefmech != SASL_AUTH_NONE) + result = oldap_perform_mechs(data); + else if(data->state.aptr.user) result = oldap_perform_bind(data, OLDAP_BIND); else { state(data, OLDAP_STOP); /* Version 3 supported: no bind required */ @@ -524,6 +809,12 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) break; #endif + case OLDAP_MECHS: + result = oldap_state_mechs_resp(data, msg, code); + break; + case OLDAP_SASL: + result = oldap_state_sasl_resp(data, msg, code); + break; case OLDAP_BIND: case OLDAP_BINDV2: result = oldap_state_bind_resp(data, msg, code); @@ -540,6 +831,10 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) if(*done) conn->recv[FIRSTSOCKET] = oldap_recv; + if(result && li->msgid) { + ldap_abandon_ext(li->ld, li->msgid, NULL, NULL); + li->msgid = 0; + } return result; } @@ -549,6 +844,9 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, { struct ldapconninfo *li = conn->proto.ldapc; (void) dead_connection; +#ifndef USE_SSL + (void)data; +#endif if(li) { if(li->ld) { @@ -562,6 +860,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, ldap_unbind_ext(li->ld, NULL, NULL); li->ld = NULL; } + Curl_sasl_cleanup(conn, li->sasl.authused); conn->proto.ldapc = NULL; free(li); } @@ -632,21 +931,21 @@ 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) +static CURLcode client_write(struct Curl_easy *data, + const char *prefix, size_t plen, + const char *value, size_t len, + const char *suffix, size_t slen) { 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(!len && plen && prefix[plen - 1] == ' ') + plen--; + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen); if(!result) - data->req.bytecount += l; + data->req.bytecount += plen; } if(!result && value) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); @@ -654,10 +953,9 @@ static CURLcode client_write(struct Curl_easy *data, const char *prefix, data->req.bytecount += len; } if(!result && suffix) { - l = strlen(suffix); - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, l); + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen); if(!result) - data->req.bytecount += l; + data->req.bytecount += slen; } return result; } @@ -734,7 +1032,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, break; } - result = client_write(data, "DN: ", bv.bv_val, bv.bv_len, "\n"); + result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len, + STRCONST("\n")); if(result) break; @@ -747,7 +1046,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, break; if(!bvals) { - result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":\n"); + result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, + STRCONST(":\n")); if(result) break; continue; @@ -759,7 +1059,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, for(i = 0; bvals[i].bv_val != NULL; i++) { int binval = 0; - result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":"); + result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, + STRCONST(":")); if(result) break; @@ -784,15 +1085,17 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, /* Binary value, encode to base64. */ if(bvals[i].bv_len) - result = Curl_base64_encode(data, bvals[i].bv_val, bvals[i].bv_len, + result = Curl_base64_encode(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"); + result = client_write(data, STRCONST(": "), val_b64, val_b64_sz, + STRCONST("\n")); free(val_b64); } else - result = client_write(data, " ", - bvals[i].bv_val, bvals[i].bv_len, "\n"); + result = client_write(data, STRCONST(" "), + bvals[i].bv_val, bvals[i].bv_len, + STRCONST("\n")); if(result) break; } @@ -800,7 +1103,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, ber_memfree(bvals); bvals = NULL; if(!result) - result = client_write(data, "\n", NULL, 0, NULL); + result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); if(result) break; } @@ -808,7 +1111,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, ber_free(ber, 0); if(!result) - result = client_write(data, "\n", NULL, 0, NULL); + result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); if(!result) result = CURLE_AGAIN; break; diff --git a/lib/pingpong.c b/lib/pingpong.c index 84c7f51..e08c1d8 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,7 +32,6 @@ #include "speedcheck.h" #include "pingpong.h" #include "multiif.h" -#include "non-ascii.h" #include "vtls/vtls.h" /* The last 3 #include files should be in this order */ @@ -199,11 +198,6 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, s = Curl_dyn_ptr(&pp->sendbuf); Curl_pp_init(data, pp); - result = Curl_convert_to_network(data, s, write_len); - /* Curl_convert_to_network calls failf if unsuccessful */ - if(result) - return result; - #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif @@ -299,7 +293,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, */ if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) { failf(data, "cached response data too big to handle"); - return CURLE_RECV_ERROR; + return CURLE_WEIRD_SERVER_REPLY; } memcpy(ptr, pp->cache, pp->cache_size); gotbytes = (ssize_t)pp->cache_size; @@ -324,11 +318,6 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, if(result == CURLE_AGAIN) return CURLE_OK; /* return */ - if(!result && (gotbytes > 0)) - /* convert from the network encoding */ - result = Curl_convert_from_network(data, ptr, gotbytes); - /* Curl_convert_from_network calls failf if unsuccessful */ - if(result) /* Set outer result variable to this error. */ keepon = FALSE; diff --git a/lib/pop3.c b/lib/pop3.c index d4ca678..2c1b06c 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -401,7 +401,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!data->state.aptr.user) { state(data, POP3_STOP); return result; @@ -435,7 +435,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!data->state.aptr.user) { state(data, POP3_STOP); return result; @@ -545,7 +545,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, /* Check we have enough data to authenticate with and end the connect phase if we don't */ - if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) { + if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { state(data, POP3_STOP); return result; } @@ -571,7 +571,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, result = pop3_perform_user(data, conn); else { /* Other mechanisms not supported */ - infof(data, "No known authentication mechanisms supported!"); + infof(data, "No known authentication mechanisms supported"); result = CURLE_LOGIN_DENIED; } } @@ -924,7 +924,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, if(pop3code != '+') { state(data, POP3_STOP); - return CURLE_RECV_ERROR; + return CURLE_WEIRD_SERVER_REPLY; } /* This 'OK' line ends with a CR LF pair which is the two first bytes of the @@ -1423,7 +1423,7 @@ static CURLcode pop3_parse_url_path(struct Curl_easy *data) const char *path = &data->state.up.path[1]; /* skip leading path */ /* URL decode the path for the message ID */ - return Curl_urldecode(data, path, 0, &pop3->id, NULL, REJECT_CTRL); + return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); } /*********************************************************************** @@ -1440,7 +1440,7 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data) /* URL decode the custom request */ if(custom) - result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, REJECT_CTRL); + result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); return result; } diff --git a/lib/quic.h b/lib/quic.h index b030359..f92720f 100644 --- a/lib/quic.h +++ b/lib/quic.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,6 +31,9 @@ #ifdef USE_QUICHE #include "vquic/quiche.h" #endif +#ifdef USE_MSH3 +#include "vquic/msh3.h" +#endif #include "urldata.h" diff --git a/lib/rand.c b/lib/rand.c index 8f2c1ba..8da1e8d 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -87,7 +87,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) if(!seeded) { struct curltime now = Curl_now(); - infof(data, "WARNING: Using weak random seed"); + infof(data, "WARNING: using weak random seed"); randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; randseed = randseed * 1103515245 + 12345; randseed = randseed * 1103515245 + 12345; diff --git a/lib/rtsp.c b/lib/rtsp.c index 30fefb9..726bfb9 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -219,7 +219,7 @@ static CURLcode rtsp_done(struct Curl_easy *data, httpStatus = Curl_http_done(data, status, premature); - if(rtsp) { + if(rtsp && !status && !httpStatus) { /* Check the sequence numbers */ long CSeq_sent = rtsp->CSeq_sent; long CSeq_recv = rtsp->CSeq_recv; @@ -340,7 +340,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) } /* Transport Header for SETUP requests */ - p_transport = Curl_checkheaders(data, "Transport"); + p_transport = Curl_checkheaders(data, STRCONST("Transport")); if(rtspreq == RTSPREQ_SETUP && !p_transport) { /* New Transport: setting? */ if(data->set.str[STRING_RTSP_TRANSPORT]) { @@ -364,11 +364,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* Accept Headers for DESCRIBE requests */ if(rtspreq == RTSPREQ_DESCRIBE) { /* Accept Header */ - p_accept = Curl_checkheaders(data, "Accept")? + p_accept = Curl_checkheaders(data, STRCONST("Accept"))? NULL:"Accept: application/sdp\r\n"; /* Accept-Encoding header */ - if(!Curl_checkheaders(data, "Accept-Encoding") && + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { Curl_safefree(data->state.aptr.accept_encoding); data->state.aptr.accept_encoding = @@ -385,11 +385,12 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, "User-Agent") && data->state.aptr.uagent) { + if(Curl_checkheaders(data, STRCONST("User-Agent")) && + data->state.aptr.uagent) { Curl_safefree(data->state.aptr.uagent); data->state.aptr.uagent = NULL; } - else if(!Curl_checkheaders(data, "User-Agent") && + else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { p_uagent = data->state.aptr.uagent; } @@ -405,7 +406,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* Referrer */ Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, "Referer")) + if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); else data->state.aptr.ref = NULL; @@ -422,7 +423,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { /* Check to see if there is a range set in the custom headers */ - if(!Curl_checkheaders(data, "Range") && data->state.range) { + if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { Curl_safefree(data->state.aptr.rangeline); data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range); p_range = data->state.aptr.rangeline; @@ -432,11 +433,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* * Sanity check the custom headers */ - if(Curl_checkheaders(data, "CSeq")) { + if(Curl_checkheaders(data, STRCONST("CSeq"))) { failf(data, "CSeq cannot be set as a custom header."); return CURLE_RTSP_CSEQ_ERROR; } - if(Curl_checkheaders(data, "Session")) { + if(Curl_checkheaders(data, STRCONST("Session"))) { failf(data, "Session ID cannot be set as a custom header."); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -523,7 +524,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) if(putsize > 0 || postsize > 0) { /* As stated in the http comments, it is probably not wise to * actually set a custom Content-Length in the headers */ - if(!Curl_checkheaders(data, "Content-Length")) { + if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { result = Curl_dyn_addf(&req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", @@ -534,18 +535,20 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) if(rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { - if(!Curl_checkheaders(data, "Content-Type")) { - result = Curl_dyn_addf(&req_buffer, - "Content-Type: text/parameters\r\n"); + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(&req_buffer, + STRCONST("Content-Type: " + "text/parameters\r\n")); if(result) return result; } } if(rtspreq == RTSPREQ_ANNOUNCE) { - if(!Curl_checkheaders(data, "Content-Type")) { - result = Curl_dyn_addf(&req_buffer, - "Content-Type: application/sdp\r\n"); + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(&req_buffer, + STRCONST("Content-Type: " + "application/sdp\r\n")); if(result) return result; } @@ -563,7 +566,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* RTSP never allows chunked transfer */ data->req.forbidchunk = TRUE; /* Finish the request buffer */ - result = Curl_dyn_add(&req_buffer, "\r\n"); + result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n")); if(result) return result; diff --git a/lib/select.c b/lib/select.c index 70d7ee5..a48da82 100644 --- a/lib/select.c +++ b/lib/select.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,25 +34,16 @@ #error "We can't compile without select() or poll() support." #endif -#if defined(__BEOS__) && !defined(__HAIKU__) -/* BeOS has FD_SET defined in socket.h */ -#include -#endif - #ifdef MSDOS #include /* delay() */ #endif -#ifdef __VXWORKS__ -#include /* bzero() in FD_SET */ -#endif - #include #include "urldata.h" #include "connect.h" #include "select.h" -#include "timeval.h" +#include "timediff.h" #include "warnless.h" /* @@ -102,26 +93,7 @@ int Curl_wait_ms(timediff_t timeout_ms) #else { struct timeval pending_tv; - timediff_t tv_sec = timeout_ms / 1000; - timediff_t tv_usec = (timeout_ms % 1000) * 1000; /* max=999999 */ -#ifdef HAVE_SUSECONDS_T -#if TIMEDIFF_T_MAX > TIME_T_MAX - /* tv_sec overflow check in case time_t is signed */ - if(tv_sec > TIME_T_MAX) - tv_sec = TIME_T_MAX; -#endif - pending_tv.tv_sec = (time_t)tv_sec; - pending_tv.tv_usec = (suseconds_t)tv_usec; -#else -#if TIMEDIFF_T_MAX > INT_MAX - /* tv_sec overflow check in case time_t is signed */ - if(tv_sec > INT_MAX) - tv_sec = INT_MAX; -#endif - pending_tv.tv_sec = (int)tv_sec; - pending_tv.tv_usec = (int)tv_usec; -#endif - r = select(0, NULL, NULL, NULL, &pending_tv); + r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms)); } #endif /* HAVE_POLL_FINE */ #endif /* USE_WINSOCK */ @@ -161,43 +133,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ } #endif - ptimeout = &pending_tv; - if(timeout_ms < 0) { - ptimeout = NULL; - } - else if(timeout_ms > 0) { - timediff_t tv_sec = timeout_ms / 1000; - timediff_t tv_usec = (timeout_ms % 1000) * 1000; /* max=999999 */ -#ifdef HAVE_SUSECONDS_T -#if TIMEDIFF_T_MAX > TIME_T_MAX - /* tv_sec overflow check in case time_t is signed */ - if(tv_sec > TIME_T_MAX) - tv_sec = TIME_T_MAX; -#endif - pending_tv.tv_sec = (time_t)tv_sec; - pending_tv.tv_usec = (suseconds_t)tv_usec; -#elif defined(WIN32) /* maybe also others in the future */ -#if TIMEDIFF_T_MAX > LONG_MAX - /* tv_sec overflow check on Windows there we know it is long */ - if(tv_sec > LONG_MAX) - tv_sec = LONG_MAX; -#endif - pending_tv.tv_sec = (long)tv_sec; - pending_tv.tv_usec = (long)tv_usec; -#else -#if TIMEDIFF_T_MAX > INT_MAX - /* tv_sec overflow check in case time_t is signed */ - if(tv_sec > INT_MAX) - tv_sec = INT_MAX; -#endif - pending_tv.tv_sec = (int)tv_sec; - pending_tv.tv_usec = (int)tv_usec; -#endif - } - else { - pending_tv.tv_sec = 0; - pending_tv.tv_usec = 0; - } + ptimeout = curlx_mstotv(&pending_tv, timeout_ms); #ifdef USE_WINSOCK /* WinSock select() must not be called with an fd_set that contains zero @@ -450,23 +386,3 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) return r; } - -#ifdef TPF -/* - * This is a replacement for select() on the TPF platform. - * It is used whenever libcurl calls select(). - * The call below to tpf_process_signals() is required because - * TPF's select calls are not signal interruptible. - * - * Return values are the same as select's. - */ -int tpf_select_libcurl(int maxfds, fd_set *reads, fd_set *writes, - fd_set *excepts, struct timeval *tv) -{ - int rc; - - rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv); - tpf_process_signals(); - return rc; -} -#endif /* TPF */ diff --git a/lib/select.h b/lib/select.h index 59a571d..f4bcba3 100644 --- a/lib/select.h +++ b/lib/select.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,22 +83,11 @@ int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); int Curl_wait_ms(timediff_t timeout_ms); -#ifdef TPF -int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, - fd_set* excepts, struct timeval *tv); -#endif - -/* TPF sockets are not in range [0..FD_SETSIZE-1], which - unfortunately makes it impossible for us to easily check if they're valid - +/* With Winsock the valid range is [0..INVALID_SOCKET-1] according to https://docs.microsoft.com/en-us/windows/win32/winsock/socket-data-type-2 */ -#if defined(TPF) -#define VALID_SOCK(x) 1 -#define VERIFY_SOCK(x) Curl_nop_stmt -#define FDSET_SOCK(x) 1 -#elif defined(USE_WINSOCK) +#ifdef USE_WINSOCK #define VALID_SOCK(s) ((s) < INVALID_SOCKET) #define FDSET_SOCK(x) 1 #define VERIFY_SOCK(x) do { \ diff --git a/lib/sendf.c b/lib/sendf.c index bcfa27a..d7d4d8a 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -41,11 +41,11 @@ #include "vssh/ssh.h" #include "easyif.h" #include "multiif.h" -#include "non-ascii.h" #include "strerror.h" #include "select.h" #include "strdup.h" #include "http2.h" +#include "headers.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -566,7 +566,7 @@ static CURLcode chop_write(struct Curl_easy *data, /* Protocols that work without network cannot be paused. This is actually only FILE:// just now, and it can't pause since the transfer isn't done using the "normal" procedure. */ - failf(data, "Write callback asked for PAUSE when not supported!"); + failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } return pausewrite(data, type, ptr, len); @@ -581,21 +581,33 @@ static CURLcode chop_write(struct Curl_easy *data, len -= chunklen; } + /* HTTP header, but not status-line */ + if((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) { + CURLcode result = + Curl_headers_push(data, optr, + type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : + (type & CLIENTWRITE_1XX ? CURLH_1XX : + (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : + CURLH_HEADER))); + if(result) + return result; + } + if(writeheader) { size_t wrote; - ptr = optr; - len = olen; + Curl_set_in_callback(data, true); - wrote = writeheader(ptr, 1, len, data->set.writeheader); + wrote = writeheader(optr, 1, olen, data->set.writeheader); Curl_set_in_callback(data, false); if(CURL_WRITEFUNC_PAUSE == wrote) /* here we pass in the HEADER bit only since if this was body as well then it was passed already and clearly that didn't trigger the pause, so this is saved for later with the HEADER bit only */ - return pausewrite(data, CLIENTWRITE_HEADER, ptr, len); + return pausewrite(data, CLIENTWRITE_HEADER, optr, olen); - if(wrote != len) { + if(wrote != olen) { failf(data, "Failed writing header"); return CURLE_WRITE_ERROR; } @@ -621,26 +633,19 @@ CURLcode Curl_client_write(struct Curl_easy *data, { struct connectdata *conn = data->conn; - DEBUGASSERT(!(type & ~CLIENTWRITE_BOTH)); - if(!len) return CURLE_OK; /* FTP data may need conversion. */ if((type & CLIENTWRITE_BODY) && - (conn->handler->protocol & PROTO_FAMILY_FTP) && - conn->proto.ftpc.transfertype == 'A') { - /* convert from the network encoding */ - CURLcode result = Curl_convert_from_network(data, ptr, len); - /* Curl_convert_from_network calls failf if unsuccessful */ - if(result) - return result; + (conn->handler->protocol & PROTO_FAMILY_FTP) && + conn->proto.ftpc.transfertype == 'A') { #ifdef CURL_DO_LINEEND_CONV /* convert end-of-line markers */ len = convert_lineends(data, ptr, len); #endif /* CURL_DO_LINEEND_CONV */ - } + } return chop_write(data, type, ptr, len); } @@ -716,44 +721,6 @@ int Curl_debug(struct Curl_easy *data, curl_infotype type, if(data->set.verbose) { static const char s_infotype[CURLINFO_END][3] = { "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; - -#ifdef CURL_DOES_CONVERSIONS - char *buf = NULL; - size_t conv_size = 0; - - switch(type) { - case CURLINFO_HEADER_OUT: - buf = Curl_memdup(ptr, size); - if(!buf) - return 1; - conv_size = size; - - /* Special processing is needed for this block if it - * contains both headers and data (separated by CRLFCRLF). - * We want to convert just the headers, leaving the data as-is. - */ - if(size > 4) { - size_t i; - for(i = 0; i < size-4; i++) { - if(memcmp(&buf[i], "\x0d\x0a\x0d\x0a", 4) == 0) { - /* convert everything through this CRLFCRLF but no further */ - conv_size = i + 4; - break; - } - } - } - - Curl_convert_from_network(data, buf, conv_size); - /* Curl_convert_from_network calls failf if unsuccessful */ - /* we might as well continue even if it fails... */ - ptr = buf; /* switch pointer to use my buffer instead */ - break; - default: - /* leave everything else as-is */ - break; - } -#endif /* CURL_DOES_CONVERSIONS */ - if(data->set.fdebug) { Curl_set_in_callback(data, true); rc = (*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); @@ -766,20 +733,11 @@ int Curl_debug(struct Curl_easy *data, curl_infotype type, case CURLINFO_HEADER_IN: fwrite(s_infotype[type], 2, 1, data->set.err); fwrite(ptr, size, 1, data->set.err); -#ifdef CURL_DOES_CONVERSIONS - if(size != conv_size) { - /* we had untranslated data so we need an explicit newline */ - fwrite("\n", 1, 1, data->set.err); - } -#endif break; default: /* nada */ break; } } -#ifdef CURL_DOES_CONVERSIONS - free(buf); -#endif } return rc; } diff --git a/lib/sendf.h b/lib/sendf.h index 108a5e9..6676003 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -45,8 +45,12 @@ void Curl_failf(struct Curl_easy *, const char *fmt, ...); #define failf Curl_failf -#define CLIENTWRITE_BODY (1<<0) -#define CLIENTWRITE_HEADER (1<<1) +#define CLIENTWRITE_BODY (1<<0) +#define CLIENTWRITE_HEADER (1<<1) +#define CLIENTWRITE_STATUS (1<<2) /* the first "header" is the status line */ +#define CLIENTWRITE_CONNECT (1<<3) /* a CONNECT response */ +#define CLIENTWRITE_1XX (1<<4) /* a 1xx response */ +#define CLIENTWRITE_TRAILER (1<<5) /* a trailer header */ #define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, diff --git a/lib/setopt.c b/lib/setopt.c index 599ed5d..0df1afa 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -62,19 +62,12 @@ CURLcode Curl_setstropt(char **charp, const char *s) Curl_safefree(*charp); if(s) { - char *str = strdup(s); + if(strlen(s) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; - if(str) { - size_t len = strlen(str); - if(len > CURL_MAX_INPUT_LENGTH) { - free(str); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - if(!str) + *charp = strdup(s); + if(!*charp) return CURLE_OUT_OF_MEMORY; - - *charp = str; } return CURLE_OK; @@ -162,7 +155,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) char *argptr; CURLcode result = CURLE_OK; long arg; +#ifdef ENABLE_IPV6 unsigned long uarg; +#endif curl_off_t bigsize; switch(option) { @@ -895,7 +890,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) ; else #endif -#if !defined(USE_NGHTTP2) && !defined(USE_HYPER) +#ifndef USE_HTTP2 if(arg >= CURL_HTTP_VERSION_2) return CURLE_UNSUPPORTED_PROTOCOL; #else @@ -1650,24 +1645,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ data->set.seek_client = va_arg(param, void *); break; - case CURLOPT_CONV_FROM_NETWORK_FUNCTION: - /* - * "Convert from network encoding" callback - */ - data->set.convfromnetwork = va_arg(param, curl_conv_callback); - break; - case CURLOPT_CONV_TO_NETWORK_FUNCTION: - /* - * "Convert to network encoding" callback - */ - data->set.convtonetwork = va_arg(param, curl_conv_callback); - break; - case CURLOPT_CONV_FROM_UTF8_FUNCTION: - /* - * "Convert from UTF-8 encoding" callback - */ - data->set.convfromutf8 = va_arg(param, curl_conv_callback); - break; case CURLOPT_IOCTLFUNCTION: /* * I/O control callback. Might be NULL. @@ -2558,6 +2535,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #endif +#ifdef ENABLE_IPV6 case CURLOPT_ADDRESS_SCOPE: /* * Use this scope id when using IPv6 @@ -2571,6 +2549,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif data->set.scope_id = (unsigned int)uarg; break; +#endif case CURLOPT_PROTOCOLS: /* set the bitmask for the protocols that are allowed to be used for the @@ -2623,7 +2602,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_SASL_AUTHZID: - /* Authorisation identity (identity to act as) */ + /* Authorization identity (identity to act as) */ result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], va_arg(param, char *)); break; @@ -2769,30 +2748,30 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype) data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ break; +#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_USERNAME: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], va_arg(param, char *)); -#ifndef CURL_DISABLE_PROXY if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && !data->set.proxy_ssl.authtype) data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ -#endif break; +#endif case CURLOPT_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], va_arg(param, char *)); if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype) data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ break; +#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], va_arg(param, char *)); -#ifndef CURL_DISABLE_PROXY if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && !data->set.proxy_ssl.authtype) data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ -#endif break; +#endif case CURLOPT_TLSAUTH_TYPE: argptr = va_arg(param, char *); if(!argptr || diff --git a/lib/sha256.c b/lib/sha256.c index cf7ea4f..1e879f6 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2017, Florin Petriuc, - * Copyright (C) 2018 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2018 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,7 +40,7 @@ #include -#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) #define USE_OPENSSL_SHA256 #endif @@ -69,8 +69,14 @@ #if defined(USE_OPENSSL_SHA256) -/* When OpenSSL is available we use the SHA256-function from OpenSSL */ +/* When OpenSSL or wolfSSL is available is available we use their + * SHA256-functions. + */ +#if defined(USE_OPENSSL) #include +#elif defined(USE_WOLFSSL) +#include +#endif #include "curl_memory.h" diff --git a/lib/smb.c b/lib/smb.c index fd49cf6..8f44704 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2016 - 2022, Daniel Stenberg, , et al. * Copyright (C) 2014, Bill Nagel , Exacq Technologies * * This software is licensed as described in the file COPYING, which @@ -262,7 +262,7 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) (void) done; /* Check we have a username and password to authenticate with */ - if(!conn->bits.user_passwd) + if(!data->state.aptr.user) return CURLE_LOGIN_DENIED; /* Initialize the connection state */ @@ -299,6 +299,7 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) static CURLcode smb_recv_message(struct Curl_easy *data, void **msg) { struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; char *buf = smbc->recv_buf; ssize_t bytes_read; @@ -307,7 +308,7 @@ static CURLcode smb_recv_message(struct Curl_easy *data, void **msg) size_t len = MAX_MESSAGE_SIZE - smbc->got; CURLcode result; - result = Curl_read(data, FIRSTSOCKET, buf + smbc->got, len, &bytes_read); + result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read); if(result) return result; @@ -377,11 +378,12 @@ static CURLcode smb_send(struct Curl_easy *data, ssize_t len, size_t upload_size) { struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; ssize_t bytes_written; CURLcode result; - result = Curl_write(data, FIRSTSOCKET, data->state.ulbuf, + result = Curl_write(data, sockfd, data->state.ulbuf, len, &bytes_written); if(result) return result; @@ -399,6 +401,7 @@ static CURLcode smb_send(struct Curl_easy *data, ssize_t len, static CURLcode smb_flush(struct Curl_easy *data) { struct connectdata *conn = data->conn; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; ssize_t bytes_written; ssize_t len = smbc->send_size - smbc->sent; @@ -407,7 +410,7 @@ static CURLcode smb_flush(struct Curl_easy *data) if(!smbc->send_size) return CURLE_OK; - result = Curl_write(data, FIRSTSOCKET, + result = Curl_write(data, sockfd, data->state.ulbuf + smbc->sent, len, &bytes_written); if(result) @@ -459,14 +462,10 @@ static CURLcode smb_send_setup(struct Curl_easy *data) if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - Curl_ntlm_core_mk_lm_hash(data, conn->passwd, lm_hash); + Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); -#ifdef USE_NTRESPONSES - Curl_ntlm_core_mk_nt_hash(data, conn->passwd, nt_hash); + Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); -#else - memset(nt, 0, sizeof(nt)); -#endif memset(&msg, 0, sizeof(msg)); msg.word_count = SMB_WC_SETUP_ANDX; @@ -989,7 +988,7 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data, char *slash; /* URL decode the path */ - CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL, + CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); if(result) return result; diff --git a/lib/smtp.c b/lib/smtp.c index 6c08293..c736cfa 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -492,7 +492,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) /* Check we have enough data to authenticate with, and the server supports authentication, and end the connect phase if not */ if(!smtpc->auth_supported || - !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { + !Curl_sasl_can_authenticate(&smtpc->sasl, data)) { state(data, SMTP_STOP); return result; } @@ -505,7 +505,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) state(data, SMTP_AUTH); else { /* Other mechanisms not supported */ - infof(data, "No known authentication mechanisms supported!"); + infof(data, "No known authentication mechanisms supported"); result = CURLE_LOGIN_DENIED; } } @@ -698,7 +698,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) NULL, MIMESTRATEGY_MAIL); if(!result) - if(!Curl_checkheaders(data, "Mime-Version")) + if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) result = Curl_mime_add_header(&data->set.mimepost.curlheaders, "Mime-Version: 1.0"); @@ -1037,7 +1037,7 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode, if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { failf(data, "Command failed: %d", smtpcode); - result = CURLE_RECV_ERROR; + result = CURLE_WEIRD_SERVER_REPLY; } else { /* Temporarily add the LF character back and send as body to the client */ @@ -1182,7 +1182,7 @@ static CURLcode smtp_state_postdata_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ if(smtpcode != 250) - result = CURLE_RECV_ERROR; + result = CURLE_WEIRD_SERVER_REPLY; /* End of DONE phase */ state(data, SMTP_STOP); @@ -1724,8 +1724,7 @@ static CURLcode smtp_parse_url_path(struct Curl_easy *data) } /* URL decode the path and use it as the domain in our EHLO */ - return Curl_urldecode(data, path, 0, &smtpc->domain, NULL, - REJECT_CTRL); + return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); } /*********************************************************************** @@ -1742,7 +1741,7 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data) /* URL decode the custom request */ if(custom) - result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, REJECT_CTRL); + result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); return result; } @@ -1841,7 +1840,7 @@ CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, const ssize_t nread) scratch = newscratch = malloc(2 * data->set.upload_buffer_size); if(!newscratch) { - failf(data, "Failed to alloc scratch buffer!"); + failf(data, "Failed to alloc scratch buffer"); return CURLE_OUT_OF_MEMORY; } diff --git a/lib/socks.c b/lib/socks.c index a014aa6..d614ae5 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -326,7 +326,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, if(proxy_user) { size_t plen = strlen(proxy_user); if(plen >= (size_t)data->set.buffer_size - 8) { - failf(data, "Too long SOCKS proxy user name, can't use!"); + failf(data, "Too long SOCKS proxy user name, can't use"); return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ diff --git a/lib/socks.h b/lib/socks.h index b0c7f9b..f30c610 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,7 +69,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_name, #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* - * This function handles the SOCKS5 GSS-API negotiation and initialisation + * This function handles the SOCKS5 GSS-API negotiation and initialization */ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct Curl_easy *data); diff --git a/lib/strcase.c b/lib/strcase.c index 955e3c7..692a3f1 100644 --- a/lib/strcase.c +++ b/lib/strcase.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,142 +28,25 @@ static char raw_tolower(char in); -/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because - its behavior is altered by the current locale. */ +/* Portable, consistent toupper. Do not use toupper() because its behavior is + altered by the current locale. */ char Curl_raw_toupper(char in) { -#if !defined(CURL_DOES_CONVERSIONS) if(in >= 'a' && in <= 'z') return (char)('A' + in - 'a'); -#else - switch(in) { - case 'a': - return 'A'; - case 'b': - return 'B'; - case 'c': - return 'C'; - case 'd': - return 'D'; - case 'e': - return 'E'; - case 'f': - return 'F'; - case 'g': - return 'G'; - case 'h': - return 'H'; - case 'i': - return 'I'; - case 'j': - return 'J'; - case 'k': - return 'K'; - case 'l': - return 'L'; - case 'm': - return 'M'; - case 'n': - return 'N'; - case 'o': - return 'O'; - case 'p': - return 'P'; - case 'q': - return 'Q'; - case 'r': - return 'R'; - case 's': - return 'S'; - case 't': - return 'T'; - case 'u': - return 'U'; - case 'v': - return 'V'; - case 'w': - return 'W'; - case 'x': - return 'X'; - case 'y': - return 'Y'; - case 'z': - return 'Z'; - } -#endif - return in; } -/* Portable, consistent tolower (remember EBCDIC). Do not use tolower() because - its behavior is altered by the current locale. */ +/* Portable, consistent tolower. Do not use tolower() because its behavior is + altered by the current locale. */ static char raw_tolower(char in) { -#if !defined(CURL_DOES_CONVERSIONS) if(in >= 'A' && in <= 'Z') return (char)('a' + in - 'A'); -#else - switch(in) { - case 'A': - return 'a'; - case 'B': - return 'b'; - case 'C': - return 'c'; - case 'D': - return 'd'; - case 'E': - return 'e'; - case 'F': - return 'f'; - case 'G': - return 'g'; - case 'H': - return 'h'; - case 'I': - return 'i'; - case 'J': - return 'j'; - case 'K': - return 'k'; - case 'L': - return 'l'; - case 'M': - return 'm'; - case 'N': - return 'n'; - case 'O': - return 'o'; - case 'P': - return 'p'; - case 'Q': - return 'q'; - case 'R': - return 'r'; - case 'S': - return 's'; - case 'T': - return 't'; - case 'U': - return 'u'; - case 'V': - return 'v'; - case 'W': - return 'w'; - case 'X': - return 'x'; - case 'Y': - return 'y'; - case 'Z': - return 'z'; - } -#endif - return in; } - /* * Curl_strcasecompare() is for doing "raw" case insensitive strings. This is * meant to be locale independent and only compare strings we know are safe @@ -171,9 +54,6 @@ static char raw_tolower(char in) * https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for some * further explanation to why this function is necessary. * - * The function is capable of comparing a-z case insensitively even for - * non-ascii. - * * @unittest: 1301 */ @@ -251,6 +131,16 @@ void Curl_strntolower(char *dest, const char *src, size_t n) } while(*src++ && --n); } +/* Compare case-sensitive NUL-terminated strings, taking care of possible + * null pointers. Return true if arguments match. + */ +bool Curl_safecmp(char *a, char *b) +{ + if(a && b) + return !strcmp(a, b); + return !a && !b; +} + /* --- public functions --- */ int curl_strequal(const char *first, const char *second) diff --git a/lib/strcase.h b/lib/strcase.h index 10dc698..2635f51 100644 --- a/lib/strcase.h +++ b/lib/strcase.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,8 +28,9 @@ * Only "raw" case insensitive strings. This is meant to be locale independent * and only compare strings we know are safe for this. * - * The function is capable of comparing a-z case insensitively even for - * non-ascii. + * The function is capable of comparing a-z case insensitively. + * + * Result is 1 if text matches and 0 if not. */ #define strcasecompare(a,b) Curl_strcasecompare(a,b) @@ -42,10 +43,12 @@ int Curl_strncasecompare(const char *first, const char *second, size_t max); char Curl_raw_toupper(char in); /* checkprefix() is a shorter version of the above, used when the first - argument is zero-byte terminated */ -#define checkprefix(a,b) curl_strnequal(a,b,strlen(a)) + argument is the string literal */ +#define checkprefix(a,b) curl_strnequal(b, STRCONST(a)) void Curl_strntoupper(char *dest, const char *src, size_t n); void Curl_strntolower(char *dest, const char *src, size_t n); +bool Curl_safecmp(char *a, char *b); + #endif /* HEADER_CURL_STRCASE_H */ diff --git a/lib/strerror.c b/lib/strerror.c index 07d73a7..781e26b 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2004 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2004 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,12 +24,9 @@ #ifdef HAVE_STRERROR_R # if (!defined(HAVE_POSIX_STRERROR_R) && \ - !defined(HAVE_GLIBC_STRERROR_R) && \ - !defined(HAVE_VXWORKS_STRERROR_R)) || \ - (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)) || \ - (defined(HAVE_GLIBC_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)) || \ + !defined(HAVE_GLIBC_STRERROR_R)) || \ (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)) -# error "strerror_r MUST be either POSIX, glibc or vxworks-style" +# error "strerror_r MUST be either POSIX, glibc style" # endif #endif @@ -224,9 +221,6 @@ curl_easy_strerror(CURLcode error) case CURLE_BAD_CONTENT_ENCODING: return "Unrecognized or bad HTTP Content or Transfer-Encoding"; - case CURLE_LDAP_INVALID_URL: - return "Invalid LDAP URL"; - case CURLE_FILESIZE_EXCEEDED: return "Maximum file size exceeded"; @@ -272,9 +266,6 @@ curl_easy_strerror(CURLcode error) case CURLE_CONV_FAILED: return "Conversion failed"; - case CURLE_CONV_REQD: - return "Caller must register CURLOPT_CONV_ callback options"; - case CURLE_REMOTE_FILE_NOT_FOUND: return "Remote file not found"; @@ -337,6 +328,8 @@ curl_easy_strerror(CURLcode error) case CURLE_OBSOLETE50: case CURLE_OBSOLETE51: case CURLE_OBSOLETE57: + case CURLE_OBSOLETE62: + case CURLE_OBSOLETE76: case CURL_LAST: break; } @@ -883,18 +876,6 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) else msnprintf(buf, max, "Unknown error %d", err); } -#elif defined(HAVE_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R) - /* - * The vxworks-style strerror_r() does use the buffer we pass to the function. - * The buffer size should be at least NAME_MAX (256) - */ - { - char buffer[256]; - if(OK == strerror_r(err, buffer)) - strncpy(buf, buffer, max); - else - msnprintf(buf, max, "Unknown error %d", err); - } #else { /* !checksrc! disable STRERROR 1 */ diff --git a/lib/telnet.c b/lib/telnet.c index a81bb81..2abfcd9 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -687,7 +687,7 @@ static void printsub(struct Curl_easy *data, infof(data, "%s", CURL_TELCMD(j)); else infof(data, "%d", j); - infof(data, ", not IAC SE!) "); + infof(data, ", not IAC SE) "); } } length -= 2; @@ -781,7 +781,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data) /* Add the user name as an environment variable if it was given on the command line */ - if(conn->bits.user_passwd) { + if(data->state.aptr.user) { msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user); beg = curl_slist_append(tn->telnet_vars, option_arg); if(!beg) { diff --git a/lib/tftp.c b/lib/tftp.c index f8c6844..7f2c88b 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -327,7 +327,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, infof(data, "got option=(%s) value=(%s)", option, value); - if(checkprefix(option, TFTP_OPTION_BLKSIZE)) { + if(checkprefix(TFTP_OPTION_BLKSIZE, option)) { long blksize; blksize = strtol(value, NULL, 10); @@ -359,7 +359,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK", state->blksize, "requested", state->requested_blksize); } - else if(checkprefix(option, TFTP_OPTION_TSIZE)) { + else if(checkprefix(TFTP_OPTION_TSIZE, option)) { long tsize = 0; tsize = strtol(value, NULL, 10); @@ -463,7 +463,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, /* As RFC3617 describes the separator slash is not actually part of the file name so we skip the always-present first letter of the path string. */ - result = Curl_urldecode(data, &state->data->state.up.path[1], 0, + result = Curl_urldecode(&state->data->state.up.path[1], 0, &filename, NULL, REJECT_ZERO); if(result) return result; diff --git a/lib/timediff.c b/lib/timediff.c new file mode 100644 index 0000000..003477c --- /dev/null +++ b/lib/timediff.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "timediff.h" + +/* + * Converts number of milliseconds into a timeval structure. + * + * Return values: + * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select) + * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select) + * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select) + */ +struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms) +{ + if(!tv) + return NULL; + + if(ms < 0) + return NULL; + + if(ms > 0) { + timediff_t tv_sec = ms / 1000; + timediff_t tv_usec = (ms % 1000) * 1000; /* max=999999 */ +#ifdef HAVE_SUSECONDS_T +#if TIMEDIFF_T_MAX > TIME_T_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > TIME_T_MAX) + tv_sec = TIME_T_MAX; +#endif + tv->tv_sec = (time_t)tv_sec; + tv->tv_usec = (suseconds_t)tv_usec; +#elif defined(WIN32) /* maybe also others in the future */ +#if TIMEDIFF_T_MAX > LONG_MAX + /* tv_sec overflow check on Windows there we know it is long */ + if(tv_sec > LONG_MAX) + tv_sec = LONG_MAX; +#endif + tv->tv_sec = (long)tv_sec; + tv->tv_usec = (long)tv_usec; +#else +#if TIMEDIFF_T_MAX > INT_MAX + /* tv_sec overflow check in case time_t is signed */ + if(tv_sec > INT_MAX) + tv_sec = INT_MAX; +#endif + tv->tv_sec = (int)tv_sec; + tv->tv_usec = (int)tv_usec; +#endif + } + else { + tv->tv_sec = 0; + tv->tv_usec = 0; + } + + return tv; +} + +/* + * Converts a timeval structure into number of milliseconds. + */ +timediff_t curlx_tvtoms(struct timeval *tv) +{ + return (tv->tv_sec*1000) + (timediff_t)(((double)tv->tv_usec)/1000.0); +} diff --git a/lib/timediff.h b/lib/timediff.h new file mode 100644 index 0000000..fcd5f05 --- /dev/null +++ b/lib/timediff.h @@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_TIMEDIFF_H +#define HEADER_CURL_TIMEDIFF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +/* Use a larger type even for 32 bit time_t systems so that we can keep + microsecond accuracy in it */ +typedef curl_off_t timediff_t; +#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T + +#define TIMEDIFF_T_MAX CURL_OFF_T_MAX +#define TIMEDIFF_T_MIN CURL_OFF_T_MIN + +/* + * Converts number of milliseconds into a timeval structure. + * + * Return values: + * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select) + * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select) + * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select) + */ +struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms); + +/* + * Converts a timeval structure into number of milliseconds. + */ +timediff_t curlx_tvtoms(struct timeval *tv); + +#endif /* HEADER_CURL_TIMEDIFF_H */ diff --git a/lib/timeval.h b/lib/timeval.h index 685e729..dce32f4 100644 --- a/lib/timeval.h +++ b/lib/timeval.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,13 +24,7 @@ #include "curl_setup.h" -/* Use a larger type even for 32 bit time_t systems so that we can keep - microsecond accuracy in it */ -typedef curl_off_t timediff_t; -#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T - -#define TIMEDIFF_T_MAX CURL_OFF_T_MAX -#define TIMEDIFF_T_MIN CURL_OFF_T_MIN +#include "timediff.h" struct curltime { time_t tv_sec; /* seconds */ diff --git a/lib/transfer.c b/lib/transfer.c index 22704fa..315da87 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,13 +73,13 @@ #include "select.h" #include "multiif.h" #include "connect.h" -#include "non-ascii.h" #include "http2.h" #include "mime.h" #include "strcase.h" #include "urlapi-int.h" #include "hsts.h" #include "setopt.h" +#include "headers.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -95,10 +95,10 @@ * Returns a pointer to the first matching header or NULL if none matched. */ char *Curl_checkheaders(const struct Curl_easy *data, - const char *thisheader) + const char *thisheader, + const size_t thislen) { struct curl_slist *head; - size_t thislen = strlen(thisheader); DEBUGASSERT(thislen); DEBUGASSERT(thisheader[thislen-1] != ':'); @@ -165,20 +165,6 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, curl_read_callback readfunc = NULL; void *extra_data = NULL; -#ifdef CURL_DOES_CONVERSIONS - bool sending_http_headers = FALSE; - struct connectdata *conn = data->conn; - - if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) { - const struct HTTP *http = data->req.p.http; - - if(http->sending == HTTPSEND_REQUEST) - /* We're sending the HTTP request headers, not the data. - Remember that so we don't re-translate them into garbage. */ - sending_http_headers = TRUE; - } -#endif - #ifndef CURL_DISABLE_HTTP if(data->state.trailers_state == TRAILERS_INITIALIZED) { struct curl_slist *trailers = NULL; @@ -260,7 +246,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, /* protocols that work without network cannot be paused. This is actually only FILE:// just now, and it can't pause since the transfer isn't done using the "normal" procedure. */ - failf(data, "Read callback asked for PAUSE when not supported!"); + failf(data, "Read callback asked for PAUSE when not supported"); return CURLE_READ_ERROR; } @@ -347,26 +333,6 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, } } -#ifdef CURL_DOES_CONVERSIONS - { - CURLcode result; - size_t length; - if(data->state.prefer_ascii) - /* translate the protocol and data */ - length = nread; - else - /* just translate the protocol portion */ - length = hexlen; - if(length) { - result = Curl_convert_to_network(data, data->req.upload_fromhere, - length); - /* Curl_convert_to_network calls failf if unsuccessful */ - if(result) - return result; - } - } -#endif /* CURL_DOES_CONVERSIONS */ - #ifndef CURL_DISABLE_HTTP if(data->state.trailers_state == TRAILERS_SENDING && !trailers_left(data)) { @@ -391,15 +357,6 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, if(added_crlf) nread += strlen(endofline_network); /* for the added end of line */ } -#ifdef CURL_DOES_CONVERSIONS - else if((data->state.prefer_ascii) && (!sending_http_headers)) { - CURLcode result; - result = Curl_convert_to_network(data, data->req.upload_fromhere, nread); - /* Curl_convert_to_network calls failf if unsuccessful */ - if(result) - return result; - } -#endif /* CURL_DOES_CONVERSIONS */ *nreadp = nread; @@ -503,7 +460,7 @@ static int data_pending(const struct Curl_easy *data) /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || -#if defined(USE_NGHTTP2) +#ifdef USE_NGHTTP2 /* For HTTP/2, we may read up everything including response body with header fields in Curl_http_readwrite_headers. If no content-length is provided, curl waits for the connection @@ -586,15 +543,14 @@ static CURLcode readwrite_data(struct Curl_easy *data, if( #ifdef USE_NGHTTP2 - /* For HTTP/2, read data without caring about the content - length. This is safe because body in HTTP/2 is always - segmented thanks to its framing layer. Meanwhile, we have to - call Curl_read to ensure that http2_handle_stream_close is - called when we read all incoming bytes for a particular - stream. */ - !is_http2 && + /* For HTTP/2, read data without caring about the content length. This + is safe because body in HTTP/2 is always segmented thanks to its + framing layer. Meanwhile, we have to call Curl_read to ensure that + http2_handle_stream_close is called when we read all incoming bytes + for a particular stream. */ + !is_http2 && #endif - k->size != -1 && !k->header) { + k->size != -1 && !k->header) { /* make sure we don't read too much */ curl_off_t totalleft = k->size - k->bytecount; if(totalleft < (curl_off_t)bytestoread) @@ -615,7 +571,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, else { /* read nothing but since we wanted nothing we consider this an OK situation to proceed from */ - DEBUGF(infof(data, "readwrite_data: we're done!")); + DEBUGF(infof(data, "readwrite_data: we're done")); nread = 0; } @@ -1039,7 +995,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, if(!data->state.scratch) { data->state.scratch = malloc(2 * data->set.upload_buffer_size); if(!data->state.scratch) { - failf(data, "Failed to alloc scratch buffer!"); + failf(data, "Failed to alloc scratch buffer"); return CURLE_OUT_OF_MEMORY; } @@ -1404,7 +1360,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(!data->state.url && !data->set.uh) { /* we can't do anything without URL */ - failf(data, "No URL set!"); + failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } @@ -1421,7 +1377,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) uc = curl_url_get(data->set.uh, CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); if(uc) { - failf(data, "No URL set!"); + failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } } @@ -1533,6 +1489,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->set.str[STRING_PROXYPASSWORD]); data->req.headerbytecount = 0; + Curl_headers_cleanup(data); return result; } @@ -1577,6 +1534,8 @@ CURLcode Curl_follow(struct Curl_easy *data, DEBUGASSERT(type != FOLLOW_NONE); + if(type != FOLLOW_FAKE) + data->state.requests++; /* count all real follows */ if(type == FOLLOW_REDIR) { if((data->set.maxredirs != -1) && (data->state.followlocation >= data->set.maxredirs)) { @@ -1652,10 +1611,57 @@ CURLcode Curl_follow(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } else { - uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); if(uc) return Curl_uc_to_curlcode(uc); + + /* Clear auth if this redirects to a different port number or protocol, + unless permitted */ + if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { + char *portnum; + int port; + bool clear = FALSE; + + if(data->set.use_port && data->state.allow_port) + /* a custom port is used */ + port = (int)data->set.use_port; + else { + uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, + CURLU_DEFAULT_PORT); + if(uc) { + free(newurl); + return Curl_uc_to_curlcode(uc); + } + port = atoi(portnum); + free(portnum); + } + if(port != data->info.conn_remote_port) { + infof(data, "Clear auth, redirects to port from %u to %u", + data->info.conn_remote_port, port); + clear = TRUE; + } + else { + char *scheme; + const struct Curl_handler *p; + uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); + if(uc) { + free(newurl); + return Curl_uc_to_curlcode(uc); + } + + p = Curl_builtin_scheme(scheme); + if(p && (p->protocol != data->info.conn_protocol)) { + infof(data, "Clear auth, redirects scheme from %s to %s", + data->info.conn_scheme, scheme); + clear = TRUE; + } + free(scheme); + } + if(clear) { + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } } if(type == FOLLOW_FAKE) { diff --git a/lib/transfer.h b/lib/transfer.h index 0fa3d55..56d2fd1 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,7 +24,8 @@ #define Curl_headersep(x) ((((x)==':') || ((x)==';'))) char *Curl_checkheaders(const struct Curl_easy *data, - const char *thisheader); + const char *thisheader, + const size_t thislen); void Curl_init_CONNECT(struct Curl_easy *data); diff --git a/lib/url.c b/lib/url.c index 9f10135..ef48ed6 100644 --- a/lib/url.c +++ b/lib/url.c @@ -99,7 +99,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "easyif.h" #include "speedcheck.h" #include "warnless.h" -#include "non-ascii.h" #include "getinfo.h" #include "urlapi-int.h" #include "system_win32.h" @@ -131,21 +130,13 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "setopt.h" #include "altsvc.h" #include "dynbuf.h" +#include "headers.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #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 @@ -445,7 +436,6 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_resolver_cleanup(data->state.async.resolver); Curl_http2_cleanup_dependencies(data); - Curl_convert_close(data); /* No longer a dirty share, if it exists */ if(data->share) { @@ -481,6 +471,7 @@ CURLcode Curl_close(struct Curl_easy **datap) /* destruct wildcard structures if it is needed */ Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); + Curl_headers_cleanup(data); free(data); return CURLE_OK; } @@ -509,11 +500,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->seek_func = ZERO_NULL; set->seek_client = ZERO_NULL; - /* conversion callbacks for non-ASCII hosts */ - set->convfromnetwork = ZERO_NULL; - set->convtonetwork = ZERO_NULL; - set->convfromutf8 = ZERO_NULL; - set->filesize = -1; /* we don't know the size */ set->postfieldsize = -1; /* unknown size */ set->maxredirs = -1; /* allow any amount by default */ @@ -634,7 +620,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->maxlifetime_conn = 0; set->http09_allowed = FALSE; set->httpwant = -#ifdef USE_NGHTTP2 +#ifdef USE_HTTP2 CURL_HTTP_VERSION_2TLS #else CURL_HTTP_VERSION_1_1 @@ -677,7 +663,6 @@ CURLcode Curl_open(struct Curl_easy **curl) result = Curl_init_userdefined(data); if(!result) { Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); - Curl_convert_init(data); Curl_initinfo(data); /* most recent connection is not yet defined */ @@ -760,7 +745,9 @@ static void conn_shutdown(struct Curl_easy *data, struct connectdata *conn) /* close the SSL stuff before we close any sockets since they will/may write to the sockets */ Curl_ssl_close(data, conn, FIRSTSOCKET); +#ifndef CURL_DISABLE_FTP Curl_ssl_close(data, conn, SECONDARYSOCKET); +#endif /* close possibly still open sockets */ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) @@ -794,6 +781,7 @@ static void conn_free(struct connectdata *conn) Curl_safefree(conn->passwd); Curl_safefree(conn->sasl_authzid); Curl_safefree(conn->options); + Curl_safefree(conn->oauth_bearer); Curl_dyn_free(&conn->trailer); Curl_safefree(conn->host.rawalloc); /* host name buffer */ Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ @@ -830,8 +818,8 @@ static void conn_free(struct connectdata *conn) * */ -CURLcode Curl_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) +void Curl_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) { /* there must be a connection to close */ DEBUGASSERT(conn); @@ -851,7 +839,7 @@ CURLcode Curl_disconnect(struct Curl_easy *data, */ if(CONN_INUSE(conn) && !dead_connection) { DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); - return CURLE_OK; + return; } if(conn->dns_entry) { @@ -883,7 +871,6 @@ CURLcode Curl_disconnect(struct Curl_easy *data, Curl_detach_connnection(data); conn_free(conn); - return CURLE_OK; } /* @@ -949,7 +936,7 @@ socks_proxy_info_matches(const struct proxy_info *data, /* the user information is case-sensitive or at least it is not defined as case-insensitive - see https://tools.ietf.org/html/rfc3986#section-3.2.1 */ + see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */ if(!data->user != !needle->user) return FALSE; /* curl_strequal does a case insentive comparison, so do not use it here! */ @@ -1047,7 +1034,7 @@ static bool extract_if_dead(struct connectdata *conn, } if(dead) { - infof(data, "Connection %ld seems to be dead!", conn->connection_id); + infof(data, "Connection %ld seems to be dead", conn->connection_id); Curl_conncache_remove_conn(data, conn, FALSE); return TRUE; } @@ -1105,7 +1092,7 @@ static void prune_dead_connections(struct Curl_easy *data) Curl_conncache_remove_conn(data, prune.extracted, TRUE); /* disconnect it */ - (void)Curl_disconnect(data, prune.extracted, TRUE); + Curl_disconnect(data, prune.extracted, TRUE); } CONNCACHE_LOCK(data); data->state.conn_cache->last_cleanup = now; @@ -1136,7 +1123,6 @@ ConnectionExists(struct Curl_easy *data, bool foundPendingCandidate = FALSE; bool canmultiplex = IsMultiplexingPossible(data, needle); struct connectbundle *bundle; - const char *hostbundle; #ifdef USE_NTLM bool wantNTLMhttp = ((data->state.authhost.want & @@ -1157,15 +1143,14 @@ ConnectionExists(struct Curl_easy *data, /* Look up the bundle with all the connections to this particular host. Locks the connection cache, beware of early returns! */ - bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache, - &hostbundle); + bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache); if(bundle) { /* Max pipe length is zero (unlimited) for multiplexed connections */ struct Curl_llist_element *curr; - infof(data, "Found bundle for host %s: %p [%s]", - hostbundle, (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? - "can multiplex" : "serially")); + infof(data, "Found bundle for host: %p [%s]", + (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially")); /* We can't multiplex if we don't know anything about the server */ if(canmultiplex) { @@ -1182,11 +1167,11 @@ ConnectionExists(struct Curl_easy *data, } if((bundle->multiuse == BUNDLE_MULTIPLEX) && !Curl_multiplex_wanted(data->multi)) { - infof(data, "Could multiplex, but not asked to!"); + infof(data, "Could multiplex, but not asked to"); canmultiplex = FALSE; } if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { - infof(data, "Can not multiplex, even if we wanted to!"); + infof(data, "Can not multiplex, even if we wanted to"); canmultiplex = FALSE; } } @@ -1209,7 +1194,7 @@ ConnectionExists(struct Curl_easy *data, if(extract_if_dead(check, data)) { /* disconnect it */ - (void)Curl_disconnect(data, check, TRUE); + Curl_disconnect(data, check, TRUE); continue; } @@ -1356,7 +1341,9 @@ ConnectionExists(struct Curl_easy *data, /* This protocol requires credentials per connection, so verify that we're using the same name and password as well */ if(strcmp(needle->user, check->user) || - strcmp(needle->passwd, check->passwd)) { + strcmp(needle->passwd, check->passwd) || + !Curl_safecmp(needle->sasl_authzid, check->sasl_authzid) || + !Curl_safecmp(needle->oauth_bearer, check->oauth_bearer)) { /* one of them was different */ continue; } @@ -1507,7 +1494,7 @@ ConnectionExists(struct Curl_easy *data, #endif /* When not multiplexed, we have a match here! */ chosen = check; - infof(data, "Multiplexed connection found!"); + infof(data, "Multiplexed connection found"); break; } else { @@ -1575,20 +1562,6 @@ bool Curl_is_ASCII_name(const char *hostname) } /* - * Strip single trailing dot in the hostname, - * primarily for SNI and http host header. - */ -static void strip_trailing_dot(struct hostname *host) -{ - size_t len; - if(!host || !host->name) - return; - len = strlen(host->name); - if(len && (host->name[len-1] == '.')) - host->name[len-1] = 0; -} - -/* * Perform any necessary IDN conversion of hostname */ CURLcode Curl_idnconvert_hostname(struct Curl_easy *data, @@ -1690,18 +1663,35 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) Note that these backend pointers can be swapped by vtls (eg ssl backend data becomes proxy backend data). */ { - size_t sslsize = Curl_ssl->sizeof_ssl_backend_data; - char *ssl = calloc(SSL_BACKEND_CNT, sslsize); + size_t onesize = Curl_ssl->sizeof_ssl_backend_data; + size_t totalsize = onesize; + char *ssl; + +#ifndef CURL_DISABLE_FTP + totalsize *= 2; +#endif +#ifndef CURL_DISABLE_PROXY + totalsize *= 2; +#endif + + ssl = calloc(1, totalsize); if(!ssl) { free(conn); return NULL; } conn->ssl_extra = ssl; - conn->ssl[0].backend = (void *)ssl; - conn->ssl[1].backend = (void *)(ssl + sslsize); + conn->ssl[FIRSTSOCKET].backend = (void *)ssl; +#ifndef CURL_DISABLE_FTP + ssl += onesize; + conn->ssl[SECONDARYSOCKET].backend = (void *)ssl; +#endif #ifndef CURL_DISABLE_PROXY - conn->proxy_ssl[0].backend = (void *)(ssl + 2 * sslsize); - conn->proxy_ssl[1].backend = (void *)(ssl + 3 * sslsize); + ssl += onesize; + conn->proxy_ssl[FIRSTSOCKET].backend = (void *)ssl; +#ifndef CURL_DISABLE_FTP + ssl += onesize; + conn->proxy_ssl[SECONDARYSOCKET].backend = (void *)ssl; +#endif #endif } #endif @@ -1761,7 +1751,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; #endif /* CURL_DISABLE_PROXY */ - conn->bits.user_passwd = (data->state.aptr.user) ? TRUE : FALSE; #ifndef CURL_DISABLE_FTP conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; @@ -1882,6 +1871,7 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) } } +#ifdef ENABLE_IPV6 /* * If the URL was set with an IPv6 numerical address with a zone id part, set * the scope_id based on that! @@ -1931,6 +1921,9 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, free(zoneid); } } +#else +#define zonefrom_url(a,b,c) Curl_nop_stmt +#endif /* * Parse URL and fill in the relevant members of the connection struct. @@ -2038,45 +2031,47 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, * User name and password set with their own options override the * credentials possibly set in the URL. */ - if(!data->state.aptr.user) { - /* we don't use the URL API's URL decoder option here since it rejects - control codes and we want to allow them for some schemes in the user - and password fields */ - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + if(!data->state.aptr.passwd) { + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); if(!uc) { char *decoded; - result = Curl_urldecode(NULL, data->state.up.user, 0, &decoded, NULL, + result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, conn->handler->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); if(result) return result; - conn->user = decoded; - conn->bits.user_passwd = TRUE; - result = Curl_setstropt(&data->state.aptr.user, decoded); + conn->passwd = decoded; + result = Curl_setstropt(&data->state.aptr.passwd, decoded); if(result) return result; } - else if(uc != CURLUE_NO_USER) + else if(uc != CURLUE_NO_PASSWORD) return Curl_uc_to_curlcode(uc); } - if(!data->state.aptr.passwd) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(!data->state.aptr.user) { + /* we don't use the URL API's URL decoder option here since it rejects + control codes and we want to allow them for some schemes in the user + and password fields */ + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); if(!uc) { char *decoded; - result = Curl_urldecode(NULL, data->state.up.password, 0, &decoded, NULL, + result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, conn->handler->flags&PROTOPT_USERPWDCTRL ? REJECT_ZERO : REJECT_CTRL); if(result) return result; - conn->passwd = decoded; - conn->bits.user_passwd = TRUE; - result = Curl_setstropt(&data->state.aptr.passwd, decoded); - if(result) - return result; + conn->user = decoded; + result = Curl_setstropt(&data->state.aptr.user, decoded); } - else if(uc != CURLUE_NO_PASSWORD) + else if(uc != CURLUE_NO_USER) return Curl_uc_to_curlcode(uc); + else if(data->state.aptr.passwd) { + /* no user was set but a password, set a blank user */ + result = Curl_setstropt(&data->state.aptr.user, ""); + } + if(result) + return result; } uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, @@ -2128,9 +2123,11 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; conn->host.name = conn->host.rawalloc; +#ifdef ENABLE_IPV6 if(data->set.scope_id) /* Override any scope that was set above. */ conn->scope_id = data->set.scope_id; +#endif return CURLE_OK; } @@ -2560,14 +2557,14 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data, CURLcode result = CURLE_OK; if(proxyuser) { - result = Curl_urldecode(data, proxyuser, 0, &conn->http_proxy.user, NULL, + result = Curl_urldecode(proxyuser, 0, &conn->http_proxy.user, NULL, REJECT_ZERO); if(!result) result = Curl_setstropt(&data->state.aptr.proxyuser, conn->http_proxy.user); } if(!result && proxypasswd) { - result = Curl_urldecode(data, proxypasswd, 0, &conn->http_proxy.passwd, + result = Curl_urldecode(proxypasswd, 0, &conn->http_proxy.passwd, NULL, REJECT_ZERO); if(!result) result = Curl_setstropt(&data->state.aptr.proxypasswd, @@ -2922,10 +2919,10 @@ static CURLcode override_login(struct Curl_easy *data, char **optionsp = &conn->options; #ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc == CURL_NETRC_REQUIRED && conn->bits.user_passwd) { + if(data->set.use_netrc == CURL_NETRC_REQUIRED && data->state.aptr.user) { Curl_safefree(*userp); Curl_safefree(*passwdp); - conn->bits.user_passwd = FALSE; /* disable user+password */ + Curl_safefree(data->state.aptr.user); /* disable user+password */ } #endif @@ -2942,6 +2939,13 @@ static CURLcode override_login(struct Curl_easy *data, bool netrc_user_changed = FALSE; bool netrc_passwd_changed = FALSE; int ret; + bool url_provided = FALSE; + + if(data->state.up.user) { + /* there was a user name in the URL */ + userp = &data->state.up.user; + url_provided = TRUE; + } ret = Curl_parsenetrc(conn->host.name, userp, passwdp, @@ -2959,29 +2963,37 @@ static CURLcode override_login(struct Curl_easy *data, file, so that it is safe to use even if we followed a Location: to a different host or similar. */ conn->bits.netrc = TRUE; - conn->bits.user_passwd = TRUE; /* enable user+password */ + } + if(url_provided) { + Curl_safefree(conn->user); + conn->user = strdup(*userp); + if(!conn->user) + return CURLE_OUT_OF_MEMORY; + /* don't update the user name below */ + userp = NULL; } } #endif /* for updated strings, we update them in the URL */ - if(*userp) { - CURLcode result = Curl_setstropt(&data->state.aptr.user, *userp); - if(result) - return result; - } - if(data->state.aptr.user) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, - CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*userp) { - *userp = strdup(data->state.aptr.user); - if(!*userp) - return CURLE_OUT_OF_MEMORY; + if(userp) { + if(*userp) { + CURLcode result = Curl_setstropt(&data->state.aptr.user, *userp); + if(result) + return result; + } + if(data->state.aptr.user) { + uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, + CURLU_URLENCODE); + if(uc) + return Curl_uc_to_curlcode(uc); + if(!*userp) { + *userp = strdup(data->state.aptr.user); + if(!*userp) + return CURLE_OUT_OF_MEMORY; + } } } - if(*passwdp) { CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); if(result) @@ -3005,14 +3017,15 @@ static CURLcode override_login(struct Curl_easy *data, /* * Set the login details so they're available in the connection */ -static CURLcode set_login(struct connectdata *conn) +static CURLcode set_login(struct Curl_easy *data, + struct connectdata *conn) { CURLcode result = CURLE_OK; const char *setuser = CURL_DEFAULT_USER; const char *setpasswd = CURL_DEFAULT_PASSWORD; /* If our protocol needs a password and we have none, use the defaults */ - if((conn->handler->flags & PROTOPT_NEEDSPWD) && !conn->bits.user_passwd) + if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) ; else { setuser = ""; @@ -3098,7 +3111,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, * name nor a numeric can legally start with a bracket. */ #else - failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in!"); + failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); result = CURLE_NOT_BUILT_IN; goto error; #endif @@ -3269,16 +3282,16 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, bool hit; struct altsvc *as; const int allowed_versions = ( ALPN_h1 -#ifdef USE_NGHTTP2 - | ALPN_h2 +#ifdef USE_HTTP2 + | ALPN_h2 #endif #ifdef ENABLE_QUIC - | ALPN_h3 + | ALPN_h3 #endif ) & data->asi->flags; host = conn->host.rawalloc; -#ifdef USE_NGHTTP2 +#ifdef USE_HTTP2 /* with h2 support, check that first */ srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, @@ -3389,7 +3402,7 @@ static CURLcode resolve_server(struct Curl_easy *data, else #endif - if(!conn->bits.proxy) { + if(!CONN_IS_PROXIED(conn)) { struct hostname *connhost; if(conn->bits.conn_to_host) connhost = &conn->conn_to_host; @@ -3486,8 +3499,7 @@ static void reuse_conn(struct Curl_easy *data, /* get the user+password information from the old_conn struct since it may * be new for this request even when we re-use an existing connection */ - conn->bits.user_passwd = old_conn->bits.user_passwd; - if(conn->bits.user_passwd) { + if(old_conn->user) { /* use the new user name and password though */ Curl_safefree(conn->user); Curl_safefree(conn->passwd); @@ -3626,6 +3638,14 @@ static CURLcode create_conn(struct Curl_easy *data, } } + if(data->set.str[STRING_BEARER]) { + conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); + if(!conn->oauth_bearer) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + #ifdef USE_UNIX_SOCKETS if(data->set.str[STRING_UNIX_SOCKET_PATH]) { conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); @@ -3665,7 +3685,7 @@ static CURLcode create_conn(struct Curl_easy *data, if(result) goto out; - result = set_login(conn); /* default credentials */ + result = set_login(data, conn); /* default credentials */ if(result) goto out; @@ -3902,14 +3922,14 @@ static CURLcode create_conn(struct Curl_easy *data, *in_connect = conn; #ifndef CURL_DISABLE_PROXY - infof(data, "Re-using existing connection! (#%ld) with %s %s", + infof(data, "Re-using existing connection #%ld with %s %s", conn->connection_id, conn->bits.proxy?"proxy":"host", conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : conn->http_proxy.host.name ? conn->http_proxy.host.dispname : conn->host.dispname); #else - infof(data, "Re-using existing connection! (#%ld) with host %s", + infof(data, "Re-using existing connection #%ld with host %s", conn->connection_id, conn->host.dispname); #endif } @@ -3933,10 +3953,8 @@ static CURLcode create_conn(struct Curl_easy *data, connections_available = FALSE; else { /* this gets a lock on the conncache */ - const char *bundlehost; struct connectbundle *bundle = - Curl_conncache_find_bundle(data, conn, data->state.conn_cache, - &bundlehost); + Curl_conncache_find_bundle(data, conn, data->state.conn_cache); if(max_host_connections > 0 && bundle && (bundle->num_connections >= max_host_connections)) { @@ -3947,10 +3965,10 @@ static CURLcode create_conn(struct Curl_easy *data, CONNCACHE_UNLOCK(data); if(conn_candidate) - (void)Curl_disconnect(data, conn_candidate, FALSE); + Curl_disconnect(data, conn_candidate, FALSE); else { - infof(data, "No more connections allowed to host %s: %zu", - bundlehost, max_host_connections); + infof(data, "No more connections allowed to host: %zu", + max_host_connections); connections_available = FALSE; } } @@ -3967,7 +3985,7 @@ static CURLcode create_conn(struct Curl_easy *data, /* The cache is full. Let's see if we can kill a connection. */ conn_candidate = Curl_conncache_extract_oldest(data); if(conn_candidate) - (void)Curl_disconnect(data, conn_candidate, FALSE); + Curl_disconnect(data, conn_candidate, FALSE); else { infof(data, "No connections available in cache"); connections_available = FALSE; @@ -4000,14 +4018,14 @@ static CURLcode create_conn(struct Curl_easy *data, connection based. */ if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && data->state.authhost.done) { - infof(data, "NTLM picked AND auth done set, clear picked!"); + infof(data, "NTLM picked AND auth done set, clear picked"); data->state.authhost.picked = CURLAUTH_NONE; data->state.authhost.done = FALSE; } if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && data->state.authproxy.done) { - infof(data, "NTLM-proxy picked AND auth done set, clear picked!"); + infof(data, "NTLM-proxy picked AND auth done set, clear picked"); data->state.authproxy.picked = CURLAUTH_NONE; data->state.authproxy.done = FALSE; } @@ -4038,17 +4056,6 @@ static CURLcode create_conn(struct Curl_easy *data, *************************************************************/ result = resolve_server(data, conn, async); - /* Strip trailing dots. resolve_server copied the name. */ - strip_trailing_dot(&conn->host); -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) - strip_trailing_dot(&conn->http_proxy.host); - if(conn->bits.socksproxy) - strip_trailing_dot(&conn->socks_proxy.host); -#endif - if(conn->bits.conn_to_host) - strip_trailing_dot(&conn->conn_to_host); - out: return result; } diff --git a/lib/url.h b/lib/url.h index 929fc60..59a1c24 100644 --- a/lib/url.h +++ b/lib/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,8 +35,8 @@ void Curl_freeset(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); -CURLcode Curl_disconnect(struct Curl_easy *data, - struct connectdata *, bool dead_connection); +void Curl_disconnect(struct Curl_easy *data, + struct connectdata *, bool dead_connection); CURLcode Curl_setup_conn(struct Curl_easy *data, bool *protocol_done); void Curl_free_request_state(struct Curl_easy *data); diff --git a/lib/urlapi.c b/lib/urlapi.c index d29aeb2..99a0f69 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -90,16 +90,6 @@ static void free_urlhandle(struct Curl_URL *u) free(u->temppath); } -/* move the full contents of one handle onto another and - free the original */ -static void mv_urlhandle(struct Curl_URL *from, - struct Curl_URL *to) -{ - free_urlhandle(to); - *to = *from; - free(from); -} - /* * Find the separator at the end of the host name, or the '?' in cases like * http://www.url.com?id=2380 @@ -804,8 +794,7 @@ static CURLUcode decode_host(char *hostname, char **outp) else { /* might be encoded */ size_t dlen; - CURLcode result = Curl_urldecode(NULL, hostname, 0, - outp, &dlen, REJECT_CTRL); + CURLcode result = Curl_urldecode(hostname, 0, outp, &dlen, REJECT_CTRL); if(result) return CURLUE_BAD_HOSTNAME; } @@ -1005,9 +994,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) return CURLUE_NO_HOST; } - len = strlen(p); - memcpy(path, p, len); - path[len] = 0; + strcpy(path, p); if(schemep) { u->scheme = strdup(schemep); @@ -1157,6 +1144,25 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } /* + * Parse the URL and, if successful, replace everything in the Curl_URL struct. + */ +static CURLUcode parseurl_and_replace(const char *url, CURLU *u, + unsigned int flags) +{ + CURLUcode result; + CURLU tmpurl; + memset(&tmpurl, 0, sizeof(tmpurl)); + result = parseurl(url, &tmpurl, flags); + if(!result) { + free_urlhandle(u); + *u = tmpurl; + } + else + free_urlhandle(&tmpurl); + return result; +} + +/* */ CURLU *curl_url(void) { @@ -1422,8 +1428,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, size_t dlen; /* this unconditional rejection of control bytes is documented API behavior */ - CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, - REJECT_CTRL); + CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL); free(*part); if(res) { *part = NULL; @@ -1564,52 +1569,24 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, CURLUcode result; char *oldurl; char *redired_url; - CURLU *handle2; - if(Curl_is_absolute_url(part, NULL, 0)) { - handle2 = curl_url(); - if(!handle2) - return CURLUE_OUT_OF_MEMORY; - result = parseurl(part, handle2, flags); - if(!result) - mv_urlhandle(handle2, u); - else - curl_url_cleanup(handle2); - return result; - } - /* extract the full "old" URL to do the redirect on */ - result = curl_url_get(u, CURLUPART_URL, &oldurl, flags); - if(result) { - /* couldn't get the old URL, just use the new! */ - handle2 = curl_url(); - if(!handle2) - return CURLUE_OUT_OF_MEMORY; - result = parseurl(part, handle2, flags); - if(!result) - mv_urlhandle(handle2, u); - else - curl_url_cleanup(handle2); - return result; + /* if the new thing is absolute or the old one is not + * (we could not get an absolute url in 'oldurl'), + * then replace the existing with the new. */ + if(Curl_is_absolute_url(part, NULL, 0) + || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) { + return parseurl_and_replace(part, u, flags); } - /* apply the relative part to create a new URL */ + /* apply the relative part to create a new URL + * and replace the existing one with it. */ redired_url = concat_url(oldurl, part); free(oldurl); if(!redired_url) return CURLUE_OUT_OF_MEMORY; - /* now parse the new URL */ - handle2 = curl_url(); - if(!handle2) { - free(redired_url); - return CURLUE_OUT_OF_MEMORY; - } - result = parseurl(redired_url, handle2, flags); + result = parseurl_and_replace(redired_url, u, flags); free(redired_url); - if(!result) - mv_urlhandle(handle2, u); - else - curl_url_cleanup(handle2); return result; } default: diff --git a/lib/urldata.h b/lib/urldata.h index cc9c888..9c34ec4 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -352,10 +352,6 @@ typedef enum { GSS_AUTHSUCC } curlnegotiate; -#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) -#include -#endif - /* Struct used for GSSAPI (Kerberos V5) authentication */ #if defined(USE_KERBEROS5) struct kerberos5data { @@ -450,6 +446,11 @@ struct negotiatedata { }; #endif +#ifdef CURL_DISABLE_PROXY +#define CONN_IS_PROXIED(x) 0 +#else +#define CONN_IS_PROXIED(x) x->bits.proxy +#endif /* * Boolean values that concerns this connection. @@ -470,6 +471,7 @@ struct ConnectBits { BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection in a CONNECT request with auth, so that libcurl should reconnect and continue. */ + BIT(proxy); /* if set, this transfer is done through a proxy - any type */ #endif /* always modify bits.close with the connclose() and connkeep() macros! */ BIT(close); /* if set, we close the connection after this request */ @@ -479,8 +481,6 @@ struct ConnectBits { that overrides the host in the URL */ BIT(conn_to_port); /* if set, this connection has a "connect to port" that overrides the port in the URL (remote port) */ - BIT(proxy); /* if set, this transfer is done through a proxy - any type */ - BIT(user_passwd); /* do we use user+password for this connection? */ BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6 IP address */ BIT(ipv6); /* we communicate with a site using an IPv6 address */ @@ -939,8 +939,9 @@ struct connectdata { cache entry remains locked. It gets unlocked in multi_done() */ struct Curl_addrinfo *ip_addr; struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */ - +#ifdef ENABLE_IPV6 unsigned int scope_id; /* Scope id for IPv6 */ +#endif enum { TRNSPRT_TCP = 3, @@ -982,7 +983,8 @@ struct connectdata { char *user; /* user name string, allocated */ char *passwd; /* password string, allocated */ char *options; /* options string, allocated */ - char *sasl_authzid; /* authorisation identity string, allocated */ + char *sasl_authzid; /* authorization identity string, allocated */ + char *oauth_bearer; /* OAUTH2 bearer, allocated */ unsigned char httpversion; /* the HTTP version*10 reported by the server */ struct curltime now; /* "current" time */ struct curltime created; /* creation time */ @@ -1158,7 +1160,11 @@ struct PureInfo { reused, in the connection cache. */ char conn_primary_ip[MAX_IPADR_LEN]; - int conn_primary_port; + int conn_primary_port; /* this is the destination port to the connection, + which might have been a proxy */ + int conn_remote_port; /* this is the "remote port", which is the port + number of the used URL, independent of proxy or + not */ char conn_local_ip[MAX_IPADR_LEN]; int conn_local_port; const char *conn_scheme; @@ -1327,14 +1333,16 @@ struct UrlState { char *ulbuf; /* allocated upload buffer or NULL */ curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - char *first_host; /* host name of the first (not followed) request. - if set, this should be the host name that we will - sent authorization to, no else. Used to make Location: - following not keep sending user+password... This is - strdup() data. - */ + + /* host name, port number and protocol of the first (not followed) request. + if set, this should be the host name that we will sent authorization to, + no else. Used to make Location: following not keep sending user+password. + This is strdup()ed data. */ + char *first_host; + int first_remote_port; + unsigned int first_remote_protocol; + int retrycount; /* number of retries on a new connection */ - int first_remote_port; /* remote port of the first (not followed) request */ struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */ long sessionage; /* number of the most recent session */ struct tempbuf tempwrite[3]; /* BOTH, HEADER, BODY */ @@ -1342,6 +1350,7 @@ struct UrlState { int os_errno; /* filled in with errno whenever an error occurs */ char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */ long followlocation; /* redirect counter */ + int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ void (*prev_signal)(int sig); @@ -1413,6 +1422,8 @@ struct UrlState { size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing headers */ + struct Curl_llist httphdrs; /* received headers */ + struct curl_header headerout; /* for external purposes */ #endif trailers_state trailers_state; /* whether we are sending trailers and what stage are we at */ @@ -1659,13 +1670,6 @@ struct UserDefined { 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 */ - /* function to convert from the network encoding: */ - curl_conv_callback convfromnetwork; - /* function to convert to the network encoding: */ - curl_conv_callback convtonetwork; - /* function to convert from UTF-8 encoding: */ - curl_conv_callback convfromutf8; #ifndef CURL_DISABLE_HSTS curl_hstsread_callback hsts_read; void *hsts_read_userp; @@ -1749,7 +1753,9 @@ struct UserDefined { long ssh_auth_types; /* allowed SSH auth types */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; +#ifdef ENABLE_IPV6 unsigned int scope_id; /* Scope id for IPv6 */ +#endif long allowed_protocols; long redir_protocols; long mime_options; /* Mime option flags. */ @@ -1949,11 +1955,6 @@ struct Curl_easy { struct PureInfo info; /* stats, reports and info data */ struct curl_tlssessioninfo tsi; /* Information about the TLS session, only valid after a client has asked for it */ -#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) - iconv_t outbound_cd; /* for translating to the network encoding */ - iconv_t inbound_cd; /* for translating from the network encoding */ - iconv_t utf8_cd; /* for translating to UTF8 */ -#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */ #ifdef USE_HYPER struct hyptransfer hyp; #endif diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index d8aac66..d461609 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,7 +40,6 @@ #include "warnless.h" #include "strtok.h" #include "strcase.h" -#include "non-ascii.h" /* included for Curl_convert_... prototypes */ #include "curl_printf.h" #include "rand.h" @@ -56,20 +55,7 @@ #define DIGEST_QOP_VALUE_STRING_AUTH "auth" #define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int" #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf" - -/* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines. - It converts digest text to ASCII so the MD5 will be correct for - what ultimately goes over the network. -*/ -#define CURL_OUTPUT_DIGEST_CONV(a, b) \ - do { \ - result = Curl_convert_to_network(a, b, strlen(b)); \ - if(result) { \ - free(b); \ - return result; \ - } \ - } while(0) -#endif /* !USE_WINDOWS_SSPI */ +#endif bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, const char **endptr) @@ -692,7 +678,7 @@ static CURLcode auth_create_digest_http_message( if(result) return result; - result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), + result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), &cnonce, &cnonce_sz); if(result) return result; @@ -705,7 +691,6 @@ static CURLcode auth_create_digest_http_message( if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, hashthis); hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, (unsigned char *)userh); @@ -726,7 +711,6 @@ static CURLcode auth_create_digest_http_message( if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, ha1); @@ -739,7 +723,6 @@ static CURLcode auth_create_digest_http_message( if(!tmp) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */ hash(hashbuf, (unsigned char *) tmp, strlen(tmp)); free(tmp); convert_to_ascii(hashbuf, ha1); @@ -778,7 +761,6 @@ static CURLcode auth_create_digest_http_message( if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, ha2); @@ -794,7 +776,6 @@ static CURLcode auth_create_digest_http_message( if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); convert_to_ascii(hashbuf, request_digest); diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 04f6590..115f70b 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,7 +34,6 @@ #define DEBUG_ME 0 #include "urldata.h" -#include "non-ascii.h" #include "sendf.h" #include "curl_ntlm_core.h" #include "curl_gethostname.h" @@ -383,12 +382,6 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); -#if defined(USE_NTRESPONSES) && \ - (defined(USE_NTLM2SESSION) || defined(USE_NTLM_V2)) -#define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY -#else -#define NTLM2FLAG 0 -#endif ntlmbuf = aprintf(NTLMSSP_SIGNATURE "%c" "\x01%c%c%c" /* 32-bit type = 1 */ "%c%c%c%c" /* 32-bit NTLM flag field */ @@ -408,7 +401,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | NTLMFLAG_REQUEST_TARGET | NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLM2FLAG | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), SHORTPAIR(domlen), SHORTPAIR(domlen), @@ -433,18 +426,18 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | NTLMFLAG_REQUEST_TARGET | NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLM2FLAG | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), NTLMFLAG_NEGOTIATE_OEM | NTLMFLAG_REQUEST_TARGET | NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLM2FLAG | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); ntlm_print_flags(stderr, NTLMFLAG_NEGOTIATE_OEM | NTLMFLAG_REQUEST_TARGET | NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLM2FLAG | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); fprintf(stderr, "\n****\n"); }); @@ -498,13 +491,11 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, unsigned char ntlmbuf[NTLM_BUFSIZE]; int lmrespoff; unsigned char lmresp[24]; /* fixed-size */ -#ifdef USE_NTRESPONSES int ntrespoff; unsigned int ntresplen = 24; unsigned char ntresp[24]; /* fixed-size */ unsigned char *ptr_ntresp = &ntresp[0]; unsigned char *ntlmv2resp = NULL; -#endif bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE; char host[HOSTNAME_MAX + 1] = ""; const char *user; @@ -533,19 +524,14 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* Get the machine's un-qualified host name as NTLM doesn't like the fully qualified domain name */ if(Curl_gethostname(host, sizeof(host))) { - infof(data, "gethostname() failed, continuing without!"); + infof(data, "gethostname() failed, continuing without"); hostlen = 0; } else { hostlen = strlen(host); } -#if defined(USE_NTRESPONSES) && \ - (defined(USE_NTLM2SESSION) || defined(USE_NTLM_V2)) - /* We don't support NTLM2 or extended security if we don't have - USE_NTRESPONSES */ if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { -# if defined(USE_NTLM_V2) unsigned char ntbuffer[0x18]; unsigned char entropy[8]; unsigned char ntlmv2hash[0x18]; @@ -558,7 +544,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, if(result) return result; - result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); + result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer); if(result) return result; @@ -580,67 +566,21 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, return result; ptr_ntresp = ntlmv2resp; -# else /* defined(USE_NTLM_V2) */ - unsigned char ntbuffer[0x18]; - unsigned char tmp[0x18]; - unsigned char md5sum[MD5_DIGEST_LEN]; - unsigned char entropy[8]; - - /* NTLM version 1 with extended security. */ - - /* Need to create 8 bytes random data */ - result = Curl_rand(data, entropy, 8); - if(result) - return result; - - /* 8 bytes random data as challenge in lmresp */ - memcpy(lmresp, entropy, 8); - - /* Pad with zeros */ - memset(lmresp + 8, 0, 0x10); - - /* Fill tmp with challenge(nonce?) + entropy */ - memcpy(tmp, &ntlm->nonce[0], 8); - memcpy(tmp + 8, entropy, 8); - - 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 */ - result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); - if(result) - return result; - - Curl_ntlm_core_lm_resp(ntbuffer, md5sum, ntresp); - - /* End of NTLM2 Session code */ - /* NTLM v2 session security is a misnomer because it is not NTLM v2. - It is NTLM v1 using the extended session security that is also - in NTLM v2 */ -# endif /* defined(USE_NTLM_V2) */ } - else -#endif - { + else { -#ifdef USE_NTRESPONSES unsigned char ntbuffer[0x18]; -#endif unsigned char lmbuffer[0x18]; /* NTLM version 1 */ -#ifdef USE_NTRESPONSES - result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); + result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer); if(result) return result; Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp); -#endif - result = Curl_ntlm_core_mk_lm_hash(data, passwdp, lmbuffer); + result = Curl_ntlm_core_mk_lm_hash(passwdp, lmbuffer); if(result) return result; @@ -659,12 +599,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, } lmrespoff = 64; /* size of the message header */ -#ifdef USE_NTRESPONSES ntrespoff = lmrespoff + 0x18; domoff = ntrespoff + ntresplen; -#else - domoff = lmrespoff + 0x18; -#endif useroff = domoff + domlen; hostoff = useroff + userlen; @@ -719,17 +655,11 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SHORTPAIR(lmrespoff), 0x0, 0x0, -#ifdef USE_NTRESPONSES SHORTPAIR(ntresplen), /* NT-response length, twice */ SHORTPAIR(ntresplen), SHORTPAIR(ntrespoff), 0x0, 0x0, -#else - 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, -#endif + SHORTPAIR(domlen), SHORTPAIR(domlen), SHORTPAIR(domoff), @@ -766,7 +696,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18); }); -#ifdef USE_NTRESPONSES /* ntresplen + size should not be risking an integer overflow here */ if(ntresplen + size > sizeof(ntlmbuf)) { failf(data, "incoming NTLM message too big"); @@ -783,8 +712,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ -#endif - DEBUG_OUT({ fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ", LONGQUARTET(ntlm->flags), ntlm->flags); @@ -823,12 +750,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, size += hostlen; - /* Convert domain, user, and host to ASCII but leave the rest as-is */ - result = Curl_convert_to_network(data, (char *)&ntlmbuf[domoff], - size - domoff); - if(result) - return CURLE_CONV_FAILED; - /* Return the binary blob. */ result = Curl_bufref_memdup(out, ntlmbuf, size); diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 8e8932b..8c1a3ed 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -204,16 +204,14 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data, - struct negotiatedata *nego, +CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, char **outptr, size_t *outlen) { CURLcode result; OM_uint32 minor_status; /* Base64 encode the already generated response */ - result = Curl_base64_encode(data, - nego->output_token.value, + result = Curl_base64_encode(nego->output_token.value, nego->output_token.length, outptr, outlen); diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 68bb17d..d219d8b 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -301,27 +301,19 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data, - struct negotiatedata *nego, +CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, char **outptr, size_t *outlen) { - CURLcode result; - /* Base64 encode the already generated response */ - result = Curl_base64_encode(data, - (const char *) nego->output_token, - nego->output_token_length, - outptr, outlen); - - if(result) - return result; - - if(!*outptr || !*outlen) { + CURLcode result = Curl_base64_encode((const char *) nego->output_token, + nego->output_token_length, outptr, + outlen); + if(!result && (!*outptr || !*outlen)) { free(*outptr); - return CURLE_REMOTE_ACCESS_DENIED; + result = CURLE_REMOTE_ACCESS_DENIED; } - return CURLE_OK; + return result; } /* diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 47a7c0b..6e12378 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2021, Steve Holme, . + * Copyright (C) 2014 - 2022, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -219,8 +219,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* This is used to generate a base64 encoded SPNEGO (Negotiate) response message */ -CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data, - struct negotiatedata *nego, +CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, char **outptr, size_t *outlen); /* This is used to clean up the SPNEGO specifiec data */ diff --git a/lib/version.c b/lib/version.c index c84ef85..e37253d 100644 --- a/lib/version.c +++ b/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -46,10 +46,6 @@ #include #endif -#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) -#include -#endif - #ifdef USE_LIBRTMP #include #endif @@ -106,7 +102,7 @@ static void zstd_version(char *buf, size_t bufsz) * zeros in the data. */ -#define VERSION_PARTS 17 /* number of substrings we can concatenate */ +#define VERSION_PARTS 16 /* number of substrings we can concatenate */ char *curl_version(void) { @@ -135,9 +131,6 @@ char *curl_version(void) #ifdef USE_LIBPSL char psl_version[40]; #endif -#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) - char iconv_version[40]="iconv"; -#endif #ifdef USE_SSH char ssh_version[40]; #endif @@ -206,15 +199,7 @@ char *curl_version(void) msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version()); src[i++] = psl_version; #endif -#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) -#ifdef _LIBICONV_VERSION - msnprintf(iconv_version, sizeof(iconv_version), "iconv/%d.%d", - _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255); -#else - /* version unknown, let the default stand */ -#endif /* _LIBICONV_VERSION */ - src[i++] = iconv_version; -#endif + #ifdef USE_SSH Curl_ssh_version(ssh_version, sizeof(ssh_version)); src[i++] = ssh_version; @@ -433,9 +418,6 @@ static curl_version_info_data version_info = { #if defined(WIN32) && defined(UNICODE) && defined(_UNICODE) | CURL_VERSION_UNICODE #endif -#if defined(CURL_DOES_CONVERSIONS) - | CURL_VERSION_CONV -#endif #if defined(USE_TLS_SRP) | CURL_VERSION_TLSAUTH_SRP #endif @@ -551,15 +533,6 @@ curl_version_info_data *curl_version_info(CURLversion stamp) version_info.features |= CURL_VERSION_IDN; #endif -#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) -#ifdef _LIBICONV_VERSION - version_info.iconv_ver_num = _LIBICONV_VERSION; -#else - /* version unknown */ - version_info.iconv_ver_num = -1; -#endif /* _LIBICONV_VERSION */ -#endif - #if defined(USE_SSH) Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer)); version_info.libssh_version = ssh_buffer; diff --git a/lib/version_win32.c b/lib/version_win32.c index 79a2aa6..afdb1d6 100644 --- a/lib/version_win32.c +++ b/lib/version_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2021, Steve Holme, . + * Copyright (C) 2016 - 2022, Steve Holme, . * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -76,6 +76,8 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, bool matched = FALSE; #if defined(CURL_WINDOWS_APP) + (void)buildVersion; + /* We have no way to determine the Windows version from Windows apps, so let's assume we're running on the target Windows version. */ const WORD fullVersion = MAKEWORD(minorVersion, majorVersion); diff --git a/lib/vquic/msh3.c b/lib/vquic/msh3.c new file mode 100644 index 0000000..be18e6e --- /dev/null +++ b/lib/vquic/msh3.c @@ -0,0 +1,498 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_MSH3 + +#include "urldata.h" +#include "curl_printf.h" +#include "timeval.h" +#include "multiif.h" +#include "sendf.h" +#include "connect.h" +#include "h2h3.h" +#include "msh3.h" + +/* #define DEBUG_HTTP3 1 */ +#ifdef DEBUG_HTTP3 +#define H3BUGF(x) x +#else +#define H3BUGF(x) do { } while(0) +#endif + +#define MSH3_REQ_INIT_BUF_LEN 8192 + +static CURLcode msh3_do_it(struct Curl_easy *data, bool *done); +static int msh3_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks); +static CURLcode msh3_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); +static unsigned int msh3_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform); +static Curl_recv msh3_stream_recv; +static Curl_send msh3_stream_send; +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header); +static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t Length, + const uint8_t *Data); +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError); +static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext); + +static const struct Curl_handler msh3_curl_handler_http3 = { + "HTTPS", /* scheme */ + ZERO_NULL, /* setup_connection */ + msh3_do_it, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + ZERO_NULL, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + msh3_getsock, /* proto_getsock */ + msh3_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + msh3_getsock, /* perform_getsock */ + msh3_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + msh3_conncheck, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_STREAM /* flags */ +}; + +static const MSH3_REQUEST_IF msh3_request_if = { + msh3_header_received, + msh3_data_received, + msh3_complete, + msh3_shutdown +}; + +void Curl_quic_ver(char *p, size_t len) +{ + (void)msnprintf(p, len, "msh3/%s", "0.0.1"); +} + +CURLcode Curl_quic_connect(struct Curl_easy *data, + struct connectdata *conn, + curl_socket_t sockfd, + int sockindex, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct quicsocket *qs = &conn->hequic[sockindex]; + bool unsecure = !conn->ssl_config.verifypeer; + memset(qs, 0, sizeof(*qs)); + + (void)sockfd; + (void)addr; /* TODO - Pass address along */ + (void)addrlen; + + H3BUGF(infof(data, "creating new api/connection")); + + qs->api = MsH3ApiOpen(); + if(!qs->api) { + failf(data, "can't create msh3 api"); + return CURLE_FAILED_INIT; + } + + qs->conn = MsH3ConnectionOpen(qs->api, conn->host.name, unsecure); + if(!qs->conn) { + failf(data, "can't create msh3 connection"); + if(qs->api) { + MsH3ApiClose(qs->api); + } + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} + +CURLcode Curl_quic_is_connected(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + bool *connected) +{ + struct quicsocket *qs = &conn->hequic[sockindex]; + MSH3_CONNECTION_STATE state; + + state = MsH3ConnectionGetState(qs->conn, false); + if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) { + failf(data, "failed to connect, state=%u", (uint32_t)state); + return CURLE_COULDNT_CONNECT; + } + + if(state == MSH3_CONN_CONNECTED) { + H3BUGF(infof(data, "connection connected")); + *connected = true; + conn->quic = qs; + conn->recv[sockindex] = msh3_stream_recv; + conn->send[sockindex] = msh3_stream_send; + conn->handler = &msh3_curl_handler_http3; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->httpversion = 30; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + /* TODO - Clean up other happy-eyeballs connection(s)? */ + } + + return CURLE_OK; +} + +static int msh3_getsock(struct Curl_easy *data, + struct connectdata *conn, curl_socket_t *socks) +{ + struct HTTP *stream = data->req.p.http; + int bitmap = GETSOCK_BLANK; + + socks[0] = conn->sock[FIRSTSOCKET]; + + if(stream->recv_error) { + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + data->state.drain++; + } + else if(stream->recv_header_len || stream->recv_data_len) { + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + data->state.drain++; + } + + H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain)); + + return bitmap; +} + +static CURLcode msh3_do_it(struct Curl_easy *data, bool *done) +{ + struct HTTP *stream = data->req.p.http; + H3BUGF(infof(data, "msh3_do_it")); + stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN); + if(!stream->recv_buf) { + return CURLE_OUT_OF_MEMORY; + } + stream->req = ZERO_NULL; + msh3_lock_initialize(&stream->recv_lock); + stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN; + stream->recv_header_len = 0; + stream->recv_header_complete = false; + stream->recv_data_len = 0; + stream->recv_data_complete = false; + stream->recv_error = CURLE_OK; + return Curl_http(data, done); +} + +static unsigned int msh3_conncheck(struct Curl_easy *data, + struct connectdata *conn, + unsigned int checks_to_perform) +{ + (void)data; + (void)conn; + (void)checks_to_perform; + H3BUGF(infof(data, "msh3_conncheck")); + return CONNRESULT_NONE; +} + +static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream) +{ + if(stream && stream->recv_buf) { + free(stream->recv_buf); + stream->recv_buf = ZERO_NULL; + msh3_lock_uninitialize(&stream->recv_lock); + } + if(qs->conn) { + MsH3ConnectionClose(qs->conn); + qs->conn = ZERO_NULL; + } + if(qs->api) { + MsH3ApiClose(qs->api); + qs->api = ZERO_NULL; + } +} + +static CURLcode msh3_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + (void)dead_connection; + H3BUGF(infof(data, "disconnecting (msh3)")); + msh3_cleanup(conn->quic, data->req.p.http); + return CURLE_OK; +} + +void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn, + int tempindex) +{ + if(conn->transport == TRNSPRT_QUIC) { + H3BUGF(infof(data, "disconnecting (curl)")); + msh3_cleanup(&conn->hequic[tempindex], data->req.p.http); + } +} + +/* Requires stream->recv_lock to be held */ +static bool msh3request_ensure_room(struct HTTP *stream, size_t len) +{ + uint8_t *new_recv_buf; + const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; + if(cur_recv_len + len > stream->recv_buf_alloc) { + size_t new_recv_buf_alloc_len = stream->recv_buf_alloc; + do { + new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */ + } while(cur_recv_len + len > new_recv_buf_alloc_len); + new_recv_buf = malloc(new_recv_buf_alloc_len); + if(!new_recv_buf) { + return false; + } + if(cur_recv_len) { + memcpy(new_recv_buf, stream->recv_buf, cur_recv_len); + } + stream->recv_buf_alloc = new_recv_buf_alloc_len; + free(stream->recv_buf); + stream->recv_buf = new_recv_buf; + } + return true; +} + +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header) +{ + struct HTTP *stream = IfContext; + size_t total_len; + (void)Request; + H3BUGF(printf("* msh3_header_received\n")); + + if(stream->recv_header_complete) { + H3BUGF(printf("* ignoring header after data\n")); + return; + } + + msh3_lock_acquire(&stream->recv_lock); + + if((Header->NameLength == 7) && + !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) { + total_len = 9 + Header->ValueLength; + if(!msh3request_ensure_room(stream, total_len)) { + /* TODO - handle error */ + goto release_lock; + } + msnprintf((char *)stream->recv_buf + stream->recv_header_len, + stream->recv_buf_alloc - stream->recv_header_len, + "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value); + } + else { + total_len = Header->NameLength + 4 + Header->ValueLength; + if(!msh3request_ensure_room(stream, total_len)) { + /* TODO - handle error */ + goto release_lock; + } + msnprintf((char *)stream->recv_buf + stream->recv_header_len, + stream->recv_buf_alloc - stream->recv_header_len, + "%.*s: %.*s\n", + (int)Header->NameLength, Header->Name, + (int)Header->ValueLength, Header->Value); + } + + stream->recv_header_len += total_len - 1; /* don't include null-terminator */ + +release_lock: + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t Length, + const uint8_t *Data) +{ + struct HTTP *stream = IfContext; + size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; + (void)Request; + H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n", + Length, cur_recv_len, stream->recv_buf_alloc)); + msh3_lock_acquire(&stream->recv_lock); + if(!stream->recv_header_complete) { + H3BUGF(printf("* Headers complete!\n")); + if(!msh3request_ensure_room(stream, 2)) { + /* TODO - handle error */ + goto release_lock; + } + stream->recv_buf[stream->recv_header_len++] = '\r'; + stream->recv_buf[stream->recv_header_len++] = '\n'; + stream->recv_header_complete = true; + cur_recv_len += 2; + } + if(!msh3request_ensure_room(stream, Length)) { + /* TODO - handle error */ + goto release_lock; + } + memcpy(stream->recv_buf + cur_recv_len, Data, Length); + stream->recv_data_len += (size_t)Length; +release_lock: + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError) +{ + struct HTTP *stream = IfContext; + (void)Request; + (void)AbortError; + H3BUGF(printf("* msh3_complete, aborted=%hhu\n", Aborted)); + msh3_lock_acquire(&stream->recv_lock); + if(Aborted) { + stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */ + } + stream->recv_header_complete = true; + stream->recv_data_complete = true; + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext) +{ + struct HTTP *stream = IfContext; + (void)Request; + (void)stream; +} + +static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo), + "Sizes must match for cast below to work"); + +static ssize_t msh3_stream_send(struct Curl_easy *data, + int sockindex, + const void *mem, + size_t len, + CURLcode *curlcode) +{ + struct connectdata *conn = data->conn; + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; + struct h2h3req *hreq; + + (void)sockindex; + H3BUGF(infof(data, "msh3_stream_send %zu", len)); + + if(!stream->req) { + *curlcode = Curl_pseudo_headers(data, mem, len, &hreq); + if(*curlcode) { + failf(data, "Curl_pseudo_headers failed"); + return -1; + } + H3BUGF(infof(data, "starting request with %zu headers", hreq->entries)); + stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream, + (MSH3_HEADER*)hreq->header, hreq->entries); + Curl_pseudo_free(hreq); + if(!stream->req) { + failf(data, "request open failed"); + *curlcode = CURLE_SEND_ERROR; + return -1; + } + *curlcode = CURLE_OK; + return len; + } + H3BUGF(infof(data, "send %zd body bytes on request %p", len, + (void *)stream->req)); + *curlcode = CURLE_SEND_ERROR; + return -1; +} + +static ssize_t msh3_stream_recv(struct Curl_easy *data, + int sockindex, + char *buf, + size_t buffersize, + CURLcode *curlcode) +{ + struct HTTP *stream = data->req.p.http; + size_t outsize = 0; + (void)sockindex; + H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize)); + + if(stream->recv_error) { + failf(data, "request aborted"); + *curlcode = stream->recv_error; + return -1; + } + + msh3_lock_acquire(&stream->recv_lock); + + if(stream->recv_header_len) { + outsize = buffersize; + if(stream->recv_header_len < outsize) { + outsize = stream->recv_header_len; + } + memcpy(buf, stream->recv_buf, outsize); + if(outsize < stream->recv_header_len + stream->recv_data_len) { + memmove(stream->recv_buf, stream->recv_buf + outsize, + stream->recv_header_len + stream->recv_data_len - outsize); + } + stream->recv_header_len -= outsize; + H3BUGF(infof(data, "returned %zu bytes of headers", outsize)); + } + else if(stream->recv_data_len) { + outsize = buffersize; + if(stream->recv_data_len < outsize) { + outsize = stream->recv_data_len; + } + memcpy(buf, stream->recv_buf, outsize); + if(outsize < stream->recv_data_len) { + memmove(stream->recv_buf, stream->recv_buf + outsize, + stream->recv_data_len - outsize); + } + stream->recv_data_len -= outsize; + H3BUGF(infof(data, "returned %zu bytes of data", outsize)); + } + else if(stream->recv_data_complete) { + H3BUGF(infof(data, "receive complete")); + } + + msh3_lock_release(&stream->recv_lock); + + return (ssize_t)outsize; +} + +CURLcode Curl_quic_done_sending(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + H3BUGF(infof(data, "Curl_quic_done_sending")); + if(conn->handler == &msh3_curl_handler_http3) { + struct HTTP *stream = data->req.p.http; + stream->upload_done = TRUE; + } + + return CURLE_OK; +} + +void Curl_quic_done(struct Curl_easy *data, bool premature) +{ + (void)data; + (void)premature; + H3BUGF(infof(data, "Curl_quic_done")); +} + +bool Curl_quic_data_pending(const struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending")); + return stream->recv_header_len || stream->recv_data_len; +} + +#endif /* USE_MSH3 */ diff --git a/lib/vquic/msh3.h b/lib/vquic/msh3.h new file mode 100644 index 0000000..bacdcb1 --- /dev/null +++ b/lib/vquic/msh3.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_VQUIC_MSH3_H +#define HEADER_CURL_VQUIC_MSH3_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_MSH3 + +#include + +struct quicsocket { + MSH3_API* api; + MSH3_CONNECTION* conn; +}; + +#endif /* USE_MSQUIC */ + +#endif /* HEADER_CURL_VQUIC_MSH3_H */ diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c index 1596049..abce631 100644 --- a/lib/vquic/ngtcp2.c +++ b/lib/vquic/ngtcp2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -45,7 +45,9 @@ #include "strerror.h" #include "dynbuf.h" #include "vquic.h" +#include "h2h3.h" #include "vtls/keylog.h" +#include "vtls/vtls.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -71,7 +73,7 @@ * the far end, then start over at index 0 again. */ -#define H3_SEND_SIZE (20*1024) +#define H3_SEND_SIZE (256*1024) struct h3out { uint8_t buf[H3_SEND_SIZE]; size_t used; /* number of bytes used in the buffer */ @@ -81,7 +83,7 @@ struct h3out { #define QUIC_MAX_STREAMS (256*1024) #define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */ +#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) #ifdef USE_OPENSSL #define QUIC_CIPHERS \ @@ -313,6 +315,25 @@ static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) return ssl_ctx; } +static CURLcode quic_set_client_cert(struct Curl_easy *data, + struct quicsocket *qs) +{ + struct connectdata *conn = data->conn; + SSL_CTX *ssl_ctx = qs->sslctx; + char *const ssl_cert = SSL_SET_OPTION(primary.clientcert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); + const char *const ssl_cert_type = SSL_SET_OPTION(cert_type); + + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { + return Curl_ossl_set_client_cert( + data, ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, + SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob), + SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)); + } + + return CURLE_OK; +} + /** SSL callbacks ***/ static int quic_init_ssl(struct quicsocket *qs) @@ -743,7 +764,8 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* ack_datagram */ NULL, /* lost_datagram */ ngtcp2_crypto_get_path_challenge_data_cb, - cb_stream_stop_sending + cb_stream_stop_sending, + NULL, /* version_negotiation */ }; /* @@ -785,6 +807,10 @@ CURLcode Curl_quic_connect(struct Curl_easy *data, qs->sslctx = quic_ssl_ctx(data); if(!qs->sslctx) return CURLE_QUIC_CONNECT_ERROR; + + result = quic_set_client_cert(data, qs); + if(result) + return result; #endif if(quic_init_ssl(qs)) @@ -841,6 +867,8 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn, { struct SingleRequest *k = &data->req; int bitmap = GETSOCK_BLANK; + struct HTTP *stream = data->req.p.http; + struct quicsocket *qs = conn->quic; socks[0] = conn->sock[FIRSTSOCKET]; @@ -849,7 +877,11 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn, 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) + if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND && + (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) && + ngtcp2_conn_get_cwnd_left(qs->qconn) && + ngtcp2_conn_get_max_data_left(qs->qconn) && + nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id)) bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); return bitmap; @@ -857,8 +889,26 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn, static void qs_disconnect(struct quicsocket *qs) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_tstamp ts; + ngtcp2_ssize rc; + ngtcp2_connection_close_error errorcode; + if(!qs->conn) /* already closed */ return; + ngtcp2_connection_close_error_set_application_error(&errorcode, + NGHTTP3_H3_NO_ERROR, + NULL, 0); + ts = timestamp(); + rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &errorcode, ts); + if(rc > 0) { + while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) && + SOCKERRNO == EINTR); + } + qs->conn = NULL; if(qs->qlogfd != -1) { close(qs->qlogfd); @@ -1043,7 +1093,7 @@ static int decode_status_code(const uint8_t *value, size_t len) } static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - void *user_data, void *stream_user_data) + int fin, void *user_data, void *stream_user_data) { struct Curl_easy *data = stream_user_data; struct HTTP *stream = data->req.p.http; @@ -1051,6 +1101,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, (void)conn; (void)stream_id; (void)user_data; + (void)fin; /* add a CRLF only if we've received some headers */ if(stream->firstheader) { @@ -1078,8 +1129,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, (void)flags; (void)user_data; - if(h3name.len == sizeof(":status") - 1 && - !memcmp(":status", h3name.base, h3name.len)) { + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { char line[14]; /* status line is always 13 characters long */ size_t ncopy; int status = decode_status_code(h3val.base, h3val.len); @@ -1218,6 +1268,8 @@ static size_t drain_overflow_buffer(struct HTTP *stream) if(ncopy != overlen) /* make the buffer only keep the tail */ (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy); + else + Curl_dyn_reset(&stream->overflow); } return ncopy; } @@ -1329,6 +1381,10 @@ static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, return 1; } + if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) { + return NGHTTP3_ERR_WOULDBLOCK; + } + nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); if(nread > 0) { /* nghttp3 wants us to hold on to the data until it tells us it is okay to @@ -1363,7 +1419,7 @@ static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, } if(stream->upload_done && !stream->upload_len && (stream->upload_left <= 0)) { - H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF")); + H3BUGF(infof(data, "cb_h3_readfunction sets EOF")); *pflags = NGHTTP3_DATA_FLAG_EOF; return nread ? 1 : 0; } @@ -1383,16 +1439,13 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem, struct connectdata *conn = data->conn; struct HTTP *stream = data->req.p.http; size_t nheader; - size_t i; - size_t authority_idx; - char *hdbuf = (char *)mem; - char *end, *line_end; struct quicsocket *qs = conn->quic; CURLcode result = CURLE_OK; nghttp3_nv *nva = NULL; int64_t stream3_id; int rc; struct h3out *h3out = NULL; + struct h2h3req *hreq = NULL; rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); if(rc) { @@ -1405,158 +1458,23 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem, stream->h3req = TRUE; /* senf off! */ Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE); - /* Calculate number of headers contained in [mem, mem + len). Assumes a - correctly generated HTTP header field block. */ - nheader = 0; - for(i = 1; i < len; ++i) { - if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { - ++nheader; - ++i; - } - } - if(nheader < 2) + result = Curl_pseudo_headers(data, mem, len, &hreq); + if(result) goto fail; + nheader = hreq->entries; - /* We counted additional 2 \r\n in the first and last line. We need 3 - new headers: :method, :path and :scheme. Therefore we need one - more space. */ - nheader += 1; nva = malloc(sizeof(nghttp3_nv) * nheader); if(!nva) { result = CURLE_OUT_OF_MEMORY; goto fail; } - - /* Extract :method, :path from request line - We do line endings with CRLF so checking for CR is enough */ - line_end = memchr(hdbuf, '\r', len); - if(!line_end) { - result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ - goto fail; - } - - /* Method does not contain spaces */ - end = memchr(hdbuf, ' ', line_end - hdbuf); - if(!end || end == hdbuf) - goto fail; - nva[0].name = (unsigned char *)":method"; - nva[0].namelen = strlen((char *)nva[0].name); - nva[0].value = (unsigned char *)hdbuf; - nva[0].valuelen = (size_t)(end - hdbuf); - nva[0].flags = NGHTTP3_NV_FLAG_NONE; - - hdbuf = end + 1; - - /* Path may contain spaces so scan backwards */ - end = NULL; - for(i = (size_t)(line_end - hdbuf); i; --i) { - if(hdbuf[i - 1] == ' ') { - end = &hdbuf[i - 1]; - break; - } - } - if(!end || end == hdbuf) - goto fail; - nva[1].name = (unsigned char *)":path"; - nva[1].namelen = strlen((char *)nva[1].name); - nva[1].value = (unsigned char *)hdbuf; - nva[1].valuelen = (size_t)(end - hdbuf); - nva[1].flags = NGHTTP3_NV_FLAG_NONE; - - nva[2].name = (unsigned char *)":scheme"; - nva[2].namelen = strlen((char *)nva[2].name); - if(conn->handler->flags & PROTOPT_SSL) - nva[2].value = (unsigned char *)"https"; - else - nva[2].value = (unsigned char *)"http"; - nva[2].valuelen = strlen((char *)nva[2].value); - nva[2].flags = NGHTTP3_NV_FLAG_NONE; - - - authority_idx = 0; - i = 3; - while(i < nheader) { - size_t hlen; - - hdbuf = line_end + 2; - - /* check for next CR, but only within the piece of data left in the given - buffer */ - line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); - if(!line_end || (line_end == hdbuf)) - goto fail; - - /* header continuation lines are not supported */ - if(*hdbuf == ' ' || *hdbuf == '\t') - goto fail; - - for(end = hdbuf; end < line_end && *end != ':'; ++end) - ; - if(end == hdbuf || end == line_end) - goto fail; - hlen = end - hdbuf; - - if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { - authority_idx = i; - nva[i].name = (unsigned char *)":authority"; - nva[i].namelen = strlen((char *)nva[i].name); - } - else { - nva[i].namelen = (size_t)(end - hdbuf); - /* Lower case the header name for HTTP/3 */ - Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); - nva[i].name = (unsigned char *)hdbuf; - } - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - hdbuf = end + 1; - while(*hdbuf == ' ' || *hdbuf == '\t') - ++hdbuf; - end = line_end; - -#if 0 /* This should probably go in more or less like this */ - switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, - end - hdbuf)) { - case HEADERINST_IGNORE: - /* skip header fields prohibited by HTTP/2 specification. */ - --nheader; - continue; - case HEADERINST_TE_TRAILERS: - nva[i].value = (uint8_t*)"trailers"; - nva[i].value_len = sizeof("trailers") - 1; - break; - default: - nva[i].value = (unsigned char *)hdbuf; - nva[i].value_len = (size_t)(end - hdbuf); - } -#endif - nva[i].value = (unsigned char *)hdbuf; - nva[i].valuelen = (size_t)(end - hdbuf); - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - - ++i; - } - - /* :authority must come before non-pseudo header fields */ - if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { - nghttp3_nv authority = nva[authority_idx]; - for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { - nva[i] = nva[i - 1]; - } - nva[i] = authority; - } - - /* Warn stream may be rejected if cumulative length of headers is too - large. */ -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; - for(i = 0; i < nheader; ++i) - acc += nva[i].namelen + nva[i].valuelen; - - if(acc > MAX_ACC) { - infof(data, "http_request: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].namelen = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].valuelen = hreq->header[i].valuelen; } } @@ -1605,10 +1523,12 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem, infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", stream3_id, (void *)data); + Curl_pseudo_free(hreq); return CURLE_OK; fail: free(nva); + Curl_pseudo_free(hreq); return result; } static ssize_t ngh3_stream_send(struct Curl_easy *data, @@ -1617,7 +1537,7 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data, size_t len, CURLcode *curlcode) { - ssize_t sent; + ssize_t sent = 0; struct connectdata *conn = data->conn; struct quicsocket *qs = conn->quic; curl_socket_t sockfd = conn->sock[sockindex]; @@ -1629,6 +1549,9 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data, *curlcode = CURLE_SEND_ERROR; return -1; } + /* Assume that mem of length len only includes HTTP/1.1 style + header fields. In other words, it does not contain request + body. */ sent = len; } else { @@ -1638,7 +1561,6 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data, stream->upload_mem = mem; stream->upload_len = len; (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); - sent = len; } else { *curlcode = CURLE_AGAIN; @@ -1653,8 +1575,20 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data, /* Reset post upload buffer after resumed. */ if(stream->upload_mem) { + if(data->set.postfields) { + sent = len; + } + else { + sent = len - stream->upload_len; + } + stream->upload_mem = NULL; stream->upload_len = 0; + + if(sent == 0) { + *curlcode = CURLE_AGAIN; + return -1; + } } *curlcode = CURLE_OK; @@ -1676,7 +1610,6 @@ static CURLcode ng_has_connected(struct Curl_easy *data, 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; @@ -1786,7 +1719,6 @@ static CURLcode ng_flush_egress(struct Curl_easy *data, uint8_t out[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; ngtcp2_path_storage ps; ngtcp2_tstamp ts = timestamp(); - struct sockaddr_storage remote_addr; ngtcp2_tstamp expiry; ngtcp2_duration timeout; int64_t stream_id; @@ -1875,7 +1807,6 @@ static CURLcode ng_flush_egress(struct Curl_easy *data, } } - memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen); while((sent = send(sockfd, (const char *)out, outlen, 0)) == -1 && SOCKERRNO == EINTR) ; @@ -1896,10 +1827,13 @@ static CURLcode ng_flush_egress(struct Curl_easy *data, expiry = ngtcp2_conn_get_expiry(qs->qconn); if(expiry != UINT64_MAX) { if(expiry <= ts) { - timeout = NGTCP2_MILLISECONDS; + timeout = 0; } else { timeout = expiry - ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } } Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); } @@ -1935,6 +1869,7 @@ void Curl_quic_done(struct Curl_easy *data, bool premature) /* only for HTTP/3 transfers */ struct HTTP *stream = data->req.p.http; Curl_dyn_free(&stream->overflow); + free(stream->h3out); } } diff --git a/lib/vquic/quiche.c b/lib/vquic/quiche.c index f757760..bfdc966 100644 --- a/lib/vquic/quiche.c +++ b/lib/vquic/quiche.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,6 +25,7 @@ #ifdef USE_QUICHE #include #include +#include #include "urldata.h" #include "sendf.h" #include "strdup.h" @@ -35,6 +36,10 @@ #include "connect.h" #include "strerror.h" #include "vquic.h" +#include "transfer.h" +#include "h2h3.h" +#include "vtls/openssl.h" +#include "vtls/keylog.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -172,6 +177,68 @@ static void quiche_debug_log(const char *line, void *argp) } #endif +static void keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} + +static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) +{ + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + + SSL_CTX_set_alpn_protos(ssl_ctx, + (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); + + SSL_CTX_set_default_verify_paths(ssl_ctx); + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + 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; +} + +static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn) +{ + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = conn->host.name; + + DEBUGASSERT(!qs->ssl); + qs->ssl = SSL_new(qs->sslctx); + + SSL_set_app_data(qs->ssl, qs); + + /* set SNI */ + SSL_set_tlsext_host_name(qs->ssl, hostname); + return 0; +} + + CURLcode Curl_quic_connect(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sockfd, int sockindex, @@ -179,7 +246,6 @@ CURLcode Curl_quic_connect(struct Curl_easy *data, { CURLcode result; struct quicsocket *qs = &conn->hequic[sockindex]; - char *keylog_file = NULL; char ipbuf[40]; int port; @@ -216,25 +282,25 @@ CURLcode Curl_quic_connect(struct Curl_easy *data, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); + qs->sslctx = quic_ssl_ctx(data); + if(!qs->sslctx) + return CURLE_QUIC_CONNECT_ERROR; + + if(quic_init_ssl(qs, conn)) + return CURLE_QUIC_CONNECT_ERROR; + result = Curl_rand(data, qs->scid, sizeof(qs->scid)); if(result) return result; - keylog_file = getenv("SSLKEYLOGFILE"); - - if(keylog_file) - quiche_config_log_keys(qs->cfg); - - qs->conn = quiche_connect(conn->host.name, (const uint8_t *) qs->scid, - sizeof(qs->scid), addr, addrlen, qs->cfg); + qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid, + sizeof(qs->scid), NULL, 0, addr, addrlen, + qs->cfg, qs->ssl, false); if(!qs->conn) { failf(data, "can't create quiche connection"); return CURLE_OUT_OF_MEMORY; } - if(keylog_file) - quiche_conn_set_keylog_path(qs->conn, keylog_file); - /* Known to not work on Windows */ #if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) { @@ -284,7 +350,8 @@ CURLcode Curl_quic_connect(struct Curl_easy *data, return CURLE_OK; } -static CURLcode quiche_has_connected(struct connectdata *conn, +static CURLcode quiche_has_connected(struct Curl_easy *data, + struct connectdata *conn, int sockindex, int tempindex) { @@ -298,6 +365,21 @@ static CURLcode quiche_has_connected(struct connectdata *conn, conn->httpversion = 30; conn->bundle->multiuse = BUNDLE_MULTIPLEX; + if(conn->ssl_config.verifyhost) { + X509 *server_cert; + server_cert = SSL_get_peer_certificate(qs->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 + infof(data, "Skipped certificate verification"); + qs->h3config = quiche_h3_config_new(); if(!qs->h3config) return CURLE_OUT_OF_MEMORY; @@ -344,8 +426,8 @@ CURLcode Curl_quic_is_connected(struct Curl_easy *data, if(quiche_conn_is_established(qs->conn)) { *done = TRUE; - result = quiche_has_connected(conn, 0, sockindex); - DEBUGF(infof(data, "quiche established connection!")); + result = quiche_has_connected(data, conn, 0, sockindex); + DEBUGF(infof(data, "quiche established connection")); } return result; @@ -392,7 +474,18 @@ static CURLcode process_ingress(struct Curl_easy *data, int sockfd, break; if(recvd < 0) { + if(QUICHE_ERR_TLS_FAIL == recvd) { + long verify_ok = SSL_get_verify_result(qs->ssl); + if(verify_ok != X509_V_OK) { + failf(data, "SSL certificate problem: %s", + X509_verify_cert_error_string(verify_ok)); + + return CURLE_PEER_FAILED_VERIFICATION; + } + } + failf(data, "quiche_conn_recv() == %zd", recvd); + return CURLE_RECV_ERROR; } } while(1); @@ -451,7 +544,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, struct h3h1header *headers = (struct h3h1header *)argp; size_t olen = 0; - if((name_len == 7) && !strncmp(":status", (char *)name, 7)) { + if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) { msnprintf(headers->dest, headers->destlen, "HTTP/3 %.*s\n", (int) value_len, value); @@ -496,6 +589,19 @@ static ssize_t h3_stream_recv(struct Curl_easy *data, return -1; } + if(qs->h3_recving) { + /* body receiving state */ + rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id, + (unsigned char *)buf, buffersize); + if(rcode <= 0) { + recvd = -1; + qs->h3_recving = FALSE; + /* fall through into the while loop below */ + } + else + recvd = rcode; + } + while(recvd < 0) { int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev); if(s < 0) @@ -537,9 +643,15 @@ static ssize_t h3_stream_recv(struct Curl_easy *data, recvd = -1; break; } + qs->h3_recving = TRUE; recvd += rcode; break; + case QUICHE_H3_EVENT_RESET: + streamclose(conn, "Stream reset"); + *curlcode = CURLE_PARTIAL_FILE; + return -1; + case QUICHE_H3_EVENT_FINISHED: streamclose(conn, "End of stream"); recvd = 0; /* end of stream */ @@ -585,10 +697,12 @@ static ssize_t h3_stream_send(struct Curl_easy *data, sent = len; } else { - H3BUGF(infof(data, "Pass on %zd body bytes to quiche", len)); sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, (uint8_t *)mem, len, FALSE); - if(sent < 0) { + if(sent == QUICHE_H3_ERR_DONE) { + sent = 0; + } + else if(sent < 0) { *curlcode = CURLE_SEND_ERROR; return -1; } @@ -618,175 +732,34 @@ void Curl_quic_ver(char *p, size_t len) static CURLcode http_request(struct Curl_easy *data, const void *mem, size_t len) { - /* - */ struct connectdata *conn = data->conn; struct HTTP *stream = data->req.p.http; size_t nheader; - size_t i; - size_t authority_idx; - char *hdbuf = (char *)mem; - char *end, *line_end; int64_t stream3_id; quiche_h3_header *nva = NULL; struct quicsocket *qs = conn->quic; CURLcode result = CURLE_OK; + struct h2h3req *hreq = NULL; stream->h3req = TRUE; /* senf off! */ - /* Calculate number of headers contained in [mem, mem + len). Assumes a - correctly generated HTTP header field block. */ - nheader = 0; - for(i = 1; i < len; ++i) { - if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { - ++nheader; - ++i; - } - } - if(nheader < 2) + result = Curl_pseudo_headers(data, mem, len, &hreq); + if(result) goto fail; + nheader = hreq->entries; - /* We counted additional 2 \r\n in the first and last line. We need 3 - new headers: :method, :path and :scheme. Therefore we need one - more space. */ - nheader += 1; nva = malloc(sizeof(quiche_h3_header) * nheader); if(!nva) { result = CURLE_OUT_OF_MEMORY; goto fail; } - - /* Extract :method, :path from request line - We do line endings with CRLF so checking for CR is enough */ - line_end = memchr(hdbuf, '\r', len); - if(!line_end) { - result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ - goto fail; - } - - /* Method does not contain spaces */ - end = memchr(hdbuf, ' ', line_end - hdbuf); - if(!end || end == hdbuf) - goto fail; - nva[0].name = (unsigned char *)":method"; - nva[0].name_len = strlen((char *)nva[0].name); - nva[0].value = (unsigned char *)hdbuf; - nva[0].value_len = (size_t)(end - hdbuf); - - hdbuf = end + 1; - - /* Path may contain spaces so scan backwards */ - end = NULL; - for(i = (size_t)(line_end - hdbuf); i; --i) { - if(hdbuf[i - 1] == ' ') { - end = &hdbuf[i - 1]; - break; - } - } - if(!end || end == hdbuf) - goto fail; - nva[1].name = (unsigned char *)":path"; - nva[1].name_len = strlen((char *)nva[1].name); - nva[1].value = (unsigned char *)hdbuf; - nva[1].value_len = (size_t)(end - hdbuf); - - nva[2].name = (unsigned char *)":scheme"; - nva[2].name_len = strlen((char *)nva[2].name); - if(conn->handler->flags & PROTOPT_SSL) - nva[2].value = (unsigned char *)"https"; - else - nva[2].value = (unsigned char *)"http"; - nva[2].value_len = strlen((char *)nva[2].value); - - - authority_idx = 0; - i = 3; - while(i < nheader) { - size_t hlen; - - hdbuf = line_end + 2; - - /* check for next CR, but only within the piece of data left in the given - buffer */ - line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); - if(!line_end || (line_end == hdbuf)) - goto fail; - - /* header continuation lines are not supported */ - if(*hdbuf == ' ' || *hdbuf == '\t') - goto fail; - - for(end = hdbuf; end < line_end && *end != ':'; ++end) - ; - if(end == hdbuf || end == line_end) - goto fail; - hlen = end - hdbuf; - - if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { - authority_idx = i; - nva[i].name = (unsigned char *)":authority"; - nva[i].name_len = strlen((char *)nva[i].name); - } - else { - nva[i].name_len = (size_t)(end - hdbuf); - /* Lower case the header name for HTTP/3 */ - Curl_strntolower((char *)hdbuf, hdbuf, nva[i].name_len); - nva[i].name = (unsigned char *)hdbuf; - } - hdbuf = end + 1; - while(*hdbuf == ' ' || *hdbuf == '\t') - ++hdbuf; - end = line_end; - -#if 0 /* This should probably go in more or less like this */ - switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, - end - hdbuf)) { - case HEADERINST_IGNORE: - /* skip header fields prohibited by HTTP/2 specification. */ - --nheader; - continue; - case HEADERINST_TE_TRAILERS: - nva[i].value = (uint8_t*)"trailers"; - nva[i].value_len = sizeof("trailers") - 1; - break; - default: - nva[i].value = (unsigned char *)hdbuf; - nva[i].value_len = (size_t)(end - hdbuf); - } -#endif - nva[i].value = (unsigned char *)hdbuf; - nva[i].value_len = (size_t)(end - hdbuf); - - ++i; - } - - /* :authority must come before non-pseudo header fields */ - if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { - quiche_h3_header authority = nva[authority_idx]; - for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { - nva[i] = nva[i - 1]; - } - nva[i] = authority; - } - - /* Warn stream may be rejected if cumulative length of headers is too - large. */ -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; - - for(i = 0; i < nheader; ++i) { - acc += nva[i].name_len + nva[i].value_len; - - H3BUGF(infof(data, "h3 [%.*s: %.*s]", - nva[i].name_len, nva[i].name, - nva[i].value_len, nva[i].value)); - } - - if(acc > MAX_ACC) { - infof(data, "http_request: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].name_len = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].value_len = hreq->header[i].valuelen; } } @@ -808,7 +781,7 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem, (uint8_t *)data->set.postfields, stream->upload_left, TRUE); if(sent <= 0) { - failf(data, "quiche_h3_send_body failed!"); + failf(data, "quiche_h3_send_body failed"); result = CURLE_SEND_ERROR; } stream->upload_left = 0; /* nothing left to send */ @@ -833,10 +806,12 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem, stream3_id, (void *)data); stream->stream3_id = stream3_id; + Curl_pseudo_free(hreq); return CURLE_OK; fail: free(nva); + Curl_pseudo_free(hreq); return result; } diff --git a/lib/vquic/quiche.h b/lib/vquic/quiche.h index d311e99..759a20b 100644 --- a/lib/vquic/quiche.h +++ b/lib/vquic/quiche.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,6 +27,7 @@ #ifdef USE_QUICHE #include +#include struct quic_handshake { char *buf; /* pointer to the buffer */ @@ -43,6 +44,9 @@ struct quicsocket { uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; curl_socket_t sockfd; uint32_t version; + SSL_CTX *sslctx; + SSL *ssl; + bool h3_recving; /* TRUE when in h3-body-reading state */ }; #endif diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 7c0cc6d..be2a65f 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -67,7 +67,7 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, result = Curl_dyn_add(&fname, hex); } if(!result) - result = Curl_dyn_add(&fname, ".qlog"); + result = Curl_dyn_add(&fname, ".sqlog"); if(!result) { int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE, diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index eb8a893..3df138f 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 3e317e8..7bf2b04 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2017 - 2021 Red Hat, Inc. + * Copyright (C) 2017 - 2022 Red Hat, Inc. * * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, * Robert Kolcun, Andreas Schneider @@ -32,10 +32,6 @@ #include #include -#ifdef HAVE_FCNTL_H -#include -#endif - #ifdef HAVE_NETINET_IN_H #include #endif @@ -81,18 +77,22 @@ #include "multiif.h" #include "select.h" #include "warnless.h" +#include "curl_path.h" -/* for permission and open flags */ -#include +#ifdef HAVE_SYS_STAT_H #include +#endif +#ifdef HAVE_UNISTD_H #include +#endif +#ifdef HAVE_FCNTL_H #include +#endif /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -#include "curl_path.h" /* A recent macro provided by libssh. Or make our own. */ #ifndef SSH_STRING_FREE_CHAR @@ -105,6 +105,14 @@ } while(0) #endif +/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */ +#ifndef SSH_S_IFMT +#define SSH_S_IFMT 00170000 +#endif +#ifndef SSH_S_IFLNK +#define SSH_S_IFLNK 0120000 +#endif + /* Local functions: */ static CURLcode myssh_connect(struct Curl_easy *data, bool *done); static CURLcode myssh_multi_statemach(struct Curl_easy *data, @@ -1468,8 +1476,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) memcpy(sshc->readdir_line, sshc->readdir_longentry, sshc->readdir_currLen); if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && - ((sshc->readdir_attrs->permissions & S_IFMT) == - S_IFLNK)) { + ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == + SSH_S_IFLNK)) { sshc->readdir_linkPath = aprintf("%s%s", protop->path, sshc->readdir_filename); @@ -1962,6 +1970,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } ssh_disconnect(sshc->ssh_session); + /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, + explicitly mark it as closed with the memdebug macro: */ + fake_sclose(conn->sock[FIRSTSOCKET]); + conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; SSH_STRING_FREE_CHAR(sshc->homedir); data->state.most_recent_ftp_entrypath = NULL; @@ -2055,6 +2067,9 @@ static int myssh_getsock(struct Curl_easy *data, if(conn->waitfor & KEEP_SEND) bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + if(!conn->waitfor) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + return bitmap; } @@ -2687,7 +2702,7 @@ static void sftp_quote(struct Curl_easy *data) */ cp = strchr(cmd, ' '); if(!cp) { - failf(data, "Syntax error in SFTP command. Supply parameter(s)!"); + failf(data, "Syntax error in SFTP command. Supply parameter(s)"); state(data, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 581bc1b..d269263 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -433,7 +433,9 @@ static int sshkeycallback(struct Curl_easy *easy, * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64. */ #ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE -#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y) +#define session_startup(x,y) libssh2_session_handshake(x, y) +#else +#define session_startup(x,y) libssh2_session_startup(x, (int)y) #endif static CURLcode ssh_knownhost(struct Curl_easy *data) @@ -495,7 +497,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) break; #endif default: - infof(data, "unsupported key type, can't check knownhosts!"); + infof(data, "unsupported key type, can't check knownhosts"); keybit = 0; break; } @@ -590,7 +592,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) LIBSSH2_KNOWNHOST_KEYENC_RAW| keybit, NULL); if(addrc) - infof(data, "Warning adding the known host %s failed!", + infof(data, "WARNING: adding the known host %s failed", conn->host.name); else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || rc == CURLKHSTAT_FINE_REPLACE) { @@ -601,7 +603,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) data->set.str[STRING_SSH_KNOWNHOSTS], LIBSSH2_KNOWNHOST_FILE_OPENSSH); if(wrc) { - infof(data, "Warning, writing %s failed!", + infof(data, "WARNING: writing %s failed", data->set.str[STRING_SSH_KNOWNHOSTS]); } } @@ -661,7 +663,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) /* The length of fingerprint is 32 bytes for SHA256. * See libssh2_hostkey_hash documentation. */ - if(Curl_base64_encode(data, fingerprint, 32, &fingerprint_b64, + if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64, &fingerprint_b64_len) != CURLE_OK) { state(data, SSH_SESSION_FREE); sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; @@ -692,12 +694,12 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) * against a known fingerprint, if available. */ if((pub_pos != b64_pos) || - Curl_strncasecompare(fingerprint_b64, pubkey_sha256, pub_pos) != 1) { + strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { free(fingerprint_b64); failf(data, - "Denied establishing ssh session: mismatch sha256 fingerprint. " - "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); + "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; @@ -705,7 +707,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) free(fingerprint_b64); - infof(data, "SHA256 checksum match!"); + infof(data, "SHA256 checksum match"); } if(pubkey_md5) { @@ -725,27 +727,24 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) 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, + /* This does NOT verify the length of 'pubkey_md5' separately, which will + make the comparison below fail unless it is exactly 32 characters */ + 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, + } + 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!"); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; } + infof(data, "MD5 checksum match"); } if(!pubkey_md5 && !pubkey_sha256) { @@ -932,7 +931,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) /* FALLTHROUGH */ case SSH_S_STARTUP: - rc = libssh2_session_startup(sshc->ssh_session, (int)sock); + rc = session_startup(sshc->ssh_session, sock); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } @@ -1468,7 +1467,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) */ cp = strchr(cmd, ' '); if(!cp) { - failf(data, "Syntax error command '%s'. Missing parameter!", + failf(data, "Syntax error command '%s', missing parameter", cmd); state(data, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; @@ -3227,7 +3226,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sshrecv.recvptr = ssh_tls_recv; sshsend.sendptr = ssh_tls_send; - infof(data, "Uses HTTPS proxy!"); + infof(data, "Uses HTTPS proxy"); /* Setup libssh2 callbacks to make it read/write TLS from the socket. diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c index 5b4cde9..85f2941 100644 --- a/lib/vssh/wolfssh.c +++ b/lib/vssh/wolfssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2019 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -469,7 +469,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_STOP); return CURLE_SSH; } - infof(data, "wolfssh connected!"); + infof(data, "wolfssh connected"); state(data, SSH_STOP); break; case SSH_STOP: @@ -490,7 +490,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) return CURLE_OK; } else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP connected!"); + infof(data, "wolfssh SFTP connected"); state(data, SSH_SFTP_REALPATH); } else { @@ -519,7 +519,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) else { memcpy(sshc->homedir, name->fName, name->fSz); sshc->homedir[name->fSz] = 0; - infof(data, "wolfssh SFTP realpath succeeded!"); + infof(data, "wolfssh SFTP realpath succeeded"); } wolfSSH_SFTPNAME_list_free(name); state(data, SSH_STOP); @@ -617,7 +617,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) return CURLE_OK; } else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP open succeeded!"); + infof(data, "wolfssh SFTP open succeeded"); } else { failf(data, "wolfssh SFTP upload open failed: %d", rc); @@ -728,7 +728,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) return CURLE_OK; } else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP open succeeded!"); + infof(data, "wolfssh SFTP open succeeded"); state(data, SSH_SFTP_DOWNLOAD_STAT); return CURLE_OK; } @@ -754,7 +754,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) return CURLE_OK; } else if(rc == WS_SUCCESS) { - infof(data, "wolfssh STAT succeeded!"); + infof(data, "wolfssh STAT succeeded"); } else { failf(data, "wolfssh SFTP open failed: %d", rc); diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 9b772d0..91f4416 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2021, Michael Forney, + * Copyright (C) 2019 - 2022, Michael Forney, * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,12 +35,15 @@ #include "multiif.h" #include "curl_printf.h" #include "curl_memory.h" +#include "strcase.h" struct x509_context { const br_x509_class *vtable; br_x509_minimal_context minimal; + br_x509_decoder_context decoder; bool verifyhost; bool verifypeer; + int cert_num; }; struct ssl_backend_data { @@ -159,6 +162,18 @@ static CURLcode load_cafile(struct cafile_source *source, if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) break; br_x509_decoder_init(&ca.xc, append_dn, &ca); + ca.in_cert = TRUE; + ca.dn_len = 0; + break; + case BR_PEM_END_OBJ: + if(!ca.in_cert) + break; + ca.in_cert = FALSE; + if(br_x509_decoder_last_error(&ca.xc)) { + ca.err = CURLE_SSL_CACERT_BADFILE; + goto fail; + } + /* add trust anchor */ if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { ca.err = CURLE_OUT_OF_MEMORY; goto fail; @@ -172,19 +187,8 @@ static CURLcode load_cafile(struct cafile_source *source, } ca.anchors = new_anchors; ca.anchors_len = new_anchors_len; - ca.in_cert = TRUE; - ca.dn_len = 0; ta = &ca.anchors[ca.anchors_len - 1]; ta->dn.data = NULL; - break; - case BR_PEM_END_OBJ: - if(!ca.in_cert) - break; - ca.in_cert = FALSE; - if(br_x509_decoder_last_error(&ca.xc)) { - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } ta->flags = 0; if(br_x509_decoder_isCA(&ca.xc)) ta->flags |= BR_X509_TA_CA; @@ -238,6 +242,8 @@ static CURLcode load_cafile(struct cafile_source *source, } while(source->type != CAFILE_SOURCE_BLOB); if(fp && ferror(fp)) ca.err = CURLE_READ_ERROR; + else if(ca.in_cert) + ca.err = CURLE_SSL_CACERT_BADFILE; fail: if(fp) @@ -260,6 +266,11 @@ static void x509_start_chain(const br_x509_class **ctx, { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + x509->cert_num = 0; + return; + } + if(!x509->verifyhost) server_name = NULL; x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); @@ -269,6 +280,13 @@ static void x509_start_cert(const br_x509_class **ctx, uint32_t length) { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + /* Only decode the first cert in the chain to obtain the public key */ + if(x509->cert_num == 0) + br_x509_decoder_init(&x509->decoder, NULL, NULL); + return; + } + x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); } @@ -277,6 +295,12 @@ static void x509_append(const br_x509_class **ctx, const unsigned char *buf, { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + if(x509->cert_num == 0) + br_x509_decoder_push(&x509->decoder, buf, len); + return; + } + x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); } @@ -284,21 +308,23 @@ static void x509_end_cert(const br_x509_class **ctx) { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + x509->cert_num++; + return; + } + x509->minimal.vtable->end_cert(&x509->minimal.vtable); } static unsigned x509_end_chain(const br_x509_class **ctx) { struct x509_context *x509 = (struct x509_context *)ctx; - unsigned err; - err = x509->minimal.vtable->end_chain(&x509->minimal.vtable); - if(err && !x509->verifypeer) { - /* ignore any X.509 errors */ - err = BR_ERR_OK; + if(!x509->verifypeer) { + return br_x509_decoder_last_error(&x509->decoder); } - return err; + return x509->minimal.vtable->end_chain(&x509->minimal.vtable); } static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, @@ -306,6 +332,15 @@ static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + /* Nothing in the chain is verified, just return the public key of the + first certificate and allow its usage for both TLS_RSA_* and + TLS_ECDHE_* */ + if(usages) + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; + return br_x509_decoder_get_pkey(&x509->decoder); + } + return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); } @@ -319,6 +354,216 @@ static const br_x509_class x509_vtable = { x509_get_pkey }; +struct st_cipher { + const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */ + const char *alias_name; /* Alias name is the same as OpenSSL cipher name */ + uint16_t num; /* BearSSL cipher suite */ +}; + +/* Macro to initialize st_cipher data structure */ +#define CIPHER_DEF(num, alias) { #num, alias, BR_##num } + +static const struct st_cipher ciphertable[] = { + /* RFC 2246 TLS 1.0 */ + CIPHER_DEF(TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ + "DES-CBC3-SHA"), + + /* RFC 3268 TLS 1.0 AES */ + CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ + "AES128-SHA"), + CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ + "AES256-SHA"), + + /* RFC 5246 TLS 1.2 */ + CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ + "AES128-SHA256"), + CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ + "AES256-SHA256"), + + /* RFC 5288 TLS 1.2 AES GCM */ + CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ + "AES128-GCM-SHA256"), + CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ + "AES256-GCM-SHA384"), + + /* RFC 4492 TLS 1.0 ECC */ + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ + "ECDH-ECDSA-DES-CBC3-SHA"), + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ + "ECDH-ECDSA-AES128-SHA"), + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ + "ECDH-ECDSA-AES256-SHA"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ + "ECDHE-ECDSA-DES-CBC3-SHA"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ + "ECDHE-ECDSA-AES128-SHA"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ + "ECDHE-ECDSA-AES256-SHA"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ + "ECDH-RSA-DES-CBC3-SHA"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ + "ECDH-RSA-AES128-SHA"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ + "ECDH-RSA-AES256-SHA"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ + "ECDHE-RSA-DES-CBC3-SHA"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ + "ECDHE-RSA-AES128-SHA"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ + "ECDHE-RSA-AES256-SHA"), + + /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */ + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ + "ECDHE-ECDSA-AES128-SHA256"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ + "ECDHE-ECDSA-AES256-SHA384"), + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ + "ECDH-ECDSA-AES128-SHA256"), + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ + "ECDH-ECDSA-AES256-SHA384"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ + "ECDHE-RSA-AES128-SHA256"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ + "ECDHE-RSA-AES256-SHA384"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ + "ECDH-RSA-AES128-SHA256"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ + "ECDH-RSA-AES256-SHA384"), + + /* RFC 5289 TLS 1.2 GCM */ + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ + "ECDHE-ECDSA-AES128-GCM-SHA256"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ + "ECDHE-ECDSA-AES256-GCM-SHA384"), + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ + "ECDH-ECDSA-AES128-GCM-SHA256"), + CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ + "ECDH-ECDSA-AES256-GCM-SHA384"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ + "ECDHE-RSA-AES128-GCM-SHA256"), + CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ + "ECDHE-RSA-AES256-GCM-SHA384"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ + "ECDH-RSA-AES128-GCM-SHA256"), + CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ + "ECDH-RSA-AES256-GCM-SHA384"), +#ifdef BR_TLS_RSA_WITH_AES_128_CCM + + /* RFC 6655 TLS 1.2 CCM + Supported since BearSSL 0.6 */ + CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */ + "AES128-CCM"), + CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */ + "AES256-CCM"), + CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */ + "AES128-CCM8"), + CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */ + "AES256-CCM8"), + + /* RFC 7251 TLS 1.2 ECC CCM + Supported since BearSSL 0.6 */ + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */ + "ECDHE-ECDSA-AES128-CCM"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */ + "ECDHE-ECDSA-AES256-CCM"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */ + "ECDHE-ECDSA-AES128-CCM8"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */ + "ECDHE-ECDSA-AES256-CCM8"), +#endif + + /* RFC 7905 TLS 1.2 ChaCha20-Poly1305 + Supported since BearSSL 0.2 */ + CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ + "ECDHE-RSA-CHACHA20-POLY1305"), + CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ + "ECDHE-ECDSA-CHACHA20-POLY1305"), +}; + +#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0])) +#define CIPHER_NAME_BUF_LEN 64 + +static bool is_separator(char c) +{ + /* Return whether character is a cipher list separator. */ + switch(c) { + case ' ': + case '\t': + case ':': + case ',': + case ';': + return true; + } + return false; +} + +static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data, + br_ssl_engine_context *ssl_eng, + const char *ciphers) +{ + uint16_t selected_ciphers[NUM_OF_CIPHERS]; + size_t selected_count = 0; + char cipher_name[CIPHER_NAME_BUF_LEN]; + const char *cipher_start = ciphers; + const char *cipher_end; + size_t i, j; + + if(!cipher_start) + return CURLE_SSL_CIPHER; + + while(true) { + /* Extract the next cipher name from the ciphers string */ + while(is_separator(*cipher_start)) + ++cipher_start; + if(*cipher_start == '\0') + break; + cipher_end = cipher_start; + while(*cipher_end != '\0' && !is_separator(*cipher_end)) + ++cipher_end; + j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ? + cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1; + strncpy(cipher_name, cipher_start, j); + cipher_name[j] = '\0'; + cipher_start = cipher_end; + + /* Lookup the cipher name in the table of available ciphers. If the cipher + name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try + to match cipher name by an (OpenSSL) alias. */ + if(strncasecompare(cipher_name, "TLS_", 4)) { + for(i = 0; i < NUM_OF_CIPHERS && + !strcasecompare(cipher_name, ciphertable[i].name); ++i); + } + else { + for(i = 0; i < NUM_OF_CIPHERS && + !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i); + } + if(i == NUM_OF_CIPHERS) { + infof(data, "BearSSL: unknown cipher in list: %s", cipher_name); + continue; + } + + /* No duplicates allowed */ + for(j = 0; j < selected_count && + selected_ciphers[j] != ciphertable[i].num; j++); + if(j < selected_count) { + infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name); + continue; + } + + DEBUGASSERT(selected_count < NUM_OF_CIPHERS); + selected_ciphers[selected_count] = ciphertable[i].num; + ++selected_count; + } + + if(selected_count == 0) { + failf(data, "BearSSL: no supported cipher in list"); + return CURLE_SSL_CIPHER; + } + + br_ssl_engine_set_suites(ssl_eng, selected_ciphers, selected_count); + return CURLE_OK; +} + static CURLcode bearssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, int sockindex) { @@ -339,6 +584,8 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, struct in_addr addr; #endif + DEBUGASSERT(backend); + switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_SSLv2: failf(data, "BearSSL does not support SSLv2"); @@ -410,6 +657,15 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, sizeof(backend->buf), 1); + if(SSL_CONN_CONFIG(cipher_list)) { + /* Override the ciphers as specified. For the default cipher list see the + BearSSL source code of br_ssl_client_init_full() */ + ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, + SSL_CONN_CONFIG(cipher_list)); + if(ret) + return ret; + } + /* initialize X.509 context */ backend->x509.vtable = &x509_vtable; backend->x509.verifypeer = verifypeer; @@ -442,12 +698,12 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, #endif ) { backend->protocols[cur++] = ALPN_H2; - infof(data, "ALPN, offering %s", ALPN_H2); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } #endif backend->protocols[cur++] = ALPN_HTTP_1_1; - infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols, cur); @@ -465,8 +721,28 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, } hostname = NULL; } + else { + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + hostname = snihost; + } - if(!br_ssl_client_reset(&backend->ctx, hostname, 0)) + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + Curl_set_in_callback(data, true); + ret = (*data->set.ssl.fsslctx)(data, &backend->ctx, + data->set.ssl.fsslctxp); + Curl_set_in_callback(data, false); + if(ret) { + failf(data, "BearSSL: error signaled by ssl ctx callback"); + return ret; + } + } + + if(!br_ssl_client_reset(&backend->ctx, hostname, 1)) return CURLE_FAILED_INIT; backend->active = TRUE; @@ -488,6 +764,8 @@ static CURLcode bearssl_run_until(struct Curl_easy *data, ssize_t ret; int err; + DEBUGASSERT(backend); + for(;;) { state = br_ssl_engine_current_state(&backend->ctx.eng); if(state & BR_SSL_CLOSED) { @@ -560,6 +838,8 @@ static CURLcode bearssl_connect_step2(struct Curl_easy *data, struct ssl_backend_data *backend = connssl->backend; CURLcode ret; + DEBUGASSERT(backend); + ret = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) @@ -582,13 +862,14 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data, CURLcode ret; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); if(conn->bits.tls_enable_alpn) { const char *protocol; protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); if(protocol) { - infof(data, "ALPN, server accepted to use %s", protocol); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol); #ifdef USE_HTTP2 if(!strcmp(protocol, ALPN_H2)) @@ -603,7 +884,7 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data, BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } else - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); } if(SSL_SET_OPTION(primary.sessionid)) { @@ -647,6 +928,8 @@ static ssize_t bearssl_send(struct Curl_easy *data, int sockindex, unsigned char *app; size_t applen; + DEBUGASSERT(backend); + for(;;) { *err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP); if (*err != CURLE_OK) @@ -680,6 +963,8 @@ static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex, unsigned char *app; size_t applen; + DEBUGASSERT(backend); + *err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP); if(*err != CURLE_OK) return -1; @@ -805,6 +1090,7 @@ static bool bearssl_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; } @@ -854,6 +1140,7 @@ static void *bearssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return &backend->ctx; } @@ -864,6 +1151,8 @@ static void bearssl_close(struct Curl_easy *data, struct ssl_backend_data *backend = connssl->backend; size_t i; + DEBUGASSERT(backend); + if(backend->active) { br_ssl_engine_close(&backend->ctx.eng); (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); @@ -893,7 +1182,7 @@ static CURLcode bearssl_sha256sum(const unsigned char *input, const struct Curl_ssl Curl_ssl_bearssl = { { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ - SSLSUPP_CAINFO_BLOB, + SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX, sizeof(struct ssl_backend_data), Curl_none_init, /* init */ diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c index e451f6a..9b5fbe4 100644 --- a/lib/vtls/gskit.c +++ b/lib/vtls/gskit.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,6 +28,7 @@ #include #undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ #include "socketpair.h" +#include "strerror.h" /* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ #ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST @@ -73,6 +74,7 @@ #include "connect.h" /* for the connect timeout */ #include "select.h" #include "strcase.h" +#include "timediff.h" #include "x509asn1.h" #include "curl_printf.h" @@ -247,10 +249,10 @@ static CURLcode set_enum(struct Curl_easy *data, gsk_handle h, static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h, - GSK_BUF_ID id, const char *buffer, bool unsupported_ok) + GSK_BUF_ID id, const char *buf, bool unsupported_ok) { char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_buffer(h, id, buffer, 0); + int rc = gsk_attribute_set_buffer(h, id, buf, 0); switch(rc) { case GSK_OK: @@ -448,8 +450,7 @@ static CURLcode set_ciphers(struct Curl_easy *data, static int gskit_init(void) { - /* No initialisation needed. */ - + /* No initialization needed. */ return 1; } @@ -513,6 +514,8 @@ static void cancel_async_handshake(struct connectdata *conn, int sockindex) struct ssl_connect_data *connssl = &conn->ssl[sockindex]; Qso_OverlappedIO_t cstat; + DEBUGASSERT(BACKEND); + if(QsoCancelOperation(conn->sock[sockindex], 0) > 0) QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); } @@ -520,6 +523,7 @@ static void cancel_async_handshake(struct connectdata *conn, int sockindex) static void close_async_handshake(struct ssl_connect_data *connssl) { + DEBUGASSERT(BACKEND); QsoDestroyIOCompletionPort(BACKEND->iocport); BACKEND->iocport = -1; } @@ -530,36 +534,36 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, #ifndef CURL_DISABLE_PROXY struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_connect_data *connproxyssl = &conn->proxy_ssl[sockindex]; - fd_set fds_read; - fd_set fds_write; + struct pollfd fds[2]; int n; int m; int i; int ret = 0; char buf[CURL_MAX_WRITE_SIZE]; + DEBUGASSERT(BACKEND); + DEBUGASSERT(connproxyssl->backend); + if(!connssl->use || !connproxyssl->use) return 0; /* No SSL over SSL: OK. */ - FD_ZERO(&fds_read); - FD_ZERO(&fds_write); - n = -1; + n = 1; + fds[0].fd = BACKEND->remotefd; + fds[1].fd = conn->sock[sockindex]; + if(directions & SOS_READ) { - FD_SET(BACKEND->remotefd, &fds_write); - n = BACKEND->remotefd; + fds[0].events |= POLLOUT; } if(directions & SOS_WRITE) { - FD_SET(BACKEND->remotefd, &fds_read); - n = BACKEND->remotefd; - FD_SET(conn->sock[sockindex], &fds_write); - if(n < conn->sock[sockindex]) - n = conn->sock[sockindex]; + n = 2; + fds[0].events |= POLLIN; + fds[1].events |= POLLOUT; } - i = Curl_select(n + 1, &fds_read, &fds_write, NULL, 0); + i = Curl_poll(fds, n, 0); if(i < 0) return -1; /* Select error. */ - if(FD_ISSET(BACKEND->remotefd, &fds_write)) { + if(fds[0].revents & POLLOUT) { /* Try getting data from HTTPS proxy and pipe it upstream. */ n = 0; i = gsk_secure_soc_read(connproxyssl->backend->handle, @@ -581,8 +585,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, } } - if(FD_ISSET(BACKEND->remotefd, &fds_read) && - FD_ISSET(conn->sock[sockindex], &fds_write)) { + if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) { /* Pipe data to HTTPS proxy. */ n = read(BACKEND->remotefd, buf, sizeof(buf)); if(n < 0) @@ -605,6 +608,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, static void close_one(struct ssl_connect_data *connssl, struct Curl_easy *data, struct connectdata *conn, int sockindex) { + DEBUGASSERT(BACKEND); if(BACKEND->handle) { gskit_status(data, gsk_secure_soc_close(&BACKEND->handle), "gsk_secure_soc_close()", 0); @@ -636,6 +640,8 @@ static ssize_t gskit_send(struct Curl_easy *data, int sockindex, CURLcode cc = CURLE_SEND_ERROR; int written; + DEBUGASSERT(BACKEND); + if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) >= 0) { cc = gskit_status(data, gsk_secure_soc_write(BACKEND->handle, @@ -661,6 +667,8 @@ static ssize_t gskit_recv(struct Curl_easy *data, int num, char *buf, int nread; CURLcode cc = CURLE_RECV_ERROR; + DEBUGASSERT(BACKEND); + if(pipe_ssloverssl(conn, num, SOS_READ) >= 0) { int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle, @@ -734,6 +742,7 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data, #endif /* Create SSL environment, start (preferably asynchronous) handshake. */ + DEBUGASSERT(BACKEND); BACKEND->handle = (gsk_handle) NULL; BACKEND->iocport = -1; @@ -830,8 +839,13 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data, /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ if(sni) { + char *snihost = Curl_ssl_snihost(data, sni, NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } result = set_buffer(data, BACKEND->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE); + GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE); if(result == CURLE_UNSUPPORTED_PROTOCOL) result = CURLE_OK; } @@ -958,14 +972,16 @@ static CURLcode gskit_connect_step2(struct Curl_easy *data, CURLcode result; /* Poll or wait for end of SSL asynchronous handshake. */ + DEBUGASSERT(BACKEND); for(;;) { timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); + stmv.tv_sec = 0; + stmv.tv_usec = 0; if(timeout_ms < 0) timeout_ms = 0; - stmv.tv_sec = timeout_ms / 1000; - stmv.tv_usec = (timeout_ms - stmv.tv_sec * 1000) * 1000; - switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, &stmv)) { + switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, + curlx_mstotv(&stmv, timeout_ms))) { case 1: /* Operation complete. */ break; case -1: /* An error occurred: handshake still in progress. */ @@ -1014,6 +1030,7 @@ static CURLcode gskit_connect_step3(struct Curl_easy *data, CURLcode result; /* SSL handshake done: gather certificate info and verify host. */ + DEBUGASSERT(BACKEND); if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle, GSK_PARTNER_CERT_INFO, @@ -1070,15 +1087,16 @@ static CURLcode gskit_connect_step3(struct Curl_easy *data, /* Check pinned public key. */ ptr = SSL_PINNED_PUB_KEY(); if(!result && ptr) { - curl_X509certificate x509; - curl_asn1Element *p; + struct Curl_X509certificate x509; + struct Curl_asn1Element *p; + memset(&x509, 0, sizeof(x509)); if(Curl_parseX509(&x509, cert, certend)) return CURLE_SSL_PINNEDPUBKEYNOTMATCH; p = &x509.subjectPublicKeyInfo; result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header); if(result) { - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); return result; } } @@ -1205,6 +1223,8 @@ static int gskit_shutdown(struct Curl_easy *data, char buf[120]; int loop = 10; /* don't get stuck */ + DEBUGASSERT(BACKEND); + if(!BACKEND->handle) return 0; @@ -1268,6 +1288,7 @@ static int gskit_check_cxn(struct connectdata *cxn) int errlen; /* The only thing that can be tested here is at the socket level. */ + DEBUGASSERT(BACKEND); if(!BACKEND->handle) return 0; /* connection has been closed */ @@ -1287,6 +1308,7 @@ static void *gskit_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { (void)info; + DEBUGASSERT(BACKEND); return BACKEND->handle; } @@ -1308,6 +1330,7 @@ const struct Curl_ssl Curl_ssl_gskit = { Curl_none_cert_status_request, /* cert_status_request */ gskit_connect, /* connect */ gskit_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_getsock, /* getsock */ gskit_get_internals, /* get_internals */ gskit_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 18864aa..0535011 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,6 +55,14 @@ /* The last #include file should be: */ #include "memdebug.h" +#ifdef HAVE_GNUTLS_SRP +/* the function exists */ +#ifdef USE_TLS_SRP +/* the functionality is not disabled */ +#define USE_GNUTLS_SRP +#endif +#endif + /* Enable GnuTLS debugging by defining GTLSDEBUG */ /*#define GTLSDEBUG */ @@ -75,7 +83,7 @@ static bool gtls_inited = FALSE; struct ssl_backend_data { gnutls_session_t session; gnutls_certificate_credentials_t cred; -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP gnutls_srp_client_credentials_t srp_client_cred; #endif }; @@ -202,9 +210,12 @@ static CURLcode handshake(struct Curl_easy *data, { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session = backend->session; + gnutls_session_t session; curl_socket_t sockfd = conn->sock[sockindex]; + DEBUGASSERT(backend); + session = backend->session; + for(;;) { timediff_t timeout_ms; int rc; @@ -406,6 +417,8 @@ gtls_connect_step1(struct Curl_easy *data, const char *tls13support; CURLcode result; + DEBUGASSERT(backend); + if(connssl->state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ @@ -431,12 +444,12 @@ gtls_connect_step1(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } -#ifdef HAVE_GNUTLS_SRP - if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) { +#ifdef USE_GNUTLS_SRP + if((SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) && + Curl_allow_auth_to_host(data)) { infof(data, "Using TLS-SRP username: %s", SSL_SET_OPTION(username)); - rc = gnutls_srp_allocate_client_credentials( - &backend->srp_client_cred); + rc = gnutls_srp_allocate_client_credentials(&backend->srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_allocate_client_cred() failed: %s", gnutls_strerror(rc)); @@ -542,11 +555,15 @@ gtls_connect_step1(struct Curl_easy *data, #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && #endif - sni && - (gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname, - strlen(hostname)) < 0)) - infof(data, "WARNING: failed to configure server name indication (SNI) " - "TLS extension"); + sni) { + size_t snilen; + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); + if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost, + snilen) < 0) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + } /* Use default priorities */ rc = gnutls_set_default_priority(session); @@ -578,7 +595,7 @@ gtls_connect_step1(struct Curl_easy *data, if(result) return result; -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP /* Only add SRP to the cipher list if SRP is requested. Otherwise * GnuTLS will disable TLS 1.3 support. */ if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) { @@ -600,7 +617,7 @@ gtls_connect_step1(struct Curl_easy *data, #endif infof(data, "GnuTLS ciphers: %s", prioritylist); rc = gnutls_priority_set_direct(session, prioritylist, &err); -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP } #endif @@ -623,14 +640,14 @@ gtls_connect_step1(struct Curl_easy *data, protocols[cur].data = (unsigned char *)ALPN_H2; protocols[cur].size = ALPN_H2_LENGTH; cur++; - infof(data, "ALPN, offering %.*s", ALPN_H2_LENGTH, ALPN_H2); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } #endif protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1; protocols[cur].size = ALPN_HTTP_1_1_LENGTH; cur++; - infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) { failf(data, "failed setting ALPN"); @@ -674,7 +691,7 @@ gtls_connect_step1(struct Curl_easy *data, } } -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP /* put the credentials to the current session */ if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) { rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP, @@ -697,7 +714,10 @@ gtls_connect_step1(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { - transport_ptr = conn->proxy_ssl[sockindex].backend->session; + struct ssl_backend_data *proxy_backend; + proxy_backend = conn->proxy_ssl[sockindex].backend; + DEBUGASSERT(proxy_backend); + transport_ptr = proxy_backend->session; gnutls_transport_push = gtls_push_ssl; gnutls_transport_pull = gtls_pull_ssl; } @@ -854,7 +874,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, if(SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost) || SSL_CONN_CONFIG(issuercert)) { -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP && SSL_SET_OPTION(username) != NULL && !SSL_CONN_CONFIG(verifypeer) @@ -867,7 +887,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, failf(data, "failed to get server cert"); *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; return CURLE_PEER_FAILED_VERIFICATION; -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP } #endif } @@ -1186,7 +1206,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, if(ptr) { result = pkp_pin_peer_pubkey(data, x509_cert, ptr); if(result != CURLE_OK) { - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); gnutls_x509_crt_deinit(x509_cert); return result; } @@ -1243,8 +1263,8 @@ Curl_gtls_verifyserver(struct Curl_easy *data, if(conn->bits.tls_enable_alpn) { rc = gnutls_alpn_get_selected_protocol(session, &proto); if(rc == 0) { - infof(data, "ALPN, server accepted to use %.*s", proto.size, - proto.data); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size, + proto.data); #ifdef USE_HTTP2 if(proto.size == ALPN_H2_LENGTH && @@ -1260,7 +1280,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, } } else - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); @@ -1352,7 +1372,9 @@ gtls_connect_common(struct Curl_easy *data, /* Finish connecting once the handshake is done */ if(ssl_connect_1 == connssl->connecting_state) { struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session = backend->session; + gnutls_session_t session; + DEBUGASSERT(backend); + session = backend->session; rc = Curl_gtls_verifyserver(data, conn, session, sockindex); if(rc) return rc; @@ -1393,6 +1415,9 @@ static bool gtls_data_pending(const struct connectdata *conn, const struct ssl_connect_data *connssl = &conn->ssl[connindex]; bool res = FALSE; struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + if(backend->session && 0 != gnutls_record_check_pending(backend->session)) res = TRUE; @@ -1400,6 +1425,7 @@ static bool gtls_data_pending(const struct connectdata *conn, #ifndef CURL_DISABLE_PROXY connssl = &conn->proxy_ssl[connindex]; backend = connssl->backend; + DEBUGASSERT(backend); if(backend->session && 0 != gnutls_record_check_pending(backend->session)) res = TRUE; @@ -1417,7 +1443,10 @@ static ssize_t gtls_send(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - ssize_t rc = gnutls_record_send(backend->session, mem, len); + ssize_t rc; + + DEBUGASSERT(backend); + rc = gnutls_record_send(backend->session, mem, len); if(rc < 0) { *curlcode = (rc == GNUTLS_E_AGAIN) @@ -1433,6 +1462,8 @@ static ssize_t gtls_send(struct Curl_easy *data, static void close_one(struct ssl_connect_data *connssl) { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(backend->session) { char buf[32]; /* Maybe the server has already sent a close notify alert. @@ -1446,7 +1477,7 @@ static void close_one(struct ssl_connect_data *connssl) gnutls_certificate_free_credentials(backend->cred); backend->cred = NULL; } -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP if(backend->srp_client_cred) { gnutls_srp_free_client_credentials(backend->srp_client_cred); backend->srp_client_cred = NULL; @@ -1475,6 +1506,8 @@ static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; int retval = 0; + DEBUGASSERT(backend); + #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in @@ -1530,7 +1563,7 @@ static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn, } gnutls_certificate_free_credentials(backend->cred); -#ifdef HAVE_GNUTLS_SRP +#ifdef USE_GNUTLS_SRP if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP && SSL_SET_OPTION(username) != NULL) gnutls_srp_free_client_credentials(backend->srp_client_cred); @@ -1553,6 +1586,8 @@ static ssize_t gtls_recv(struct Curl_easy *data, /* connection data */ struct ssl_backend_data *backend = connssl->backend; ssize_t ret; + DEBUGASSERT(backend); + ret = gnutls_record_recv(backend->session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; @@ -1624,6 +1659,7 @@ static void *gtls_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->session; } diff --git a/lib/vtls/hostcheck.c b/lib/vtls/hostcheck.c new file mode 100644 index 0000000..8dc97a2 --- /dev/null +++ b/lib/vtls/hostcheck.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_OPENSSL) \ + || defined(USE_GSKIT) \ + || defined(USE_SCHANNEL) +/* these backends use functions from this file */ + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#include "curl_memrchr.h" + +#include "hostcheck.h" +#include "strcase.h" +#include "hostip.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* check the two input strings with given length, but do not + assume they end in nul-bytes */ +static bool pmatch(const char *hostname, size_t hostlen, + const char *pattern, size_t patternlen) +{ + if(hostlen != patternlen) + return FALSE; + return strncasecompare(hostname, pattern, hostlen); +} + +/* + * Match a hostname against a wildcard pattern. + * E.g. + * "foo.host.com" matches "*.host.com". + * + * We use the matching rule described in RFC6125, section 6.4.3. + * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 + * + * In addition: ignore trailing dots in the host names and wildcards, so that + * the names are used normalized. This is what the browsers do. + * + * Do not allow wildcard matching on IP numbers. There are apparently + * certificates being used with an IP address in the CN field, thus making no + * apparent distinction between a name and an IP. We need to detect the use of + * an IP address and not wildcard match on such names. + * + * Return TRUE on a match. FALSE if not. + */ + +static bool hostmatch(const char *hostname, + size_t hostlen, + const char *pattern, + size_t patternlen) +{ + const char *pattern_label_end, *wildcard, *hostname_label_end; + size_t prefixlen, suffixlen; + + /* normalize pattern and hostname by stripping off trailing dots */ + DEBUGASSERT(patternlen); + if(hostname[hostlen-1]=='.') + hostlen--; + if(pattern[patternlen-1]=='.') + patternlen--; + + wildcard = memchr(pattern, '*', patternlen); + if(!wildcard) + return pmatch(hostname, hostlen, pattern, patternlen); + + /* detect IP address as hostname and fail the match if so */ + if(Curl_host_is_ipnum(hostname)) + return FALSE; + + /* We require at least 2 dots in the pattern to avoid too wide wildcard + match. */ + pattern_label_end = memchr(pattern, '.', patternlen); + if(!pattern_label_end || + (memrchr(pattern, '.', patternlen) == pattern_label_end) || + strncasecompare(pattern, "xn--", 4)) + return pmatch(hostname, hostlen, pattern, patternlen); + + hostname_label_end = memchr(hostname, '.', hostlen); + if(!hostname_label_end) + return FALSE; + else { + size_t skiphost = hostname_label_end - hostname; + size_t skiplen = pattern_label_end - pattern; + if(!pmatch(hostname_label_end, hostlen - skiphost, + pattern_label_end, patternlen - skiplen)) + return FALSE; + } + /* The wildcard must match at least one character, so the left-most + label of the hostname is at least as large as the left-most label + of the pattern. */ + if(hostname_label_end - hostname < pattern_label_end - pattern) + return FALSE; + + prefixlen = wildcard - pattern; + suffixlen = pattern_label_end - (wildcard + 1); + return strncasecompare(pattern, hostname, prefixlen) && + strncasecompare(wildcard + 1, hostname_label_end - suffixlen, + suffixlen) ? TRUE : FALSE; +} + +/* + * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not. + */ +bool Curl_cert_hostcheck(const char *match, size_t matchlen, + const char *hostname, size_t hostlen) +{ + if(match && *match && hostname && *hostname) + return hostmatch(hostname, hostlen, match, matchlen); + return FALSE; +} + +#endif /* OPENSSL, GSKIT or schannel+wince */ diff --git a/lib/vtls/hostcheck.h b/lib/vtls/hostcheck.h new file mode 100644 index 0000000..aa96640 --- /dev/null +++ b/lib/vtls/hostcheck.h @@ -0,0 +1,31 @@ +#ifndef HEADER_CURL_HOSTCHECK_H +#define HEADER_CURL_HOSTCHECK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include + +/* returns TRUE if there's a match */ +bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, + const char *hostname, size_t hostlen); + +#endif /* HEADER_CURL_HOSTCHECK_H */ diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 1d209b2..64f57c5 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -41,9 +41,6 @@ #include #endif #include -#if MBEDTLS_VERSION_NUMBER < 0x03000000 -#include -#endif #include #include @@ -73,17 +70,28 @@ #include "curl_memory.h" #include "memdebug.h" +/* ALPN for http2 */ +#ifdef USE_HTTP2 +# undef HAS_ALPN +# ifdef MBEDTLS_SSL_ALPN +# define HAS_ALPN +# endif +#endif + struct ssl_backend_data { mbedtls_ctr_drbg_context ctr_drbg; mbedtls_entropy_context entropy; mbedtls_ssl_context ssl; - int server_fd; mbedtls_x509_crt cacert; mbedtls_x509_crt clicert; +#ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl crl; +#endif mbedtls_pk_context pk; mbedtls_ssl_config config; +#ifdef HAS_ALPN const char *protocols[3]; +#endif }; /* apply threading? */ @@ -145,15 +153,6 @@ static void mbed_debug(void *context, int level, const char *f_name, #else #endif -/* ALPN for http2? */ -#ifdef USE_NGHTTP2 -# undef HAS_ALPN -# ifdef MBEDTLS_SSL_ALPN -# define HAS_ALPN -# endif -#endif - - /* * profile */ @@ -231,6 +230,8 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, long ssl_version_max = SSL_CONN_CONFIG(version_max); CURLcode result = CURLE_OK; + DEBUGASSERT(backend); + switch(ssl_version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: @@ -286,6 +287,8 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, int ret = -1; char errorbuf[128]; + DEBUGASSERT(backend); + if((SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) || (SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3)) { failf(data, "Not supported SSL version"); @@ -335,11 +338,12 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", -ret, errorbuf); - return ret; + return CURLE_SSL_CERTPROBLEM; } } if(ssl_cafile && verifypeer) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); if(ret<0) { @@ -348,9 +352,14 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, ssl_cafile, -ret, errorbuf); return CURLE_SSL_CACERT_BADFILE; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } if(ssl_capath) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); if(ret<0) { @@ -361,12 +370,17 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, if(verifypeer) return CURLE_SSL_CACERT_BADFILE; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } /* Load the client certificate */ mbedtls_x509_crt_init(&backend->clicert); if(ssl_cert) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert); if(ret) { @@ -376,6 +390,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CERTPROBLEM; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } if(ssl_cert_blob) { @@ -388,7 +406,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, 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); + ssl_cert_blob->len + 1); free(newblob); if(ret) { @@ -404,6 +422,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { if(SSL_SET_OPTION(key)) { +#ifdef MBEDTLS_FS_IO #if MBEDTLS_VERSION_NUMBER >= 0x03000000 ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), SSL_SET_OPTION(key_passwd), @@ -420,6 +439,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, SSL_SET_OPTION(key), -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } else { const struct curl_blob *ssl_key_blob = SSL_SET_OPTION(key_blob); @@ -452,9 +475,11 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, } /* Load the CRL */ +#ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_init(&backend->crl); if(ssl_crlfile) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); if(ret) { @@ -464,17 +489,21 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CRL_BADFILE; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } +#else + if(ssl_crlfile) { + failf(data, "mbedtls: crl support not built in"); + return CURLE_NOT_BUILT_IN; } +#endif infof(data, "mbedTLS: Connecting to %s:%ld", hostname, port); mbedtls_ssl_config_init(&backend->config); - - mbedtls_ssl_init(&backend->ssl); - if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { - failf(data, "mbedTLS: ssl_init failed"); - return CURLE_SSL_CONNECT_ERROR; - } ret = mbedtls_ssl_config_defaults(&backend->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, @@ -484,6 +513,12 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } + mbedtls_ssl_init(&backend->ssl); + if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { + failf(data, "mbedTLS: ssl_init failed"); + return CURLE_SSL_CONNECT_ERROR; + } + /* new profile with RSA min key len = 1024 ... */ mbedtls_ssl_conf_cert_profile(&backend->config, &mbedtls_x509_crt_profile_fr); @@ -555,26 +590,33 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, mbedtls_ssl_conf_ca_chain(&backend->config, &backend->cacert, +#ifdef MBEDTLS_X509_CRL_PARSE_C &backend->crl); +#else + NULL); +#endif if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, &backend->pk); } - if(mbedtls_ssl_set_hostname(&backend->ssl, hostname)) { - /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks *and* - the name to set in the SNI extension. So even if curl connects to a - host specified as an IP address, this function must be used. */ - failf(data, "couldn't set hostname in mbedTLS"); - return CURLE_SSL_CONNECT_ERROR; + { + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) { + /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and + the name to set in the SNI extension. So even if curl connects to a + host specified as an IP address, this function must be used. */ + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } } #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { const char **p = &backend->protocols[0]; -#ifdef USE_NGHTTP2 +#ifdef USE_HTTP2 if(data->state.httpwant >= CURL_HTTP_VERSION_2) - *p++ = NGHTTP2_PROTO_VERSION_ID; + *p++ = ALPN_H2; #endif *p++ = ALPN_HTTP_1_1; *p = NULL; @@ -586,7 +628,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } for(p = &backend->protocols[0]; *p; ++p) - infof(data, "ALPN, offering %s", *p); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p); } #endif @@ -627,6 +669,8 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, const mbedtls_x509_crt *peercert; const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); + DEBUGASSERT(backend); + conn->recv[sockindex] = mbed_recv; conn->send[sockindex] = mbed_send; @@ -769,11 +813,10 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl); if(next_protocol) { - infof(data, "ALPN, server accepted to use %s", next_protocol); -#ifdef USE_NGHTTP2 - if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID, - NGHTTP2_PROTO_VERSION_ID_LEN) && - !next_protocol[NGHTTP2_PROTO_VERSION_ID_LEN]) { + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol); +#ifdef USE_HTTP2 + if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LEN) && + !next_protocol[ALPN_H2_LEN]) { conn->negnpn = CURL_HTTP_VERSION_2; } else @@ -784,7 +827,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, } } else { - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); } Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); @@ -806,6 +849,7 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); if(SSL_SET_OPTION(primary.sessionid)) { int ret; @@ -862,6 +906,8 @@ static ssize_t mbed_send(struct Curl_easy *data, int sockindex, struct ssl_backend_data *backend = connssl->backend; int ret = -1; + DEBUGASSERT(backend); + ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { @@ -886,6 +932,8 @@ static void mbedtls_close(struct Curl_easy *data, char buf[32]; (void) data; + DEBUGASSERT(backend); + /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf)); @@ -893,7 +941,9 @@ static void mbedtls_close(struct Curl_easy *data, mbedtls_pk_free(&backend->pk); mbedtls_x509_crt_free(&backend->clicert); mbedtls_x509_crt_free(&backend->cacert); +#ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_free(&backend->crl); +#endif mbedtls_ssl_config_free(&backend->config); mbedtls_ssl_free(&backend->ssl); mbedtls_ctr_drbg_free(&backend->ctr_drbg); @@ -912,6 +962,8 @@ static ssize_t mbed_recv(struct Curl_easy *data, int num, int ret = -1; ssize_t len = -1; + DEBUGASSERT(backend); + ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, buffersize); @@ -1146,6 +1198,7 @@ static bool mbedtls_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; } @@ -1175,6 +1228,7 @@ static void *mbedtls_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return &backend->ssl; } diff --git a/lib/vtls/mesalink.c b/lib/vtls/mesalink.c deleted file mode 100644 index 35a9165..0000000 --- a/lib/vtls/mesalink.c +++ /dev/null @@ -1,679 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017 - 2018, Yiming Jing, - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* - * Source file for all MesaLink-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - */ - -/* - * Based upon the CyaSSL implementation in cyassl.c and cyassl.h: - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. - * - * Thanks for code and inspiration! - */ - -#include "curl_setup.h" - -#ifdef USE_MESALINK - -#include -#include - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "vtls.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "x509asn1.h" -#include "curl_printf.h" - -#include "mesalink.h" -#include -#include - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define MESALINK_MAX_ERROR_SZ 80 - -struct ssl_backend_data -{ - SSL_CTX *ctx; - SSL *handle; -}; - -static Curl_recv mesalink_recv; -static Curl_send mesalink_send; - -static int do_file_type(const char *type) -{ - if(!type || !type[0]) - return SSL_FILETYPE_PEM; - if(strcasecompare(type, "PEM")) - return SSL_FILETYPE_PEM; - if(strcasecompare(type, "DER")) - return SSL_FILETYPE_ASN1; - return -1; -} - -/* - * This function loads all the client/CA certificates and CRLs. Setup the TLS - * layer and do all necessary magic. - */ -static CURLcode -mesalink_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - char *ciphers; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct in_addr addr4; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#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; - - if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { - failf(data, "MesaLink does not support to set maximum SSL/TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - failf(data, "MesaLink does not support SSL 3.0, TLS 1.0, or TLS 1.1"); - return CURLE_NOT_BUILT_IN; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1_2: - req_method = TLSv1_2_client_method(); - break; - case CURL_SSLVERSION_TLSv1_3: - req_method = TLSv1_3_client_method(); - break; - case CURL_SSLVERSION_SSLv2: - failf(data, "MesaLink does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(!req_method) { - failf(data, "SSL: couldn't create a method!"); - return CURLE_OUT_OF_MEMORY; - } - - if(backend->ctx) - SSL_CTX_free(backend->ctx); - backend->ctx = SSL_CTX_new(req_method); - - 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) ? - 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_CONN_CONFIG(verifypeer)) { - failf(data, - "error setting certificate verify locations: " - " CAfile: %s CApath: %s", - SSL_CONN_CONFIG(CAfile) ? - SSL_CONN_CONFIG(CAfile) : "none", - SSL_CONN_CONFIG(CApath) ? - SSL_CONN_CONFIG(CApath) : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - infof(data, - "error setting certificate verify locations," - " continuing anyway:"); - } - else { - infof(data, "successfully set certificate verify locations:"); - } - infof(data, " CAfile: %s", - SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none"); - infof(data, " CApath: %s", - SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none"); - } - - 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, - SSL_SET_OPTION(primary.clientcert), - file_type) != 1) { - failf(data, "unable to use client certificate (no key or wrong pass" - " phrase?)"); - return CURLE_SSL_CONNECT_ERROR; - } - - 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) { - failf(data, "unable to set private key"); - return CURLE_SSL_CONNECT_ERROR; - } - infof(data, - "client cert: %s", - SSL_CONN_CONFIG(clientcert)? - SSL_CONN_CONFIG(clientcert): "none"); - } - - ciphers = SSL_CONN_CONFIG(cipher_list); - if(ciphers) { -#ifdef MESALINK_HAVE_CIPHER - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - return CURLE_SSL_CIPHER; - } -#endif - infof(data, "Cipher selection: %s", ciphers); - } - - 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; - } - - if((hostname_len < USHRT_MAX) && - (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) -#ifdef ENABLE_IPV6 - && (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) -#endif - ) { - /* hostname is not a valid IP address */ - if(SSL_set_tlsext_host_name(backend->handle, hostname) != SSL_SUCCESS) { - failf(data, - "WARNING: failed to configure server name indication (SNI) " - "TLS extension\n"); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { -#ifdef CURLDEBUG - /* Check if the hostname is 127.0.0.1 or [::1]; - * otherwise reject because MesaLink always wants a valid DNS Name - * specified in RFC 5280 Section 7.2 */ - if(strncmp(hostname, "127.0.0.1", 9) == 0 -#ifdef ENABLE_IPV6 - || strncmp(hostname, "[::1]", 5) == 0 -#endif - ) { - SSL_set_tlsext_host_name(backend->handle, "localhost"); - } - else -#endif - { - failf(data, - "ERROR: MesaLink does not accept an IP address as a hostname\n"); - return CURLE_SSL_CONNECT_ERROR; - } - } - -#ifdef MESALINK_HAVE_SESSION - if(SSL_SET_OPTION(primary.sessionid)) { - void *ssl_sessionid = NULL; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { - /* we got a session id, use it! */ - 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)); - return CURLE_SSL_CONNECT_ERROR; - } - /* Informational message */ - infof(data, "SSL re-using session ID"); - } - Curl_ssl_sessionid_unlock(data); - } -#endif /* MESALINK_HAVE_SESSION */ - - if(SSL_set_fd(backend->handle, (int)sockfd) != SSL_SUCCESS) { - failf(data, "SSL: SSL_set_fd failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; -} - -static CURLcode -mesalink_connect_step2(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - 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); - if(ret != SSL_SUCCESS) { - 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; - return CURLE_OK; - } - else { - char error_buffer[MESALINK_MAX_ERROR_SZ]; - failf(data, - "SSL_connect failed with error %d: %s", - detail, - ERR_error_string_n(detail, error_buffer, sizeof(error_buffer))); - ERR_print_errors_fp(stderr); - if(detail && SSL_CONN_CONFIG(verifypeer)) { - detail &= ~0xFF; - if(detail == TLS_ERROR_WEBPKI_ERRORS) { - failf(data, "Cert verify failed"); - return CURLE_PEER_FAILED_VERIFICATION; - } - } - return CURLE_SSL_CONNECT_ERROR; - } - } - - connssl->connecting_state = ssl_connect_3; - infof(data, - "SSL connection using %s / %s", - SSL_get_version(backend->handle), - SSL_get_cipher_name(backend->handle)); - - return CURLE_OK; -} - -static CURLcode -mesalink_connect_step3(struct connectdata *conn, int sockindex) -{ - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - -#ifdef MESALINK_HAVE_SESSION - if(SSL_SET_OPTION(primary.sessionid)) { - bool incache; - 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); - - Curl_ssl_sessionid_lock(data); - incache = - !(Curl_ssl_getsessionid(data, conn, isproxy, &old_ssl_sessionid, NULL, - sockindex)); - if(incache) { - if(old_ssl_sessionid != our_ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } - } - - if(!incache) { - result = - Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, 0, - sockindex, NULL); - if(result) { - Curl_ssl_sessionid_unlock(data); - failf(data, "failed to store ssl session"); - return result; - } - } - Curl_ssl_sessionid_unlock(data); - } -#endif /* MESALINK_HAVE_SESSION */ - - connssl->connecting_state = ssl_connect_done; - - return result; -} - -static ssize_t -mesalink_send(struct Curl_easy *data, int sockindex, const void *mem, - size_t len, CURLcode *curlcode) -{ - 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); - - if(rc < 0) { - int err = SSL_get_error(backend->handle, rc); - switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_write() */ - *curlcode = CURLE_AGAIN; - return -1; - default: - failf(data, - "SSL write: %s, errno %d", - ERR_error_string_n(err, error_buffer, sizeof(error_buffer)), - SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - } - return rc; -} - -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->ctx) { - SSL_CTX_free(backend->ctx); - backend->ctx = NULL; - } -} - -static ssize_t -mesalink_recv(struct Curl_easy *data, int num, char *buf, size_t buffersize, - CURLcode *curlcode) -{ - 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); - - if(nread <= 0) { - int err = SSL_get_error(backend->handle, nread); - - switch(err) { - case SSL_ERROR_ZERO_RETURN: /* no more data */ - case IO_ERROR_CONNECTION_ABORTED: - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - *curlcode = CURLE_AGAIN; - return -1; - default: - failf(data, - "SSL read: %s, errno %d", - ERR_error_string_n(err, error_buffer, sizeof(error_buffer)), - SOCKERRNO); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - } - return nread; -} - -static size_t -mesalink_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING); -} - -static int -mesalink_init(void) -{ - return (SSL_library_init() == SSL_SUCCESS); -} - -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int -mesalink_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - 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; - } - return retval; -} - -static CURLcode -mesalink_connect_common(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool nonblocking, bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - timediff_t timeout_ms; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = mesalink_connect_step1(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = - ssl_connect_2_writing == connssl->connecting_state ? sockfd - : CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == connssl->connecting_state - ? sockfd - : CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - result = mesalink_connect_step2(data, conn, sockindex); - - if(result || - (nonblocking && (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) { - return result; - } - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = mesalink_connect_step3(conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = mesalink_recv; - conn->send[sockindex] = mesalink_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode -mesalink_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool *done) -{ - return mesalink_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode -mesalink_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = mesalink_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -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; -} - -const struct Curl_ssl Curl_ssl_mesalink = { - { CURLSSLBACKEND_MESALINK, "MesaLink" }, /* info */ - - SSLSUPP_SSL_CTX, - - sizeof(struct ssl_backend_data), - - mesalink_init, /* init */ - Curl_none_cleanup, /* cleanup */ - mesalink_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - mesalink_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - mesalink_connect, /* connect */ - mesalink_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - mesalink_get_internals, /* get_internals */ - mesalink_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif diff --git a/lib/vtls/mesalink.h b/lib/vtls/mesalink.h deleted file mode 100644 index 03f520c..0000000 --- a/lib/vtls/mesalink.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef HEADER_CURL_MESALINK_H -#define HEADER_CURL_MESALINK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017 - 2018, Yiming Jing, - * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ -#include "curl_setup.h" - -#ifdef USE_MESALINK - -extern const struct Curl_ssl Curl_ssl_mesalink; - -#endif /* USE_MESALINK */ -#endif /* HEADER_CURL_MESALINK_H */ diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 2b44f05..5b7de9f 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -434,7 +434,7 @@ static char *dup_nickname(struct Curl_easy *data, const char *str) /* search the first slash; we require at least one slash in a file name */ n = strchr(str, '/'); if(!n) { - infof(data, "warning: certificate file name \"%s\" handled as nickname; " + infof(data, "WARNING: certificate file name \"%s\" handled as nickname; " "please use \"./%s\" to force file name", str, str); return strdup(str); } @@ -488,6 +488,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl, const int slot_id = (cacert) ? 0 : 1; char *slot_name = aprintf("PEM Token #%d", slot_id); struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + if(!slot_name) return CURLE_OUT_OF_MEMORY; @@ -859,11 +862,11 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) #endif case SSL_NEXT_PROTO_NO_SUPPORT: case SSL_NEXT_PROTO_NO_OVERLAP: - infof(data, "ALPN/NPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); return; #ifdef SSL_ENABLE_ALPN case SSL_NEXT_PROTO_SELECTED: - infof(data, "ALPN, server accepted to use %.*s", buflen, buf); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf); break; #endif case SSL_NEXT_PROTO_NEGOTIATED: @@ -871,7 +874,7 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) break; } -#ifdef USE_NGHTTP2 +#ifdef USE_HTTP2 if(buflen == ALPN_H2_LENGTH && !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) { conn->negnpn = CURL_HTTP_VERSION_2; @@ -882,8 +885,14 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { conn->negnpn = CURL_HTTP_VERSION_1_1; } - Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + + /* This callback might get called when PR_Recv() is used within + * close_one() during a connection shutdown. At that point there might not + * be any "bundle" associated with the connection anymore. + */ + if(conn->bundle) + Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } } @@ -1105,9 +1114,12 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, { CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = backend->data; + struct Curl_easy *data = NULL; CERTCertificate *cert; + DEBUGASSERT(backend); + data = backend->data; + if(!pinnedpubkey) /* no pinned public key specified */ return CURLE_OK; @@ -1134,7 +1146,7 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, /* report the resulting status */ switch(result) { case CURLE_OK: - infof(data, "pinned public key verified successfully!"); + infof(data, "pinned public key verified successfully"); break; case CURLE_SSL_PINNEDPUBKEYNOTMATCH: failf(data, "failed to verify pinned public key"); @@ -1158,10 +1170,15 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, { struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = backend->data; - const char *nickname = backend->client_nickname; + struct Curl_easy *data = NULL; + const char *nickname = NULL; static const char pem_slotname[] = "PEM Token #1"; + DEBUGASSERT(backend); + + data = backend->data; + nickname = backend->client_nickname; + if(backend->obj_clicert) { /* use the cert/key provided by PEM reader */ SECItem cert_der = { 0, NULL, 0 }; @@ -1529,6 +1546,8 @@ static int nss_check_cxn(struct connectdata *conn) int rc; char buf; + DEBUGASSERT(backend); + rc = PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK, PR_SecondsToInterval(1)); @@ -1545,7 +1564,11 @@ static void close_one(struct ssl_connect_data *connssl) { /* before the cleanup, check whether we are using a client certificate */ struct ssl_backend_data *backend = connssl->backend; - const bool client_cert = (backend->client_nickname != NULL) + bool client_cert = true; + + DEBUGASSERT(backend); + + client_cert = (backend->client_nickname != NULL) || (backend->obj_clicert != NULL); if(backend->handle) { @@ -1587,8 +1610,13 @@ static void nss_close(struct Curl_easy *data, struct connectdata *conn, struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex]; #endif struct ssl_backend_data *backend = connssl->backend; - (void)data; + + DEBUGASSERT(backend); +#ifndef CURL_DISABLE_PROXY + DEBUGASSERT(connssl_proxy->backend != NULL); +#endif + if(backend->handle #ifndef CURL_DISABLE_PROXY || connssl_proxy->backend->handle @@ -1720,7 +1748,7 @@ static CURLcode nss_load_ca_certificates(struct Curl_easy *data, PR_CloseDir(dir); } else - infof(data, "warning: CURLOPT_CAPATH not a directory (%s)", capath); + infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath); } return CURLE_OK; @@ -1816,6 +1844,8 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(is_nss_error(curlerr)) { /* read NSPR error code */ PRErrorCode err = PR_GetError(); @@ -1842,6 +1872,9 @@ static CURLcode nss_set_blocking(struct ssl_connect_data *connssl, { PRSocketOptionData sock_opt; struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = !blocking; @@ -1865,7 +1898,6 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, CURLcode result; bool second_layer = FALSE; SSLVersionRange sslver_supported; - SSLVersionRange sslver = { SSL_LIBRARY_VERSION_TLS_1_0, /* min */ #ifdef SSL_LIBRARY_VERSION_TLS_1_3 @@ -1878,6 +1910,13 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, SSL_LIBRARY_VERSION_TLS_1_0 #endif }; + char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + + DEBUGASSERT(backend); backend->data = data; @@ -1946,11 +1985,11 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, /* unless the user explicitly asks to allow the protocol vulnerability, we use the work-around */ if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) - infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d", + infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d", ssl_cbc_random_iv); #else if(ssl_cbc_random_iv) - infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in"); + infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in"); #endif if(SSL_CONN_CONFIG(cipher_list)) { @@ -1961,7 +2000,7 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, } if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost)) - infof(data, "warning: ignoring value of ssl.verifyhost"); + infof(data, "WARNING: ignoring value of ssl.verifyhost"); /* bypass the default SSL_AuthCertificate() hook in case we do not want to * verify peer */ @@ -1981,7 +2020,7 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, const CURLcode rv = nss_load_ca_certificates(data, conn, sockindex); if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer)) /* not a fatal error because we are not going to verify the peer */ - infof(data, "warning: CA certificates failed to load"); + infof(data, "WARNING: CA certificates failed to load"); else if(rv) { result = rv; goto error; @@ -2028,9 +2067,12 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { + struct ssl_backend_data *proxy_backend; + proxy_backend = conn->proxy_ssl[sockindex].backend; DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); - DEBUGASSERT(conn->proxy_ssl[sockindex].backend->handle != NULL); - nspr_io = conn->proxy_ssl[sockindex].backend->handle; + DEBUGASSERT(proxy_backend); + DEBUGASSERT(proxy_backend->handle); + nspr_io = proxy_backend->handle; second_layer = TRUE; } #endif @@ -2140,11 +2182,11 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, goto error; /* propagate hostname to the TLS layer */ - if(SSL_SetURL(backend->handle, SSL_HOST_NAME()) != SECSuccess) + if(SSL_SetURL(backend->handle, snihost) != SECSuccess) goto error; /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(backend->handle, SSL_HOST_NAME()) != SECSuccess) + if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) goto error; return CURLE_OK; @@ -2172,6 +2214,8 @@ static CURLcode nss_do_connect(struct Curl_easy *data, goto error; } + DEBUGASSERT(backend); + /* Force the handshake now */ timeout = PR_MillisecondsToInterval((PRUint32) time_left); if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { @@ -2305,6 +2349,8 @@ static ssize_t nss_send(struct Curl_easy *data, /* transfer */ struct ssl_backend_data *backend = connssl->backend; ssize_t rc; + DEBUGASSERT(backend); + /* The SelectClientCert() hook uses this for infof() and failf() but the handle stored in nss_setup_connect() could have already been freed. */ backend->data = data; @@ -2344,6 +2390,8 @@ static ssize_t nss_recv(struct Curl_easy *data, /* transfer */ struct ssl_backend_data *backend = connssl->backend; ssize_t nread; + DEBUGASSERT(backend); + /* The SelectClientCert() hook uses this for infof() and failf() but the handle stored in nss_setup_connect() could have already been freed. */ backend->data = data; @@ -2442,6 +2490,7 @@ static void *nss_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->handle; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index f836c63..3722005 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -91,7 +91,6 @@ #endif #include "warnless.h" -#include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */ /* The last #include files should be: */ #include "curl_memory.h" @@ -209,9 +208,18 @@ !defined(OPENSSL_IS_BORINGSSL)) #define HAVE_SSL_CTX_SET_CIPHERSUITES #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH -/* SET_EC_CURVES is available under the same preconditions: see - * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html +#endif + +/* + * Whether SSL_CTX_set1_curves_list is available. + * OpenSSL: supported since 1.0.2, see + * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html + * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30) + * LibreSSL: not tested. */ +#if ((OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + !defined(LIBRESSL_VERSION_NUMBER)) || \ + defined(OPENSSL_IS_BORINGSSL) #define HAVE_SSL_CTX_SET_EC_CURVES #endif @@ -266,7 +274,7 @@ struct ssl_backend_data { #endif }; -static void ossl_associate_connection(struct Curl_easy *data, +static bool ossl_associate_connection(struct Curl_easy *data, struct connectdata *conn, int sockindex); @@ -549,7 +557,7 @@ static CURLcode ossl_seed(struct Curl_easy *data) } } - infof(data, "libcurl is now using a weak random seed!"); + infof(data, "libcurl is now using a weak random seed"); return (rand_enough() ? CURLE_OK : CURLE_SSL_CONNECT_ERROR /* confusing error code */); #endif @@ -1159,6 +1167,22 @@ int cert_stuff(struct Curl_easy *data, return 1; } +CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, SSL_CTX *ctx, + char *cert_file, + const struct curl_blob *cert_blob, + const char *cert_type, char *key_file, + const struct curl_blob *key_blob, + const char *key_type, char *key_passwd) +{ + int rv = cert_stuff(data, ctx, cert_file, cert_blob, cert_type, key_file, + key_blob, key_type, key_passwd); + if(rv != 1) { + return CURLE_SSL_CERTPROBLEM; + } + + return CURLE_OK; +} + /* returns non-zero on failure */ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) { @@ -1432,6 +1456,9 @@ static void ossl_closeone(struct Curl_easy *data, struct ssl_connect_data *connssl) { struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + if(backend->handle) { char buf[32]; set_logger(conn, data); @@ -1489,6 +1516,8 @@ static int ossl_shutdown(struct Curl_easy *data, struct ssl_backend_data *backend = connssl->backend; int loop = 10; + DEBUGASSERT(backend); + #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in @@ -1610,54 +1639,26 @@ static void ossl_close_all(struct Curl_easy *data) /* ====================================================== */ /* - * Match subjectAltName against the host name. This requires a conversion - * in CURL_DOES_CONVERSIONS builds. + * Match subjectAltName against the host name. */ static bool subj_alt_hostcheck(struct Curl_easy *data, - const char *match_pattern, const char *hostname, + const char *match_pattern, + size_t matchlen, + const char *hostname, + size_t hostlen, const char *dispname) -#ifdef CURL_DOES_CONVERSIONS -{ - bool res = FALSE; - - /* Curl_cert_hostcheck uses host encoding, but we get ASCII from - OpenSSl. - */ - char *match_pattern2 = strdup(match_pattern); - - if(match_pattern2) { - if(Curl_convert_from_network(data, match_pattern2, - strlen(match_pattern2)) == CURLE_OK) { - if(Curl_cert_hostcheck(match_pattern2, hostname)) { - res = TRUE; - infof(data, - " subjectAltName: host \"%s\" matched cert's \"%s\"", - dispname, match_pattern2); - } - } - free(match_pattern2); - } - else { - failf(data, - "SSL: out of memory when allocating temporary for subjectAltName"); - } - return res; -} -#else { #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)dispname; (void)data; #endif - if(Curl_cert_hostcheck(match_pattern, hostname)) { + if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) { infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"", dispname, match_pattern); return TRUE; } return FALSE; } -#endif - /* Quote from RFC2818 section 3.1 "Server Identity" @@ -1698,6 +1699,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ const char * const hostname = SSL_HOST_NAME(); const char * const dispname = SSL_HOST_DISPNAME(); + size_t hostlen = strlen(hostname); #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -1760,7 +1762,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, if((altlen == strlen(altptr)) && /* if this isn't true, there was an embedded zero in the name string and we cannot match it. */ - subj_alt_hostcheck(data, altptr, hostname, dispname)) { + subj_alt_hostcheck(data, + altptr, + altlen, hostname, hostlen, dispname)) { dnsmatched = TRUE; } break; @@ -1796,17 +1800,17 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, else { /* we have to look to the last occurrence of a commonName in the distinguished one to get the most significant one. */ - int j, i = -1; + int i = -1; + unsigned char *peer_CN = NULL; + int peerlen = 0; /* The following is done because of a bug in 0.9.6b */ - - unsigned char *nulstr = (unsigned char *)""; - unsigned char *peer_CN = nulstr; - X509_NAME *name = X509_get_subject_name(server_cert); - if(name) + if(name) { + int j; while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) i = j; + } /* we have the name entry and we will now convert this to a string that we can use for comparison. Doing this we support BMPstring, @@ -1822,19 +1826,21 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, conditional in the future when OpenSSL has been fixed. */ if(tmp) { if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { - j = ASN1_STRING_length(tmp); - if(j >= 0) { - peer_CN = OPENSSL_malloc(j + 1); + peerlen = ASN1_STRING_length(tmp); + if(peerlen >= 0) { + peer_CN = OPENSSL_malloc(peerlen + 1); if(peer_CN) { - memcpy(peer_CN, ASN1_STRING_get0_data(tmp), j); - peer_CN[j] = '\0'; + memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen); + peer_CN[peerlen] = '\0'; } + else + result = CURLE_OUT_OF_MEMORY; } } else /* not a UTF8 name */ - j = ASN1_STRING_to_UTF8(&peer_CN, tmp); + peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp); - if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) { + if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) { /* there was a terminating zero before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); @@ -1843,19 +1849,6 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, } } - if(peer_CN == nulstr) - peer_CN = NULL; - else { - /* convert peer_CN from UTF8 */ - CURLcode rc = Curl_convert_from_utf8(data, (char *)peer_CN, - strlen((char *)peer_CN)); - /* Curl_convert_from_utf8 calls failf if unsuccessful */ - if(rc) { - OPENSSL_free(peer_CN); - return rc; - } - } - if(result) /* error already detected, pass through */ ; @@ -1864,7 +1857,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, "SSL: unable to obtain common name from peer certificate"); result = CURLE_PEER_FAILED_VERIFICATION; } - else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) { + else if(!Curl_cert_hostcheck((const char *)peer_CN, + peerlen, hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, dispname); result = CURLE_PEER_FAILED_VERIFICATION; @@ -1898,8 +1892,11 @@ static CURLcode verifystatus(struct Curl_easy *data, int cert_status, crl_reason; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; int ret; + long len; - long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); + DEBUGASSERT(backend); + + len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); if(!status) { failf(data, "No OCSP response received"); @@ -1930,6 +1927,11 @@ static CURLcode verifystatus(struct Curl_easy *data, } ch = SSL_get_peer_cert_chain(backend->handle); + if(!ch) { + failf(data, "Could not get peer certificate chain"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } st = SSL_CTX_get_cert_store(backend->ctx); #if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ @@ -2158,7 +2160,10 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, struct connectdata *conn = userp; struct ssl_connect_data *connssl = &conn->ssl[0]; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = backend->logger; + struct Curl_easy *data = NULL; + + DEBUGASSERT(backend); + data = backend->logger; if(!conn || !data || !data->set.fdebug || (direction != 0 && direction != 1)) @@ -2363,10 +2368,12 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) case CURL_SSLVERSION_TLSv1_2: ossl_ssl_version_min = TLS1_2_VERSION; break; -#ifdef TLS1_3_VERSION case CURL_SSLVERSION_TLSv1_3: +#ifdef TLS1_3_VERSION ossl_ssl_version_min = TLS1_3_VERSION; break; +#else + return CURLE_NOT_BUILT_IN; #endif } @@ -2422,6 +2429,8 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) #ifdef OPENSSL_IS_BORINGSSL typedef uint32_t ctx_option_t; +#elif OPENSSL_VERSION_NUMBER >= 0x30000000L +typedef uint64_t ctx_option_t; #else typedef long ctx_option_t; #endif @@ -2442,6 +2451,8 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } @@ -2521,13 +2532,12 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) return 0; conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx); - if(!conn) - return 0; - data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); - /* The sockindex has been stored as a pointer to an array element */ sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx); + if(!conn || !data || !sockindex_ptr) + return 0; + sockindex = (int)(sockindex_ptr - conn->sock); isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE; @@ -2670,6 +2680,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, bool imported_native_ca = false; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(backend); /* Make funny stuff to get random input */ result = ossl_seed(data); @@ -2736,8 +2747,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, implementations is desired." The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to - disable "rfc4507bis session ticket support". rfc4507bis was later turned - into the proper RFC5077 it seems: https://tools.ietf.org/html/rfc5077 + disable "rfc4507bis session ticket support". rfc4507bis was later turned + into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077 The enabled extension concerns the session management. I wonder how often libcurl stops a connection and then resumes a TLS session. Also, sending @@ -2840,14 +2851,14 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); cur += ALPN_H2_LENGTH; - infof(data, "ALPN, offering %s", ALPN_H2); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } #endif protocols[cur++] = ALPN_HTTP_1_1_LENGTH; memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); cur += ALPN_HTTP_1_1_LENGTH; - infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); /* expects length prefixed preference ordered list of protocols in wire * format @@ -2913,7 +2924,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #endif #ifdef USE_OPENSSL_SRP - if(ssl_authtype == CURL_TLSAUTH_SRP) { + if((ssl_authtype == CURL_TLSAUTH_SRP) && + Curl_allow_auth_to_host(data)) { char * const ssl_username = SSL_SET_OPTION(username); infof(data, "Using TLS-SRP username: %s", ssl_username); @@ -2942,7 +2954,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, /* Import certificates from the Windows root certificate store if requested. https://stackoverflow.com/questions/9507184/ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 - https://tools.ietf.org/html/rfc5280 */ + https://datatracker.ietf.org/doc/html/rfc5280 */ if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && (SSL_SET_OPTION(native_ca_store))) { X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); @@ -3220,7 +3232,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, SSL_free(backend->handle); backend->handle = SSL_new(backend->ctx); if(!backend->handle) { - failf(data, "SSL: couldn't create a context (handle)!"); + failf(data, "SSL: couldn't create a context (handle)"); return CURLE_OUT_OF_MEMORY; } @@ -3243,44 +3255,48 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && #endif sni) { - size_t nlen = strlen(hostname); - if((long)nlen >= data->set.buffer_size) - /* this is seriously messed up */ + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) { + failf(data, "Failed set SNI"); return CURLE_SSL_CONNECT_ERROR; - - /* RFC 6066 section 3 says the SNI field is case insensitive, but browsers - send the data lowercase and subsequently there are now numerous servers - out there that don't work unless the name is lowercased */ - Curl_strntolower(data->state.buffer, hostname, nlen); - data->state.buffer[nlen] = 0; - if(!SSL_set_tlsext_host_name(backend->handle, data->state.buffer)) - infof(data, "WARNING: failed to configure server name indication (SNI) " - "TLS extension"); + } } #endif - ossl_associate_connection(data, conn, sockindex); + if(!ossl_associate_connection(data, conn, sockindex)) { + /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */ + failf(data, "SSL: ossl_associate_connection failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { - /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { - Curl_ssl_sessionid_unlock(data); - failf(data, "SSL: SSL_set_session failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; + if(SSL_SET_OPTION(primary.sessionid)) { + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, + &ssl_sessionid, NULL, sockindex)) { + /* we got a session id, use it! */ + if(!SSL_set_session(backend->handle, ssl_sessionid)) { + Curl_ssl_sessionid_unlock(data); + failf(data, "SSL: SSL_set_session failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + /* Informational message */ + infof(data, "SSL re-using session ID"); } - /* Informational message */ - infof(data, "SSL re-using session ID"); + Curl_ssl_sessionid_unlock(data); } - Curl_ssl_sessionid_unlock(data); #ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { BIO *const bio = BIO_new(BIO_f_ssl()); - SSL *handle = conn->proxy_ssl[sockindex].backend->handle; + struct ssl_backend_data *proxy_backend; + SSL* handle = NULL; + proxy_backend = conn->proxy_ssl[sockindex].backend; + DEBUGASSERT(proxy_backend); + handle = proxy_backend->handle; DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); DEBUGASSERT(handle != NULL); DEBUGASSERT(bio != NULL); @@ -3310,6 +3326,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); + DEBUGASSERT(backend); ERR_clear_error(); @@ -3441,7 +3458,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, unsigned int len; SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); if(len) { - infof(data, "ALPN, server accepted to use %.*s", len, neg_protocol); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol); #ifdef USE_HTTP2 if(len == ALPN_H2_LENGTH && @@ -3456,7 +3473,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, } } else - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); @@ -3571,6 +3588,8 @@ static CURLcode get_cert_chain(struct Curl_easy *data, BIO *mem; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + sk = SSL_get_peer_cert_chain(backend->handle); if(!sk) { return CURLE_OUT_OF_MEMORY; @@ -3883,6 +3902,8 @@ static CURLcode servercert(struct Curl_easy *data, BIO *mem = BIO_new(BIO_s_mem()); struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(!mem) { failf(data, "BIO_new return NULL, " OSSL_PACKAGE @@ -3902,7 +3923,7 @@ static CURLcode servercert(struct Curl_easy *data, if(!strict) return CURLE_OK; - failf(data, "SSL: couldn't get peer certificate!"); + failf(data, "SSL: couldn't get peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } @@ -3942,7 +3963,7 @@ static CURLcode servercert(struct Curl_easy *data, buffer, sizeof(buffer)); if(rc) { if(strict) - failf(data, "SSL: couldn't get X509-issuer name!"); + failf(data, "SSL: couldn't get X509-issuer name"); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -3953,9 +3974,20 @@ static CURLcode servercert(struct Curl_easy *data, /* e.g. match issuer name with provided issuer certificate */ if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) { - if(SSL_CONN_CONFIG(issuercert_blob)) + if(SSL_CONN_CONFIG(issuercert_blob)) { fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data, (int)SSL_CONN_CONFIG(issuercert_blob)->len); + if(!fp) { + failf(data, + "BIO_new_mem_buf NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + X509_free(backend->server_cert); + backend->server_cert = NULL; + return CURLE_OUT_OF_MEMORY; + } + } else { fp = BIO_new(BIO_s_file()); if(!fp) { @@ -4049,7 +4081,7 @@ static CURLcode servercert(struct Curl_easy *data, if(!result && ptr) { result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); if(result) - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); } X509_free(backend->server_cert); @@ -4222,11 +4254,13 @@ static bool ossl_data_pending(const struct connectdata *conn, int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + DEBUGASSERT(connssl->backend); if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) return TRUE; #ifndef CURL_DISABLE_PROXY { const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; + DEBUGASSERT(proxyssl->backend); if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) return TRUE; } @@ -4253,6 +4287,8 @@ static ssize_t ossl_send(struct Curl_easy *data, struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; @@ -4332,6 +4368,8 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ struct ssl_connect_data *connssl = &conn->ssl[num]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; @@ -4531,20 +4569,22 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return info == CURLINFO_TLS_SESSION ? (void *)backend->ctx : (void *)backend->handle; } -static void ossl_associate_connection(struct Curl_easy *data, +static bool ossl_associate_connection(struct Curl_easy *data, struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); /* If we don't have SSL context, do nothing. */ if(!backend->handle) - return; + return FALSE; if(SSL_SET_OPTION(primary.sessionid)) { int data_idx = ossl_get_ssl_data_index(); @@ -4554,19 +4594,26 @@ static void ossl_associate_connection(struct Curl_easy *data, if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 && proxy_idx >= 0) { + int data_status, conn_status, sockindex_status, proxy_status; + /* Store the data needed for the "new session" callback. * The sockindex is stored as a pointer to an array element. */ - SSL_set_ex_data(backend->handle, data_idx, data); - SSL_set_ex_data(backend->handle, connectdata_idx, conn); - SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex); + data_status = SSL_set_ex_data(backend->handle, data_idx, data); + conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn); + sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, + conn->sock + sockindex); #ifndef CURL_DISABLE_PROXY - SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1: - NULL); + proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, + SSL_IS_PROXY() ? (void *) 1 : NULL); #else - SSL_set_ex_data(backend->handle, proxy_idx, NULL); + proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL); #endif + if(data_status && conn_status && sockindex_status && proxy_status) + return TRUE; } + return FALSE; } + return TRUE; } /* @@ -4583,6 +4630,7 @@ static void ossl_disassociate_connection(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); /* If we don't have SSL context, do nothing. */ if(!backend->handle) diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 2805845..0a7536e 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,12 +30,26 @@ * and ngtcp2.c */ -#include #include "urldata.h" +/* + * In an effort to avoid using 'X509 *' here, we instead use the struct + * x509_st version of the type so that we can forward-declare it here without + * having to include . Including that header causes name + * conflicts when libcurl is built with both Schannel and OpenSSL support. + */ +struct x509_st; CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert); + struct x509_st *server_cert); extern const struct Curl_ssl Curl_ssl_openssl; +struct ssl_ctx_st; +CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, + struct ssl_ctx_st *ctx, char *cert_file, + const struct curl_blob *cert_blob, + const char *cert_type, char *key_file, + const struct curl_blob *key_blob, + const char *key_type, char *key_passwd); + #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 6dbb1ef..16970b7 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews, + * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, * * * This software is licensed as described in the file COPYING, which @@ -65,6 +65,7 @@ cr_data_pending(const struct connectdata *conn, int sockindex) { const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return backend->data_pending; } @@ -118,7 +119,8 @@ cr_recv(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *const rconn = backend->conn; + struct rustls_connection *rconn = NULL; + size_t n = 0; size_t tls_bytes_read = 0; size_t plain_bytes_copied = 0; @@ -126,6 +128,9 @@ cr_recv(struct Curl_easy *data, int sockindex, char errorbuf[255]; rustls_io_result io_error; + DEBUGASSERT(backend); + rconn = backend->conn; + io_error = rustls_connection_read_tls(rconn, read_cb, &conn->sock[sockindex], &tls_bytes_read); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { @@ -215,13 +220,16 @@ cr_send(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *const rconn = backend->conn; + struct rustls_connection *rconn = NULL; size_t plainwritten = 0; size_t tlswritten = 0; size_t tlswritten_total = 0; rustls_result rresult; rustls_io_result io_error; + DEBUGASSERT(backend); + rconn = backend->conn; + infof(data, "cr_send %ld bytes of plaintext", plainlen); if(plainlen > 0) { @@ -295,9 +303,13 @@ static CURLcode cr_init_backend(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *const backend) { - struct rustls_connection *rconn = backend->conn; + struct rustls_connection *rconn = NULL; struct rustls_client_config_builder *config_builder = NULL; - const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); + struct rustls_root_cert_store *roots = NULL; + 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 *hostname = conn->host.name; char errorbuf[256]; @@ -308,14 +320,17 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, }; + DEBUGASSERT(backend); + rconn = backend->conn; + config_builder = rustls_client_config_builder_new(); #ifdef USE_HTTP2 - infof(data, "offering ALPN for HTTP/1.1 and HTTP/2"); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); 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_alpn_protocols(config_builder, alpn, 1); #endif + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); if(!verifypeer) { rustls_client_config_builder_dangerous_set_certificate_verifier( config_builder, cr_verify_none); @@ -328,6 +343,29 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, hostname = "example.invalid"; } } + else if(ca_info_blob) { + roots = rustls_root_cert_store_new(); + + /* Enable strict parsing only if verification isn't disabled. */ + result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, + ca_info_blob->len, verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to parse trusted certificates from blob"); + rustls_root_cert_store_free(roots); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + + result = rustls_client_config_builder_use_roots(config_builder, roots); + rustls_root_cert_store_free(roots); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to load trusted certificates"); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } else if(ssl_cafile) { result = rustls_client_config_builder_load_roots_from_file( config_builder, ssl_cafile); @@ -341,7 +379,14 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, backend->config = rustls_client_config_builder_build(config_builder); DEBUGASSERT(rconn == NULL); - result = rustls_client_connection_new(backend->config, hostname, &rconn); + { + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + result = rustls_client_connection_new(backend->config, snihost, &rconn); + } if(result != RUSTLS_RESULT_OK) { rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf); @@ -361,20 +406,20 @@ cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn, rustls_connection_get_alpn_protocol(rconn, &protocol, &len); if(!protocol) { - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); return; } #ifdef USE_HTTP2 if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) { - infof(data, "ALPN, negotiated h2"); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2); conn->negnpn = CURL_HTTP_VERSION_2; } else #endif if(len == ALPN_HTTP_1_1_LENGTH && 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) { - infof(data, "ALPN, negotiated http/1.1"); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1); conn->negnpn = CURL_HTTP_VERSION_1_1; } else { @@ -401,6 +446,8 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, curl_socket_t writefd; curl_socket_t readfd; + DEBUGASSERT(backend); + if(ssl_connection_none == connssl->state) { result = cr_init_backend(data, conn, connssl->backend); if(result != CURLE_OK) { @@ -495,7 +542,10 @@ cr_getsock(struct connectdata *conn, curl_socket_t *socks) struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *rconn = backend->conn; + struct rustls_connection *rconn = NULL; + + DEBUGASSERT(backend); + rconn = backend->conn; if(rustls_connection_wants_write(rconn)) { socks[0] = sockfd; @@ -514,6 +564,7 @@ cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return &backend->conn; } @@ -526,6 +577,8 @@ cr_close(struct Curl_easy *data, struct connectdata *conn, CURLcode tmperr = CURLE_OK; ssize_t n = 0; + DEBUGASSERT(backend); + if(backend->conn) { rustls_connection_send_close_notify(backend->conn); n = cr_send(data, sockindex, NULL, 0, &tmperr); @@ -550,7 +603,8 @@ static size_t cr_version(char *buffer, size_t size) const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, - SSLSUPP_TLS13_CIPHERSUITES, /* supports */ + SSLSUPP_CAINFO_BLOB | /* supports */ + SSLSUPP_TLS13_CIPHERSUITES, sizeof(struct ssl_backend_data), Curl_none_init, /* init */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 0a8e606..dfec66d 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * Copyright (C) 2012 - 2016, Marc Hoersken, * Copyright (C) 2012, Mark Salisbury, * @@ -325,13 +325,15 @@ get_alg_id_by_name(char *name) return 0; } +#define NUM_CIPHERS 47 /* There are 47 options listed above */ + static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, ALG_ID *algIds) { char *startCur = ciphers; int algCount = 0; - while(startCur && (0 != *startCur) && (algCount < NUMOF_CIPHERS)) { + while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) { long alg = strtol(startCur, 0, 0); if(!alg) alg = get_alg_id_by_name(startCur); @@ -418,11 +420,14 @@ schannel_acquire_credential_handle(struct Curl_easy *data, { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SCHANNEL_CRED schannel_cred; + ALG_ID algIds[NUM_CIPHERS]; PCCERT_CONTEXT client_certs[1] = { NULL }; SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + /* setup Schannel API options */ memset(&schannel_cred, 0, sizeof(schannel_cred)); schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; @@ -502,7 +507,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); + algIds); if(CURLE_OK != result) { failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); return result; @@ -765,11 +770,12 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif - TCHAR *host_name; CURLcode result; char * const hostname = SSL_HOST_NAME(); struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)", hostname, conn->remote_port)); @@ -846,10 +852,21 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, } if(!backend->cred) { + char *snihost; result = schannel_acquire_credential_handle(data, conn, sockindex); if(result != CURLE_OK) { return result; } + /* A hostname associated with the credential is needed by + InitializeSecurityContext for SNI and other reasons. */ + snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); + if(!backend->cred->sni_hostname) + return CURLE_OUT_OF_MEMORY; } /* Warn if SNI is disabled due to use of an IP address */ @@ -891,14 +908,14 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, alpn_buffer[cur++] = ALPN_H2_LENGTH; memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH); cur += ALPN_H2_LENGTH; - infof(data, "schannel: ALPN, offering %s", ALPN_H2); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } #endif alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH; memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); cur += ALPN_HTTP_1_1_LENGTH; - infof(data, "schannel: ALPN, offering %s", ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); *list_len = curlx_uitous(cur - list_start_index); *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short); @@ -936,10 +953,6 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } - host_name = curlx_convert_UTF8_to_tchar(hostname); - if(!host_name) - return CURLE_OUT_OF_MEMORY; - /* Schannel InitializeSecurityContext: https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx @@ -948,13 +961,12 @@ 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->cred->cred_handle, NULL, backend->cred->sni_hostname, + 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); @@ -1027,15 +1039,16 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; bool doread; - char * const hostname = SSL_HOST_NAME(); const char *pubkey_ptr; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)", - hostname, conn->remote_port)); + SSL_HOST_NAME(), conn->remote_port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1083,7 +1096,6 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } for(;;) { - TCHAR *host_name; if(doread) { /* read encrypted handshake data from socket */ result = Curl_read_plain(conn->sock[sockindex], @@ -1136,17 +1148,12 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, 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; - sspi_status = s_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, - host_name, backend->req_flags, 0, 0, &inbuf_desc, 0, NULL, + backend->cred->sni_hostname, backend->req_flags, + 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); - curlx_unicodefree(host_name); - /* free buffer for received handshake data */ Curl_safefree(inbuf[0].pvBuffer); @@ -1279,7 +1286,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, if(pubkey_ptr) { result = pkp_pin_peer_pubkey(data, conn, sockindex, pubkey_ptr); if(result) { - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); return result; } } @@ -1370,6 +1377,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)", @@ -1408,7 +1416,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, if(alpn_result.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) { - infof(data, "schannel: ALPN, server accepted to use %.*s", + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, alpn_result.ProtocolIdSize, alpn_result.ProtocolId); #ifdef USE_HTTP2 @@ -1425,7 +1433,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, } } else - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } @@ -1610,6 +1618,7 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn, */ { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); conn->sslContext = &backend->ctxt->ctxt_handle; } #endif @@ -1640,6 +1649,8 @@ schannel_send(struct Curl_easy *data, int sockindex, CURLcode result; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + /* check if the maximum stream sizes were queried */ if(backend->stream_sizes.cbMaximumMessage == 0) { sspi_status = s_pSecFn->QueryContextAttributes( @@ -1788,6 +1799,8 @@ schannel_recv(struct Curl_easy *data, int sockindex, size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + /**************************************************************************** * 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. @@ -2122,6 +2135,8 @@ static bool schannel_data_pending(const struct connectdata *conn, const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(connssl->use) /* SSL/TLS is in use */ return (backend->decdata_offset > 0 || (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); @@ -2138,6 +2153,7 @@ static void schannel_session_free(void *ptr) cred->refcount--; if(cred->refcount == 0) { s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + curlx_unicodefree(cred->sni_hostname); Curl_safefree(cred); } } @@ -2157,6 +2173,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(data); + DEBUGASSERT(backend); if(connssl->use) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu", @@ -2170,7 +2187,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, SecBuffer outbuf; SecBufferDesc outbuf_desc; CURLcode result; - TCHAR *host_name; DWORD dwshut = SCHANNEL_SHUTDOWN; InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); @@ -2185,10 +2201,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); } - host_name = curlx_convert_UTF8_to_tchar(hostname); - if(!host_name) - return CURLE_OUT_OF_MEMORY; - /* setup output buffer */ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&outbuf_desc, &outbuf, 1); @@ -2196,7 +2208,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, sspi_status = s_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, - host_name, + backend->cred->sni_hostname, backend->req_flags, 0, 0, @@ -2207,8 +2219,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, &backend->ret_flags, &backend->ctxt->time_stamp); - curlx_unicodefree(host_name); - if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { /* send close message which is in output buffer */ ssize_t written; @@ -2314,6 +2324,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + DEBUGASSERT(backend); + /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) return CURLE_OK; @@ -2359,7 +2371,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); if(result) { - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); } } while(0); @@ -2434,6 +2446,7 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return &backend->ctxt->ctxt_handle; } diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h index 77853aa..da60702 100644 --- a/lib/vtls/schannel.h +++ b/lib/vtls/schannel.h @@ -8,7 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012, Marc Hoersken, , et al. - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -71,11 +71,10 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, #endif #endif -#define NUMOF_CIPHERS 45 /* There are 45 listed in the MS headers */ - struct Curl_schannel_cred { CredHandle cred_handle; TimeStamp time_stamp; + TCHAR *sni_hostname; int refcount; }; @@ -104,7 +103,6 @@ struct ssl_backend_data { #ifdef HAS_MANUAL_VERIFY_API bool use_manual_cred_validation; /* true if manual cred validation is used */ #endif - ALG_ID algIds[NUMOF_CIPHERS]; }; #endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */ diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 4966cd4..4dc2d14 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, * Copyright (C) 2012, Mark Salisbury, - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -286,7 +286,6 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, goto cleanup; } - result = CURLE_OK; while(total_bytes_read < ca_file_bufsize) { DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); DWORD bytes_read = 0; @@ -313,9 +312,6 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, /* Null terminate the buffer */ ca_file_buffer[ca_file_bufsize] = '\0'; - if(result != CURLE_OK) { - goto cleanup; - } result = add_certs_data_to_store(trust_store, ca_file_buffer, ca_file_bufsize, ca_file, @@ -465,6 +461,7 @@ static CURLcode verify_host(struct Curl_easy *data, CURLcode result = CURLE_PEER_FAILED_VERIFICATION; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; + size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; @@ -520,10 +517,8 @@ static CURLcode verify_host(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; } else { - int match_result; - - match_result = Curl_cert_hostcheck(cert_hostname, conn_hostname); - if(match_result == CURL_HOST_MATCH) { + if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), + conn_hostname, hostlen)) { infof(data, "schannel: connection hostname (%s) validated " "against certificate name (%s)", @@ -577,6 +572,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, HCERTSTORE trust_store = NULL; const char * const conn_hostname = SSL_HOST_NAME(); + DEBUGASSERT(BACKEND); + sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index f7a20b2..8ee8fe9 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, , et al. * Copyright (C) 2012 - 2017, Nick Zitzmann, . * * This software is licensed as described in the file COPYING, which @@ -603,7 +603,7 @@ const static struct st_cipher ciphertable[] = { CIPHER_WEAK_RC_ENCRYPTION), CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ "ECDH-ECDSA-DES-CBC3-SHA", - CIPHER_STRONG_ENOUGH), + CIPHER_WEAK_3DES_ENCRYPTION), CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ "ECDH-ECDSA-AES128-SHA", CIPHER_STRONG_ENOUGH), @@ -837,12 +837,14 @@ static OSStatus SocketRead(SSLConnectionRef connection, /*int sock = *(int *)connection;*/ struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; struct ssl_backend_data *backend = connssl->backend; - int sock = backend->ssl_sockfd; + int sock; OSStatus rtn = noErr; size_t bytesRead; ssize_t rrtn; int theErr; + DEBUGASSERT(backend); + sock = backend->ssl_sockfd; *dataLength = 0; for(;;) { @@ -898,13 +900,15 @@ static OSStatus SocketWrite(SSLConnectionRef connection, /*int sock = *(int *)connection;*/ struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; struct ssl_backend_data *backend = connssl->backend; - int sock = backend->ssl_sockfd; + int sock; ssize_t length; size_t dataLen = *dataLength; const UInt8 *dataPtr = (UInt8 *)data; OSStatus ortn; int theErr; + DEBUGASSERT(backend); + sock = backend->ssl_sockfd; *dataLength = 0; do { @@ -934,9 +938,9 @@ static OSStatus SocketWrite(SSLConnectionRef connection, #ifndef CURL_DISABLE_VERBOSE_STRINGS CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) { - /* The first ciphers in the ciphertable are continuos. Here we do small + /* The first ciphers in the ciphertable are continuous. Here we do small optimization and instead of loop directly get SSL name by cipher number. - */ + */ if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) { return ciphertable[cipher].name; } @@ -1376,6 +1380,8 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, long ssl_version_max = SSL_CONN_CONFIG(version_max); long max_supported_version_by_os; + DEBUGASSERT(backend); + /* macOS 10.5-10.7 supported TLS 1.0 only. macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2. macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */ @@ -1684,6 +1690,8 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, #if CURL_BUILD_MAC int darwinver_maj = 0, darwinver_min = 0; + DEBUGASSERT(backend); + GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -1693,7 +1701,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, CFRelease(backend->ssl_ctx); backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); if(!backend->ssl_ctx) { - failf(data, "SSL: couldn't create a context!"); + failf(data, "SSL: couldn't create a context"); return CURLE_OUT_OF_MEMORY; } } @@ -1843,12 +1851,12 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, #endif ) { CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2)); - infof(data, "ALPN, offering %s", ALPN_H2); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } #endif CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1)); - infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); /* expects length prefixed preference ordered list of protocols in wire * format @@ -2028,8 +2036,13 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, * Both hostname check and SNI require SSLSetPeerDomainName(). * Also: the verifyhost setting influences SNI usage */ if(conn->ssl_config.verifyhost) { - err = SSLSetPeerDomainName(backend->ssl_ctx, hostname, - strlen(hostname)); + size_t snilen; + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen); if(err != noErr) { infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d", @@ -2542,6 +2555,7 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); + DEBUGASSERT(backend); /* Here goes nothing: */ err = SSLHandshake(backend->ssl_ctx); @@ -2774,7 +2788,7 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, pkp_pin_peer_pubkey(data, backend->ssl_ctx, data->set.str[STRING_SSL_PINNEDPUBLICKEY]); if(result) { - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); return result; } } @@ -2839,7 +2853,7 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, conn->negnpn = CURL_HTTP_VERSION_1_1; } else - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); @@ -2918,6 +2932,8 @@ collect_server_cert(struct Curl_easy *data, CFIndex i, count; SecTrustRef trust = NULL; + DEBUGASSERT(backend); + if(!show_verbose_server_cert && !data->set.ssl.certinfo) return CURLE_OK; @@ -3162,6 +3178,8 @@ static void sectransp_close(struct Curl_easy *data, struct connectdata *conn, (void) data; + DEBUGASSERT(backend); + if(backend->ssl_ctx) { (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS @@ -3190,6 +3208,8 @@ static int sectransp_shutdown(struct Curl_easy *data, char buf[120]; int loop = 10; /* avoid getting stuck */ + DEBUGASSERT(backend); + if(!backend->ssl_ctx) return 0; @@ -3269,6 +3289,8 @@ static int sectransp_check_cxn(struct connectdata *conn) OSStatus err; SSLSessionState state; + DEBUGASSERT(backend); + if(backend->ssl_ctx) { err = SSLGetSessionState(backend->ssl_ctx, &state); if(err == noErr) @@ -3286,6 +3308,8 @@ static bool sectransp_data_pending(const struct connectdata *conn, OSStatus err; size_t buffer; + DEBUGASSERT(backend); + if(backend->ssl_ctx) { /* SSL is in use */ err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) @@ -3347,6 +3371,8 @@ static ssize_t sectransp_send(struct Curl_easy *data, size_t processed = 0UL; OSStatus err; + DEBUGASSERT(backend); + /* The SSLWrite() function works a little differently than expected. The fourth argument (processed) is currently documented in Apple's documentation as: "On return, the length, in bytes, of the data actually @@ -3414,6 +3440,8 @@ static ssize_t sectransp_recv(struct Curl_easy *data, size_t processed = 0UL; OSStatus err; + DEBUGASSERT(backend); + again: err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); @@ -3463,6 +3491,7 @@ static void *sectransp_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->ssl_ctx; } diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 6007bbb..a40ac06 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -125,15 +125,6 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second) return !memcmp(first->data, second->data, first->len); /* same data */ } -static bool safecmp(char *a, char *b) -{ - if(a && b) - return !strcmp(a, b); - else if(!a && !b) - return TRUE; /* match */ - return FALSE; /* no match */ -} - bool Curl_ssl_config_matches(struct ssl_primary_config *data, @@ -147,12 +138,12 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, blobcmp(data->cert_blob, needle->cert_blob) && blobcmp(data->ca_info_blob, needle->ca_info_blob) && blobcmp(data->issuercert_blob, needle->issuercert_blob) && - safecmp(data->CApath, needle->CApath) && - safecmp(data->CAfile, needle->CAfile) && - safecmp(data->issuercert, needle->issuercert) && - safecmp(data->clientcert, needle->clientcert) && - safecmp(data->random_file, needle->random_file) && - safecmp(data->egdsocket, needle->egdsocket) && + Curl_safecmp(data->CApath, needle->CApath) && + Curl_safecmp(data->CAfile, needle->CAfile) && + Curl_safecmp(data->issuercert, needle->issuercert) && + Curl_safecmp(data->clientcert, needle->clientcert) && + Curl_safecmp(data->random_file, needle->random_file) && + Curl_safecmp(data->egdsocket, needle->egdsocket) && Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && Curl_safe_strcasecompare(data->curves, needle->curves) && @@ -300,6 +291,8 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex) pbdata = conn->proxy_ssl[sockindex].backend; conn->proxy_ssl[sockindex] = conn->ssl[sockindex]; + DEBUGASSERT(pbdata != NULL); + memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex])); memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data); @@ -628,7 +621,8 @@ void Curl_ssl_associate_conn(struct Curl_easy *data, { if(Curl_ssl->associate_connection) { Curl_ssl->associate_connection(data, conn, FIRSTSOCKET); - if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted) + if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && + conn->bits.sock_accepted) Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET); } } @@ -638,7 +632,8 @@ void Curl_ssl_detach_conn(struct Curl_easy *data, { if(Curl_ssl->disassociate_connection) { Curl_ssl->disassociate_connection(data, FIRSTSOCKET); - if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted) + if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && + conn->bits.sock_accepted) Curl_ssl->disassociate_connection(data, SECONDARYSOCKET); } } @@ -872,6 +867,32 @@ CURLcode Curl_ssl_random(struct Curl_easy *data, } /* + * Curl_ssl_snihost() converts the input host name to a suitable SNI name put + * in data->state.buffer. Returns a pointer to the name (or NULL if a problem) + * and stores the new length in 'olen'. + * + * SNI fields must not have any trailing dot and while RFC 6066 section 3 says + * the SNI field is case insensitive, browsers always send the data lowercase + * and subsequently there are numerous servers out there that don't work + * unless the name is lowercased. + */ + +char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen) +{ + size_t len = strlen(host); + if(len && (host[len-1] == '.')) + len--; + if((long)len >= data->set.buffer_size) + return NULL; + + Curl_strntolower(data->state.buffer, host, len); + data->state.buffer[len] = 0; + if(olen) + *olen = len; + return data->state.buffer; +} + +/* * Public key pem to der conversion */ @@ -969,7 +990,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, if(encode != CURLE_OK) return encode; - encode = Curl_base64_encode(data, (char *)sha256sumdigest, + encode = Curl_base64_encode((char *)sha256sumdigest, CURL_SHA256_DIGEST_LENGTH, &encoded, &encodedlen); Curl_safefree(sha256sumdigest); @@ -1296,8 +1317,6 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_openssl; #elif defined(USE_SCHANNEL) &Curl_ssl_schannel; -#elif defined(USE_MESALINK) - &Curl_ssl_mesalink; #elif defined(USE_BEARSSL) &Curl_ssl_bearssl; #else @@ -1329,9 +1348,6 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_SCHANNEL) &Curl_ssl_schannel, #endif -#if defined(USE_MESALINK) - &Curl_ssl_mesalink, -#endif #if defined(USE_BEARSSL) &Curl_ssl_bearssl, #endif diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index c7bbba0..6bd1e0d 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,6 +34,17 @@ struct ssl_connect_data; #define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ #define SSLSUPP_CAINFO_BLOB (1<<6) +#define ALPN_ACCEPTED "ALPN: server accepted " + +#define VTLS_INFOF_NO_ALPN \ + "ALPN: server did not agree on a protocol. Uses default." +#define VTLS_INFOF_ALPN_OFFER_1STR \ + "ALPN: offers %s" +#define VTLS_INFOF_ALPN_ACCEPTED_1STR \ + ALPN_ACCEPTED "%s" +#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ + ALPN_ACCEPTED "%.*s" + struct Curl_ssl { /* * This *must* be the first entry to allow returning the list of available @@ -85,7 +96,7 @@ struct Curl_ssl { CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256sumlen); - void (*associate_connection)(struct Curl_easy *data, + bool (*associate_connection)(struct Curl_easy *data, struct connectdata *conn, int sockindex); void (*disassociate_connection)(struct Curl_easy *data, int sockindex); @@ -120,7 +131,6 @@ bool Curl_ssl_tls13_ciphersuites(void); #include "schannel.h" /* Schannel SSPI version */ #include "sectransp.h" /* SecureTransport (Darwin) version */ #include "mbedtls.h" /* mbedTLS versions */ -#include "mesalink.h" /* MesaLink versions */ #include "bearssl.h" /* BearSSL versions */ #include "rustls.h" /* rustls versions */ @@ -173,6 +183,7 @@ bool Curl_ssl_tls13_ciphersuites(void); data->set.str[STRING_SSL_PINNEDPUBLICKEY] #endif +char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); bool Curl_ssl_config_matches(struct ssl_primary_config *data, struct ssl_primary_config *needle); bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 8c5b915..da8cb82 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -263,6 +263,8 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, #define use_sni(x) Curl_nop_stmt #endif + DEBUGASSERT(backend); + if(connssl->state == ssl_connection_complete) return CURLE_OK; @@ -322,7 +324,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, } if(!req_method) { - failf(data, "SSL: couldn't create a method!"); + failf(data, "SSL: couldn't create a method"); return CURLE_OUT_OF_MEMORY; } @@ -331,7 +333,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, backend->ctx = SSL_CTX_new(req_method); if(!backend->ctx) { - failf(data, "SSL: couldn't create a context!"); + failf(data, "SSL: couldn't create a context"); return CURLE_OUT_OF_MEMORY; } @@ -460,14 +462,19 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, const char * const hostname = SSL_HOST_NAME(); size_t hostname_len = strlen(hostname); if((hostname_len < USHRT_MAX) && - (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) && + !Curl_inet_pton(AF_INET, hostname, &addr4) #ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) && + && !Curl_inet_pton(AF_INET6, hostname, &addr6) #endif - (wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, hostname, - (unsigned short)hostname_len) != 1)) { - infof(data, "WARNING: failed to configure server name indication (SNI) " - "TLS extension"); + ) { + size_t snilen; + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); + if(!snihost || + wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost, + (unsigned short)snilen) != 1) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } } } #endif @@ -496,7 +503,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, SSL_free(backend->handle); backend->handle = SSL_new(backend->ctx); if(!backend->handle) { - failf(data, "SSL: couldn't create a context (handle)!"); + failf(data, "SSL: couldn't create a context"); return CURLE_OUT_OF_MEMORY; } @@ -519,12 +526,12 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, #ifdef USE_HTTP2 if(data->state.httpwant >= CURL_HTTP_VERSION_2) { strcpy(protocols + strlen(protocols), ALPN_H2 ","); - infof(data, "ALPN, offering %s", ALPN_H2); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); } #endif strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); - infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); if(wolfSSL_UseALPN(backend->handle, protocols, (unsigned)strlen(protocols), @@ -590,10 +597,11 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, int ret = -1; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - const char * const hostname = SSL_HOST_NAME(); const char * const dispname = SSL_HOST_DISPNAME(); const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); + DEBUGASSERT(backend); + ERR_clear_error(); conn->recv[sockindex] = wolfssl_recv; @@ -601,9 +609,10 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, /* Enable RFC2818 checks */ if(SSL_CONN_CONFIG(verifyhost)) { - ret = wolfSSL_check_domain_name(backend->handle, hostname); - if(ret == SSL_FAILURE) - return CURLE_OUT_OF_MEMORY; + char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost || + (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)) + return CURLE_SSL_CONNECT_ERROR; } ret = SSL_connect(backend->handle); @@ -730,7 +739,7 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); if(result) { - failf(data, "SSL: public key does not match pinned public key!"); + failf(data, "SSL: public key does not match pinned public key"); return result; } #else @@ -748,8 +757,7 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); if(rc == SSL_SUCCESS) { - infof(data, "ALPN, server accepted to use %.*s", protocol_len, - protocol); + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol); if(protocol_len == ALPN_HTTP_1_1_LENGTH && !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) @@ -767,7 +775,7 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } else if(rc == SSL_ALPN_NOT_FOUND) - infof(data, "ALPN, server did not agree to a protocol"); + infof(data, VTLS_INFOF_NO_ALPN); else { failf(data, "ALPN, failure getting protocol, error %d", rc); return CURLE_SSL_CONNECT_ERROR; @@ -797,6 +805,7 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); if(SSL_SET_OPTION(primary.sessionid)) { bool incache; @@ -848,6 +857,8 @@ static ssize_t wolfssl_send(struct Curl_easy *data, int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; + DEBUGASSERT(backend); + ERR_clear_error(); rc = SSL_write(backend->handle, mem, memlen); @@ -880,6 +891,8 @@ static void wolfssl_close(struct Curl_easy *data, struct connectdata *conn, (void) data; + DEBUGASSERT(backend); + if(backend->handle) { char buf[32]; /* Maybe the server has already sent a close notify alert. @@ -908,17 +921,22 @@ static ssize_t wolfssl_recv(struct Curl_easy *data, int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; int nread; + DEBUGASSERT(backend); + ERR_clear_error(); nread = SSL_read(backend->handle, buf, buffsize); - if(nread < 0) { + if(nread <= 0) { int err = SSL_get_error(backend->handle, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ break; + case SSL_ERROR_NONE: + /* FALLTHROUGH */ case SSL_ERROR_WANT_READ: + /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ *curlcode = CURLE_AGAIN; @@ -974,6 +992,7 @@ static bool wolfssl_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); if(backend->handle) /* SSL is in use */ return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE; else @@ -994,6 +1013,8 @@ static int wolfssl_shutdown(struct Curl_easy *data, struct connectdata *conn, (void) data; + DEBUGASSERT(backend); + if(backend->handle) { ERR_clear_error(); SSL_free(backend->handle); @@ -1173,6 +1194,7 @@ static void *wolfssl_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->handle; } diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c new file mode 100644 index 0000000..f64acb8 --- /dev/null +++ b/lib/vtls/x509asn1.c @@ -0,0 +1,1409 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) + +#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) +#define WANT_PARSEX509 /* uses Curl_parseX509() */ +#endif + +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ +#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ +#endif + +#if defined(USE_GSKIT) +#define WANT_VERIFYHOST /* uses Curl_verifyhost () */ +#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ +#endif + +#include +#include "urldata.h" +#include "strcase.h" +#include "hostcheck.h" +#include "vtls/vtls.h" +#include "sendf.h" +#include "inet_pton.h" +#include "curl_base64.h" +#include "x509asn1.h" +#include "dynbuf.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Constants. + */ + +/* Largest supported ASN.1 structure. */ +#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ + +/* ASN.1 classes. */ +#define CURL_ASN1_UNIVERSAL 0 +#define CURL_ASN1_APPLICATION 1 +#define CURL_ASN1_CONTEXT_SPECIFIC 2 +#define CURL_ASN1_PRIVATE 3 + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +#define CURL_ASN1_OBJECT_DESCRIPTOR 7 +#define CURL_ASN1_INSTANCE_OF 8 +#define CURL_ASN1_REAL 9 +#define CURL_ASN1_ENUMERATED 10 +#define CURL_ASN1_EMBEDDED 11 +#define CURL_ASN1_UTF8_STRING 12 +#define CURL_ASN1_RELATIVE_OID 13 +#define CURL_ASN1_SEQUENCE 16 +#define CURL_ASN1_SET 17 +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +#define CURL_ASN1_VIDEOTEX_STRING 21 +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +#define CURL_ASN1_GRAPHIC_STRING 25 +#define CURL_ASN1_VISIBLE_STRING 26 +#define CURL_ASN1_GENERAL_STRING 27 +#define CURL_ASN1_UNIVERSAL_STRING 28 +#define CURL_ASN1_CHARACTER_STRING 29 +#define CURL_ASN1_BMP_STRING 30 + +#ifdef WANT_EXTRACT_CERTINFO +/* ASN.1 OID table entry. */ +struct Curl_OID { + const char *numoid; /* Dotted-numeric OID. */ + const char *textoid; /* OID name. */ +}; + +/* ASN.1 OIDs. */ +static const char cnOID[] = "2.5.4.3"; /* Common name. */ +static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ + +static const struct Curl_OID OIDtable[] = { + { "1.2.840.10040.4.1", "dsa" }, + { "1.2.840.10040.4.3", "dsa-with-sha1" }, + { "1.2.840.10045.2.1", "ecPublicKey" }, + { "1.2.840.10045.3.0.1", "c2pnb163v1" }, + { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, + { "1.2.840.10046.2.1", "dhpublicnumber" }, + { "1.2.840.113549.1.1.1", "rsaEncryption" }, + { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, + { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, + { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, + { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, + { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, + { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, + { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, + { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, + { "1.2.840.113549.2.2", "md2" }, + { "1.2.840.113549.2.5", "md5" }, + { "1.3.14.3.2.26", "sha1" }, + { cnOID, "CN" }, + { "2.5.4.4", "SN" }, + { "2.5.4.5", "serialNumber" }, + { "2.5.4.6", "C" }, + { "2.5.4.7", "L" }, + { "2.5.4.8", "ST" }, + { "2.5.4.9", "streetAddress" }, + { "2.5.4.10", "O" }, + { "2.5.4.11", "OU" }, + { "2.5.4.12", "title" }, + { "2.5.4.13", "description" }, + { "2.5.4.17", "postalCode" }, + { "2.5.4.41", "name" }, + { "2.5.4.42", "givenName" }, + { "2.5.4.43", "initials" }, + { "2.5.4.44", "generationQualifier" }, + { "2.5.4.45", "X500UniqueIdentifier" }, + { "2.5.4.46", "dnQualifier" }, + { "2.5.4.65", "pseudonym" }, + { "1.2.840.113549.1.9.1", "emailAddress" }, + { "2.5.4.72", "role" }, + { sanOID, "subjectAltName" }, + { "2.5.29.18", "issuerAltName" }, + { "2.5.29.19", "basicConstraints" }, + { "2.16.840.1.101.3.4.2.4", "sha224" }, + { "2.16.840.1.101.3.4.2.1", "sha256" }, + { "2.16.840.1.101.3.4.2.2", "sha384" }, + { "2.16.840.1.101.3.4.2.3", "sha512" }, + { (const char *) NULL, (const char *) NULL } +}; + +#endif /* WANT_EXTRACT_CERTINFO */ + +/* + * Lightweight ASN.1 parser. + * In particular, it does not check for syntactic/lexical errors. + * It is intended to support certificate information gathering for SSL backends + * that offer a mean to get certificates as a whole, but do not supply + * entry points to get particular certificate sub-fields. + * Please note there is no pretention here to rewrite a full SSL library. + */ + +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) + WARN_UNUSED_RESULT; + +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) +{ + unsigned char b; + unsigned long len; + struct Curl_asn1Element lelem; + + /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' + ending at `end'. + Returns a pointer in source string after the parsed element, or NULL + if an error occurs. */ + if(!beg || !end || beg >= end || !*beg || + (size_t)(end - beg) > CURL_ASN1_MAX) + return NULL; + + /* Process header byte. */ + elem->header = beg; + b = (unsigned char) *beg++; + elem->constructed = (b & 0x20) != 0; + elem->class = (b >> 6) & 3; + b &= 0x1F; + if(b == 0x1F) + return NULL; /* Long tag values not supported here. */ + elem->tag = b; + + /* Process length. */ + if(beg >= end) + return NULL; + b = (unsigned char) *beg++; + if(!(b & 0x80)) + len = b; + else if(!(b &= 0x7F)) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = getASN1Element(&lelem, beg, end); + if(!beg) + return NULL; + } + if(beg >= end) + return NULL; + elem->end = beg; + return beg + 1; + } + else if((unsigned)b > (size_t)(end - beg)) + return NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } + if(len > (size_t)(end - beg)) + return NULL; /* Element data does not fit in source. */ + elem->beg = beg; + elem->end = beg + len; + return elem->end; +} + +#ifdef WANT_EXTRACT_CERTINFO + +/* + * Search the null terminated OID or OID identifier in local table. + * Return the table entry pointer or NULL if not found. + */ +static const struct Curl_OID *searchOID(const char *oid) +{ + const struct Curl_OID *op; + for(op = OIDtable; op->numoid; op++) + if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) + return op; + + return NULL; +} + +/* + * Convert an ASN.1 Boolean value into its string representation. Return the + * dynamically allocated string, or NULL if source is not an ASN.1 Boolean + * value. + */ + +static const char *bool2str(const char *beg, const char *end) +{ + if(end - beg != 1) + return NULL; + return strdup(*beg? "TRUE": "FALSE"); +} + +/* + * Convert an ASN.1 octet string to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *octet2str(const char *beg, const char *end) +{ + struct dynbuf buf; + CURLcode result; + + Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1); + result = Curl_dyn_addn(&buf, "", 0); + + while(!result && beg < end) + result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++); + + return Curl_dyn_ptr(&buf); +} + +static const char *bit2str(const char *beg, const char *end) +{ + /* Convert an ASN.1 bit string to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(++beg > end) + return NULL; + return octet2str(beg, end); +} + +/* + * Convert an ASN.1 integer value into its string representation. + * Return the dynamically allocated string, or NULL if source is not an + * ASN.1 integer value. + */ +static const char *int2str(const char *beg, const char *end) +{ + unsigned long val = 0; + size_t n = end - beg; + + if(!n) + return NULL; + + if(n > 4) + return octet2str(beg, end); + + /* Represent integers <= 32-bit as a single value. */ + if(*beg & 0x80) + val = ~val; + + do + val = (val << 8) | *(const unsigned char *) beg++; + while(beg < end); + return curl_maprintf("%s%lx", val >= 10? "0x": "", val); +} + +/* + * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the + * destination buffer dynamically. The allocation size will normally be too + * large: this is to avoid buffer overflows. + * Terminate the string with a nul byte and return the converted + * string length. + */ +static ssize_t +utf8asn1str(char **to, int type, const char *from, const char *end) +{ + size_t inlength = end - from; + int size = 1; + size_t outlength; + char *buf; + + *to = NULL; + switch(type) { + case CURL_ASN1_BMP_STRING: + size = 2; + break; + case CURL_ASN1_UNIVERSAL_STRING: + size = 4; + break; + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UTF8_STRING: + break; + default: + return -1; /* Conversion not supported. */ + } + + if(inlength % size) + return -1; /* Length inconsistent with character size. */ + if(inlength / size > (SIZE_T_MAX - 1) / 4) + return -1; /* Too big. */ + buf = malloc(4 * (inlength / size) + 1); + if(!buf) + return -1; /* Not enough memory. */ + + if(type == CURL_ASN1_UTF8_STRING) { + /* Just copy. */ + outlength = inlength; + if(outlength) + memcpy(buf, from, outlength); + } + else { + for(outlength = 0; from < end;) { + int charsize; + unsigned int wc; + + wc = 0; + switch(size) { + case 4: + wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *) from++; + /* FALLTHROUGH */ + case 2: + wc = (wc << 8) | *(const unsigned char *) from++; + /* FALLTHROUGH */ + default: /* case 1: */ + wc = (wc << 8) | *(const unsigned char *) from++; + } + charsize = 1; + if(wc >= 0x00000080) { + if(wc >= 0x00000800) { + if(wc >= 0x00010000) { + if(wc >= 0x00200000) { + free(buf); + return -1; /* Invalid char. size for target encoding. */ + } + buf[outlength + 3] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00010000; + charsize++; + } + buf[outlength + 2] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00000800; + charsize++; + } + buf[outlength + 1] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x000000C0; + charsize++; + } + buf[outlength] = (char) wc; + outlength += charsize; + } + } + buf[outlength] = '\0'; + *to = buf; + return outlength; +} + +/* + * Convert an ASN.1 String into its UTF-8 string representation. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *string2str(int type, const char *beg, const char *end) +{ + char *buf; + if(utf8asn1str(&buf, type, beg, end) < 0) + return NULL; + return buf; +} + +/* + * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at + * buf. Return the total number of encoded digits, even if larger than + * `buflen'. + */ +static size_t encodeUint(char *buf, size_t buflen, unsigned int x) +{ + size_t i = 0; + unsigned int y = x / 10; + + if(y) { + i = encodeUint(buf, buflen, y); + x -= y * 10; + } + if(i < buflen) + buf[i] = (char) ('0' + x); + i++; + if(i < buflen) + buf[i] = '\0'; /* Store a terminator if possible. */ + return i; +} + +/* + * Convert an ASN.1 OID into its dotted string representation. + * Store the result in th `n'-byte buffer at `buf'. + * Return the converted string length, or 0 on errors. + */ +static size_t encodeOID(char *buf, size_t buflen, + const char *beg, const char *end) +{ + size_t i; + unsigned int x; + unsigned int y; + + /* Process the first two numbers. */ + y = *(const unsigned char *) beg++; + x = y / 40; + y -= x * 40; + i = encodeUint(buf, buflen, x); + if(i < buflen) + buf[i] = '.'; + i++; + if(i >= buflen) + i += encodeUint(NULL, 0, y); + else + i += encodeUint(buf + i, buflen - i, y); + + /* Process the trailing numbers. */ + while(beg < end) { + if(i < buflen) + buf[i] = '.'; + i++; + x = 0; + do { + if(x & 0xFF000000) + return 0; + y = *(const unsigned char *) beg++; + x = (x << 7) | (y & 0x7F); + } while(y & 0x80); + if(i >= buflen) + i += encodeUint(NULL, 0, x); + else + i += encodeUint(buf + i, buflen - i, x); + } + if(i < buflen) + buf[i] = '\0'; + return i; +} + +/* + * Convert an ASN.1 OID into its dotted or symbolic string representation. + * Return the dynamically allocated string, or NULL if an error occurs. + */ + +static const char *OID2str(const char *beg, const char *end, bool symbolic) +{ + char *buf = NULL; + if(beg < end) { + size_t buflen = encodeOID(NULL, 0, beg, end); + if(buflen) { + buf = malloc(buflen + 1); /* one extra for the zero byte */ + if(buf) { + encodeOID(buf, buflen, beg, end); + buf[buflen] = '\0'; + + if(symbolic) { + const struct Curl_OID *op = searchOID(buf); + if(op) { + free(buf); + buf = strdup(op->textoid); + } + } + } + } + } + return buf; +} + +static const char *GTime2str(const char *beg, const char *end) +{ + const char *tzp; + const char *fracp; + char sec1, sec2; + size_t fracl; + size_t tzl; + const char *sep = ""; + + /* Convert an ASN.1 Generalized time to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) + ; + + /* Get seconds digits. */ + sec1 = '0'; + switch(fracp - beg - 12) { + case 0: + sec2 = '0'; + break; + case 2: + sec1 = fracp[-2]; + /* FALLTHROUGH */ + case 1: + sec2 = fracp[-1]; + break; + default: + return NULL; + } + + /* Scan for timezone, measure fractional seconds. */ + tzp = fracp; + fracl = 0; + if(fracp < end && (*fracp == '.' || *fracp == ',')) { + fracp++; + do + tzp++; + while(tzp < end && *tzp >= '0' && *tzp <= '9'); + /* Strip leading zeroes in fractional seconds. */ + for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) + ; + } + + /* Process timezone. */ + if(tzp >= end) + ; /* Nothing to do. */ + else if(*tzp == 'Z') { + tzp = " GMT"; + end = tzp + 4; + } + else { + sep = " "; + tzp++; + } + + tzl = end - tzp; + return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", + beg, beg + 4, beg + 6, + beg + 8, beg + 10, sec1, sec2, + fracl? ".": "", (int)fracl, fracp, + sep, (int)tzl, tzp); +} + +/* + * Convert an ASN.1 UTC time to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *UTime2str(const char *beg, const char *end) +{ + const char *tzp; + size_t tzl; + const char *sec; + + for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) + ; + /* Get the seconds. */ + sec = beg + 10; + switch(tzp - sec) { + case 0: + sec = "00"; + case 2: + break; + default: + return NULL; + } + + /* Process timezone. */ + if(tzp >= end) + return NULL; + if(*tzp == 'Z') { + tzp = "GMT"; + end = tzp + 3; + } + else + tzp++; + + tzl = end - tzp; + return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", + 20 - (*beg >= '5'), beg, beg + 2, beg + 4, + beg + 6, beg + 8, sec, + (int)tzl, tzp); +} + +/* + * Convert an ASN.1 element to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *ASN1tostr(struct Curl_asn1Element *elem, int type) +{ + if(elem->constructed) + return NULL; /* No conversion of structured elements. */ + + if(!type) + type = elem->tag; /* Type not forced: use element tag as type. */ + + switch(type) { + case CURL_ASN1_BOOLEAN: + return bool2str(elem->beg, elem->end); + case CURL_ASN1_INTEGER: + case CURL_ASN1_ENUMERATED: + return int2str(elem->beg, elem->end); + case CURL_ASN1_BIT_STRING: + return bit2str(elem->beg, elem->end); + case CURL_ASN1_OCTET_STRING: + return octet2str(elem->beg, elem->end); + case CURL_ASN1_NULL: + return strdup(""); + case CURL_ASN1_OBJECT_IDENTIFIER: + return OID2str(elem->beg, elem->end, TRUE); + case CURL_ASN1_UTC_TIME: + return UTime2str(elem->beg, elem->end); + case CURL_ASN1_GENERALIZED_TIME: + return GTime2str(elem->beg, elem->end); + case CURL_ASN1_UTF8_STRING: + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UNIVERSAL_STRING: + case CURL_ASN1_BMP_STRING: + return string2str(type, elem->beg, elem->end); + } + + return NULL; /* Unsupported. */ +} + +/* + * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at + * `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) +{ + struct Curl_asn1Element rdn; + struct Curl_asn1Element atv; + struct Curl_asn1Element oid; + struct Curl_asn1Element value; + size_t l = 0; + const char *p1; + const char *p2; + const char *p3; + const char *str; + + for(p1 = dn->beg; p1 < dn->end;) { + p1 = getASN1Element(&rdn, p1, dn->end); + if(!p1) + return -1; + for(p2 = rdn.beg; p2 < rdn.end;) { + p2 = getASN1Element(&atv, p2, rdn.end); + if(!p2) + return -1; + p3 = getASN1Element(&oid, atv.beg, atv.end); + if(!p3) + return -1; + if(!getASN1Element(&value, p3, atv.end)) + return -1; + str = ASN1tostr(&oid, 0); + if(!str) + return -1; + + /* Encode delimiter. + If attribute has a short uppercase name, delimiter is ", ". */ + if(l) { + for(p3 = str; isupper(*p3); p3++) + ; + for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) { + if(l < buflen) + buf[l] = *p3; + l++; + } + } + + /* Encode attribute name. */ + for(p3 = str; *p3; p3++) { + if(l < buflen) + buf[l] = *p3; + l++; + } + free((char *) str); + + /* Generate equal sign. */ + if(l < buflen) + buf[l] = '='; + l++; + + /* Generate value. */ + str = ASN1tostr(&value, 0); + if(!str) + return -1; + for(p3 = str; *p3; p3++) { + if(l < buflen) + buf[l] = *p3; + l++; + } + free((char *) str); + } + } + + return l; +} + +#endif /* WANT_EXTRACT_CERTINFO */ + +#ifdef WANT_PARSEX509 +/* + * ASN.1 parse an X509 certificate into structure subfields. + * Syntax is assumed to have already been checked by the SSL backend. + * See RFC 5280. + */ +int Curl_parseX509(struct Curl_X509certificate *cert, + const char *beg, const char *end) +{ + struct Curl_asn1Element elem; + struct Curl_asn1Element tbsCertificate; + const char *ccp; + static const char defaultVersion = 0; /* v1. */ + + cert->certificate.header = NULL; + cert->certificate.beg = beg; + cert->certificate.end = end; + + /* Get the sequence content. */ + if(!getASN1Element(&elem, beg, end)) + return -1; /* Invalid bounds/size. */ + beg = elem.beg; + end = elem.end; + + /* Get tbsCertificate. */ + beg = getASN1Element(&tbsCertificate, beg, end); + if(!beg) + return -1; + /* Skip the signatureAlgorithm. */ + beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + if(!beg) + return -1; + /* Get the signatureValue. */ + if(!getASN1Element(&cert->signature, beg, end)) + return -1; + + /* Parse TBSCertificate. */ + beg = tbsCertificate.beg; + end = tbsCertificate.end; + /* Get optional version, get serialNumber. */ + cert->version.header = NULL; + cert->version.beg = &defaultVersion; + cert->version.end = &defaultVersion + sizeof(defaultVersion); + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + if(elem.tag == 0) { + if(!getASN1Element(&cert->version, elem.beg, elem.end)) + return -1; + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + cert->serialNumber = elem; + /* Get signature algorithm. */ + beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + /* Get issuer. */ + beg = getASN1Element(&cert->issuer, beg, end); + if(!beg) + return -1; + /* Get notBefore and notAfter. */ + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->notAfter, ccp, elem.end)) + return -1; + /* Get subject. */ + beg = getASN1Element(&cert->subject, beg, end); + if(!beg) + return -1; + /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ + beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); + if(!beg) + return -1; + ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, + cert->subjectPublicKeyInfo.beg, + cert->subjectPublicKeyInfo.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->subjectPublicKey, ccp, + cert->subjectPublicKeyInfo.end)) + return -1; + /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ + cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; + cert->extensions.tag = elem.tag = 0; + cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; + cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; + cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; + cert->extensions.header = NULL; + cert->extensions.beg = cert->extensions.end = ""; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + if(elem.tag == 1) { + cert->issuerUniqueID = elem; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + } + if(elem.tag == 2) { + cert->subjectUniqueID = elem; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + } + if(elem.tag == 3) + if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) + return -1; + return 0; +} + +#endif /* WANT_PARSEX509 */ + +#ifdef WANT_EXTRACT_CERTINFO + +/* + * Copy at most 64-characters, terminate with a newline and returns the + * effective number of stored characters. + */ +static size_t copySubstring(char *to, const char *from) +{ + size_t i; + for(i = 0; i < 64; i++) { + to[i] = *from; + if(!*from++) + break; + } + + to[i++] = '\n'; + return i; +} + +static const char *dumpAlgo(struct Curl_asn1Element *param, + const char *beg, const char *end) +{ + struct Curl_asn1Element oid; + + /* Get algorithm parameters and return algorithm name. */ + + beg = getASN1Element(&oid, beg, end); + if(!beg) + return NULL; + param->header = NULL; + param->tag = 0; + param->beg = param->end = end; + if(beg < end) + if(!getASN1Element(param, beg, end)) + return NULL; + return OID2str(oid.beg, oid.end, TRUE); +} + +/* 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) + 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; +} + +/* 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; + const char *p; + + /* Generate all information records for the public key. */ + + /* Get the public key (single element). */ + if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) + return 1; + + if(strcasecompare(algo, "rsaEncryption")) { + const char *q; + unsigned long len; + + p = getASN1Element(&elem, pk.beg, pk.end); + if(!p) + return 1; + + /* Compute key length. */ + for(q = elem.beg; !*q && q < elem.end; q++) + ; + len = (unsigned long)((elem.end - q) * 8); + if(len) { + unsigned int i; + for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) + len--; + } + if(len > 32) + elem.beg = q; /* Strip leading zero bytes. */ + if(!certnum) + infof(data, " RSA Public Key (%lu bits)", len); + if(data->set.ssl.certinfo) { + q = curl_maprintf("%lu", len); + if(q) { + CURLcode result = + Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q); + free((char *) q); + if(result) + return 1; + } + } + /* Generate coefficients. */ + if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) + return 1; + if(!getASN1Element(&elem, p, pk.end)) + 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) { + if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) + return 1; + p = getASN1Element(&elem, p, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) + return 1; + if(getASN1Element(&elem, p, param->end)) { + if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) + return 1; + } + } + } + } + else if(strcasecompare(algo, "dhpublicnumber")) { + p = getASN1Element(&elem, param->beg, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dh(p)", &elem)) + return 1; + if(getASN1Element(&elem, param->beg, param->end)) { + 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; +} + +/* + * Convert an ASN.1 distinguished name into a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *DNtostr(struct Curl_asn1Element *dn) +{ + char *buf = NULL; + ssize_t buflen = encodeDN(NULL, 0, dn); + + if(buflen >= 0) { + buf = malloc(buflen + 1); + if(buf) { + if(encodeDN(buf, buflen + 1, dn) == -1) { + free(buf); + return NULL; + } + buf[buflen] = '\0'; + } + } + return buf; +} + +CURLcode Curl_extract_certinfo(struct Curl_easy *data, + int certnum, + const char *beg, + const char *end) +{ + struct Curl_X509certificate cert; + struct Curl_asn1Element param; + const char *ccp; + char *cp1; + size_t cl1; + char *cp2; + CURLcode result = CURLE_OK; + unsigned long version; + size_t i; + size_t j; + + if(!data->set.ssl.certinfo) + if(certnum) + return CURLE_OK; + + /* Prepare the certificate information for curl_easy_getinfo(). */ + + /* Extract the certificate ASN.1 elements. */ + if(Curl_parseX509(&cert, beg, end)) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Subject. */ + ccp = DNtostr(&cert.subject); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + 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); + + /* Issuer. */ + ccp = DNtostr(&cert.issuer); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + 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; + for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) + version = (version << 8) | *(const unsigned char *) ccp; + if(data->set.ssl.certinfo) { + ccp = curl_maprintf("%lx", version); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + 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); + + /* Serial number. */ + ccp = ASN1tostr(&cert.serialNumber, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + 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, + cert.signatureAlgorithm.end); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + 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) + 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) + 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, + cert.subjectPublicKeyAlgorithm.end); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + 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) + 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(cert.certificate.beg, + cert.certificate.end - cert.certificate.beg, + &cp1, &cl1); + if(result) + return result; + /* Compute the number of characters in final certificate string. Format is: + -----BEGIN CERTIFICATE-----\n + \n + . + . + . + -----END CERTIFICATE-----\n + */ + i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26; + cp2 = malloc(i + 1); + if(!cp2) { + free(cp1); + return CURLE_OUT_OF_MEMORY; + } + /* Build the certificate string. */ + i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----"); + for(j = 0; j < cl1; j += 64) + i += copySubstring(cp2 + i, cp1 + j); + i += copySubstring(cp2 + i, "-----END CERTIFICATE-----"); + cp2[i] = '\0'; + free(cp1); + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); + if(!certnum) + infof(data, "%s", cp2); + free(cp2); + return result; +} + +#endif /* WANT_EXTRACT_CERTINFO */ + +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL + * or USE_SECTRANSP */ + +#ifdef WANT_VERIFYHOST + +static const char *checkOID(const char *beg, const char *end, + const char *oid) +{ + struct Curl_asn1Element e; + const char *ccp; + const char *p; + bool matched; + + /* Check if first ASN.1 element at `beg' is the given OID. + Return a pointer in the source after the OID if found, else NULL. */ + + ccp = getASN1Element(&e, beg, end); + if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER) + return NULL; + + p = OID2str(e.beg, e.end, FALSE); + if(!p) + return NULL; + + matched = !strcmp(p, oid); + free((char *) p); + return matched? ccp: NULL; +} + +CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + const char *beg, const char *end) +{ + struct Curl_X509certificate cert; + struct Curl_asn1Element dn; + struct Curl_asn1Element elem; + struct Curl_asn1Element ext; + struct Curl_asn1Element name; + const char *p; + const char *q; + char *dnsname; + int matched = -1; + size_t addrlen = (size_t) -1; + ssize_t len; + const char * const hostname = SSL_HOST_NAME(); + const char * const dispname = SSL_HOST_DISPNAME(); + size_t hostlen = strlen(hostname); +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + + /* Verify that connection server matches info in X509 certificate at + `beg'..`end'. */ + + if(!SSL_CONN_CONFIG(verifyhost)) + return CURLE_OK; + + if(Curl_parseX509(&cert, beg, end)) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Get the server IP address. */ +#ifdef ENABLE_IPV6 + if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, hostname, &addr)) + addrlen = sizeof(struct in6_addr); + else +#endif + if(Curl_inet_pton(AF_INET, hostname, &addr)) + addrlen = sizeof(struct in_addr); + + /* Process extensions. */ + for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) { + p = getASN1Element(&ext, p, cert.extensions.end); + if(!p) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Check if extension is a subjectAlternativeName. */ + ext.beg = checkOID(ext.beg, ext.end, sanOID); + if(ext.beg) { + ext.beg = getASN1Element(&elem, ext.beg, ext.end); + if(!ext.beg) + return CURLE_PEER_FAILED_VERIFICATION; + /* Skip critical if present. */ + if(elem.tag == CURL_ASN1_BOOLEAN) { + ext.beg = getASN1Element(&elem, ext.beg, ext.end); + if(!ext.beg) + return CURLE_PEER_FAILED_VERIFICATION; + } + /* Parse the octet string contents: is a single sequence. */ + if(!getASN1Element(&elem, elem.beg, elem.end)) + return CURLE_PEER_FAILED_VERIFICATION; + /* Check all GeneralNames. */ + for(q = elem.beg; matched != 1 && q < elem.end;) { + q = getASN1Element(&name, q, elem.end); + if(!q) + break; + switch(name.tag) { + case 2: /* DNS name. */ + len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, + name.beg, name.end); + if(len > 0 && (size_t)len == strlen(dnsname)) + matched = Curl_cert_hostcheck(dnsname, + (size_t)len, hostname, hostlen); + else + matched = 0; + free(dnsname); + break; + + case 7: /* IP address. */ + matched = (size_t) (name.end - name.beg) == addrlen && + !memcmp(&addr, name.beg, addrlen); + break; + } + } + } + } + + switch(matched) { + case 1: + /* an alternative name matched the server hostname */ + infof(data, " subjectAltName: %s matched", dispname); + return CURLE_OK; + case 0: + /* an alternative name field existed, but didn't match and then + we MUST fail */ + infof(data, " subjectAltName does not match %s", dispname); + return CURLE_PEER_FAILED_VERIFICATION; + } + + /* Process subject. */ + name.header = NULL; + name.beg = name.end = ""; + q = cert.subject.beg; + /* we have to look to the last occurrence of a commonName in the + distinguished one to get the most significant one. */ + while(q < cert.subject.end) { + q = getASN1Element(&dn, q, cert.subject.end); + if(!q) + break; + for(p = dn.beg; p < dn.end;) { + p = getASN1Element(&elem, p, dn.end); + if(!p) + return CURLE_PEER_FAILED_VERIFICATION; + /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */ + elem.beg = checkOID(elem.beg, elem.end, cnOID); + if(elem.beg) + name = elem; /* Latch CN. */ + } + } + + /* Check the CN if found. */ + if(!getASN1Element(&elem, name.beg, name.end)) + failf(data, "SSL: unable to obtain common name from peer certificate"); + else { + len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end); + if(len < 0) { + free(dnsname); + return CURLE_OUT_OF_MEMORY; + } + if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ + failf(data, "SSL: illegal cert name field"); + else if(Curl_cert_hostcheck((const char *) dnsname, + len, hostname, hostlen)) { + infof(data, " common name: %s (matched)", dnsname); + free(dnsname); + return CURLE_OK; + } + else + failf(data, "SSL: certificate subject name '%s' does not match " + "target host name '%s'", dnsname, dispname); + free(dnsname); + } + + return CURLE_PEER_FAILED_VERIFICATION; +} + +#endif /* WANT_VERIFYHOST */ diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h new file mode 100644 index 0000000..db7df0e --- /dev/null +++ b/lib/vtls/x509asn1.h @@ -0,0 +1,78 @@ +#ifndef HEADER_CURL_X509ASN1_H +#define HEADER_CURL_X509ASN1_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) + +#include "urldata.h" + +/* + * Types. + */ + +/* ASN.1 parsed element. */ +struct Curl_asn1Element { + const char *header; /* Pointer to header byte. */ + const char *beg; /* Pointer to element data. */ + const char *end; /* Pointer to 1st byte after element. */ + unsigned char class; /* ASN.1 element class. */ + unsigned char tag; /* ASN.1 element tag. */ + bool constructed; /* Element is constructed. */ +}; + +/* X509 certificate: RFC 5280. */ +struct Curl_X509certificate { + struct Curl_asn1Element certificate; + struct Curl_asn1Element version; + struct Curl_asn1Element serialNumber; + struct Curl_asn1Element signatureAlgorithm; + struct Curl_asn1Element signature; + struct Curl_asn1Element issuer; + struct Curl_asn1Element notBefore; + struct Curl_asn1Element notAfter; + struct Curl_asn1Element subject; + struct Curl_asn1Element subjectPublicKeyInfo; + struct Curl_asn1Element subjectPublicKeyAlgorithm; + struct Curl_asn1Element subjectPublicKey; + struct Curl_asn1Element issuerUniqueID; + struct Curl_asn1Element subjectUniqueID; + struct Curl_asn1Element extensions; +}; + +/* + * Prototypes. + */ + +int Curl_parseX509(struct Curl_X509certificate *cert, + const char *beg, const char *end); +CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, + const char *beg, const char *end); +CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + const char *beg, const char *end); +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL + * or USE_SECTRANSP */ +#endif /* HEADER_CURL_X509ASN1_H */ diff --git a/lib/warnless.c b/lib/warnless.c index 15c8156..0336a41 100644 --- a/lib/warnless.c +++ b/lib/warnless.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -360,7 +360,7 @@ curl_socket_t curlx_sitosk(int i) #endif /* USE_WINSOCK */ -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) ssize_t curlx_read(int fd, void *buf, size_t count) { @@ -372,7 +372,7 @@ ssize_t curlx_write(int fd, const void *buf, size_t count) return (ssize_t)write(fd, buf, curlx_uztoui(count)); } -#endif /* WIN32 || _WIN32 */ +#endif /* WIN32 */ #if defined(__INTEL_COMPILER) && defined(__unix__) diff --git a/lib/warnless.h b/lib/warnless.h index 2c619bf..37ac5ba 100644 --- a/lib/warnless.h +++ b/lib/warnless.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, , 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,6 +22,8 @@ * ***************************************************************************/ +#include "curl_setup.h" + #ifdef USE_WINSOCK #include /* for curl_socket_t */ #endif @@ -65,7 +67,7 @@ curl_socket_t curlx_sitosk(int i); #endif /* USE_WINSOCK */ -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) ssize_t curlx_read(int fd, void *buf, size_t count); @@ -78,7 +80,7 @@ ssize_t curlx_write(int fd, const void *buf, size_t count); # define write(fd, buf, count) curlx_write(fd, buf, count) #endif -#endif /* WIN32 || _WIN32 */ +#endif /* WIN32 */ #if defined(__INTEL_COMPILER) && defined(__unix__) diff --git a/lib/x509asn1.c b/lib/x509asn1.c deleted file mode 100644 index 0341543..0000000 --- a/lib/x509asn1.c +++ /dev/null @@ -1,1329 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) - -#include -#include "urldata.h" -#include "strcase.h" -#include "hostcheck.h" -#include "vtls/vtls.h" -#include "sendf.h" -#include "inet_pton.h" -#include "curl_base64.h" -#include "x509asn1.h" -#include "dynbuf.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* ASN.1 OIDs. */ -static const char cnOID[] = "2.5.4.3"; /* Common name. */ -static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ - -static const struct Curl_OID OIDtable[] = { - { "1.2.840.10040.4.1", "dsa" }, - { "1.2.840.10040.4.3", "dsa-with-sha1" }, - { "1.2.840.10045.2.1", "ecPublicKey" }, - { "1.2.840.10045.3.0.1", "c2pnb163v1" }, - { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, - { "1.2.840.10046.2.1", "dhpublicnumber" }, - { "1.2.840.113549.1.1.1", "rsaEncryption" }, - { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, - { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, - { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, - { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, - { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, - { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, - { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, - { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, - { "1.2.840.113549.2.2", "md2" }, - { "1.2.840.113549.2.5", "md5" }, - { "1.3.14.3.2.26", "sha1" }, - { cnOID, "CN" }, - { "2.5.4.4", "SN" }, - { "2.5.4.5", "serialNumber" }, - { "2.5.4.6", "C" }, - { "2.5.4.7", "L" }, - { "2.5.4.8", "ST" }, - { "2.5.4.9", "streetAddress" }, - { "2.5.4.10", "O" }, - { "2.5.4.11", "OU" }, - { "2.5.4.12", "title" }, - { "2.5.4.13", "description" }, - { "2.5.4.17", "postalCode" }, - { "2.5.4.41", "name" }, - { "2.5.4.42", "givenName" }, - { "2.5.4.43", "initials" }, - { "2.5.4.44", "generationQualifier" }, - { "2.5.4.45", "X500UniqueIdentifier" }, - { "2.5.4.46", "dnQualifier" }, - { "2.5.4.65", "pseudonym" }, - { "1.2.840.113549.1.9.1", "emailAddress" }, - { "2.5.4.72", "role" }, - { sanOID, "subjectAltName" }, - { "2.5.29.18", "issuerAltName" }, - { "2.5.29.19", "basicConstraints" }, - { "2.16.840.1.101.3.4.2.4", "sha224" }, - { "2.16.840.1.101.3.4.2.1", "sha256" }, - { "2.16.840.1.101.3.4.2.2", "sha384" }, - { "2.16.840.1.101.3.4.2.3", "sha512" }, - { (const char *) NULL, (const char *) NULL } -}; - -/* - * Lightweight ASN.1 parser. - * In particular, it does not check for syntactic/lexical errors. - * It is intended to support certificate information gathering for SSL backends - * that offer a mean to get certificates as a whole, but do not supply - * entry points to get particular certificate sub-fields. - * Please note there is no pretention here to rewrite a full SSL library. - */ - -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) - WARN_UNUSED_RESULT; - -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) -{ - unsigned char b; - unsigned long len; - struct Curl_asn1Element lelem; - - /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' - ending at `end'. - Returns a pointer in source string after the parsed element, or NULL - if an error occurs. */ - if(!beg || !end || beg >= end || !*beg || - (size_t)(end - beg) > CURL_ASN1_MAX) - return NULL; - - /* Process header byte. */ - elem->header = beg; - b = (unsigned char) *beg++; - elem->constructed = (b & 0x20) != 0; - elem->class = (b >> 6) & 3; - b &= 0x1F; - if(b == 0x1F) - return NULL; /* Long tag values not supported here. */ - elem->tag = b; - - /* Process length. */ - if(beg >= end) - return NULL; - b = (unsigned char) *beg++; - if(!(b & 0x80)) - len = b; - else if(!(b &= 0x7F)) { - /* Unspecified length. Since we have all the data, we can determine the - effective length by skipping element until an end element is found. */ - if(!elem->constructed) - return NULL; - elem->beg = beg; - while(beg < end && *beg) { - beg = getASN1Element(&lelem, beg, end); - if(!beg) - return NULL; - } - if(beg >= end) - return NULL; - elem->end = beg; - return beg + 1; - } - else if((unsigned)b > (size_t)(end - beg)) - return NULL; /* Does not fit in source. */ - else { - /* Get long length. */ - len = 0; - do { - if(len & 0xFF000000L) - return NULL; /* Lengths > 32 bits are not supported. */ - len = (len << 8) | (unsigned char) *beg++; - } while(--b); - } - if(len > (size_t)(end - beg)) - return NULL; /* Element data does not fit in source. */ - elem->beg = beg; - elem->end = beg + len; - return elem->end; -} - -/* - * Search the null terminated OID or OID identifier in local table. - * Return the table entry pointer or NULL if not found. - */ -static const struct Curl_OID *searchOID(const char *oid) -{ - const struct Curl_OID *op; - for(op = OIDtable; op->numoid; op++) - if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) - return op; - - return NULL; -} - -/* - * Convert an ASN.1 Boolean value into its string representation. Return the - * dynamically allocated string, or NULL if source is not an ASN.1 Boolean - * value. - */ - -static const char *bool2str(const char *beg, const char *end) -{ - if(end - beg != 1) - return NULL; - return strdup(*beg? "TRUE": "FALSE"); -} - -/* - * Convert an ASN.1 octet string to a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *octet2str(const char *beg, const char *end) -{ - struct dynbuf buf; - CURLcode result; - - Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1); - result = Curl_dyn_addn(&buf, "", 0); - - while(!result && beg < end) - result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++); - - return Curl_dyn_ptr(&buf); -} - -static const char *bit2str(const char *beg, const char *end) -{ - /* Convert an ASN.1 bit string to a printable string. - Return the dynamically allocated string, or NULL if an error occurs. */ - - if(++beg > end) - return NULL; - return octet2str(beg, end); -} - -/* - * Convert an ASN.1 integer value into its string representation. - * Return the dynamically allocated string, or NULL if source is not an - * ASN.1 integer value. - */ -static const char *int2str(const char *beg, const char *end) -{ - unsigned long val = 0; - size_t n = end - beg; - - if(!n) - return NULL; - - if(n > 4) - return octet2str(beg, end); - - /* Represent integers <= 32-bit as a single value. */ - if(*beg & 0x80) - val = ~val; - - do - val = (val << 8) | *(const unsigned char *) beg++; - while(beg < end); - return curl_maprintf("%s%lx", val >= 10? "0x": "", val); -} - -/* - * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the - * destination buffer dynamically. The allocation size will normally be too - * large: this is to avoid buffer overflows. - * Terminate the string with a nul byte and return the converted - * string length. - */ -static ssize_t -utf8asn1str(char **to, int type, const char *from, const char *end) -{ - size_t inlength = end - from; - int size = 1; - size_t outlength; - char *buf; - - *to = NULL; - switch(type) { - case CURL_ASN1_BMP_STRING: - size = 2; - break; - case CURL_ASN1_UNIVERSAL_STRING: - size = 4; - break; - case CURL_ASN1_NUMERIC_STRING: - case CURL_ASN1_PRINTABLE_STRING: - case CURL_ASN1_TELETEX_STRING: - case CURL_ASN1_IA5_STRING: - case CURL_ASN1_VISIBLE_STRING: - case CURL_ASN1_UTF8_STRING: - break; - default: - return -1; /* Conversion not supported. */ - } - - if(inlength % size) - return -1; /* Length inconsistent with character size. */ - if(inlength / size > (SIZE_T_MAX - 1) / 4) - return -1; /* Too big. */ - buf = malloc(4 * (inlength / size) + 1); - if(!buf) - return -1; /* Not enough memory. */ - - if(type == CURL_ASN1_UTF8_STRING) { - /* Just copy. */ - outlength = inlength; - if(outlength) - memcpy(buf, from, outlength); - } - else { - for(outlength = 0; from < end;) { - int charsize; - unsigned int wc; - - wc = 0; - switch(size) { - case 4: - wc = (wc << 8) | *(const unsigned char *) from++; - wc = (wc << 8) | *(const unsigned char *) from++; - /* FALLTHROUGH */ - case 2: - wc = (wc << 8) | *(const unsigned char *) from++; - /* FALLTHROUGH */ - default: /* case 1: */ - wc = (wc << 8) | *(const unsigned char *) from++; - } - charsize = 1; - if(wc >= 0x00000080) { - if(wc >= 0x00000800) { - if(wc >= 0x00010000) { - if(wc >= 0x00200000) { - free(buf); - return -1; /* Invalid char. size for target encoding. */ - } - buf[outlength + 3] = (char) (0x80 | (wc & 0x3F)); - wc = (wc >> 6) | 0x00010000; - charsize++; - } - buf[outlength + 2] = (char) (0x80 | (wc & 0x3F)); - wc = (wc >> 6) | 0x00000800; - charsize++; - } - buf[outlength + 1] = (char) (0x80 | (wc & 0x3F)); - wc = (wc >> 6) | 0x000000C0; - charsize++; - } - buf[outlength] = (char) wc; - outlength += charsize; - } - } - buf[outlength] = '\0'; - *to = buf; - return outlength; -} - -/* - * Convert an ASN.1 String into its UTF-8 string representation. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *string2str(int type, const char *beg, const char *end) -{ - char *buf; - if(utf8asn1str(&buf, type, beg, end) < 0) - return NULL; - return buf; -} - -/* - * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at - * buf. Return the total number of encoded digits, even if larger than - * `buflen'. - */ -static size_t encodeUint(char *buf, size_t buflen, unsigned int x) -{ - size_t i = 0; - unsigned int y = x / 10; - - if(y) { - i = encodeUint(buf, buflen, y); - x -= y * 10; - } - if(i < buflen) - buf[i] = (char) ('0' + x); - i++; - if(i < buflen) - buf[i] = '\0'; /* Store a terminator if possible. */ - return i; -} - -/* - * Convert an ASN.1 OID into its dotted string representation. - * Store the result in th `n'-byte buffer at `buf'. - * Return the converted string length, or 0 on errors. - */ -static size_t encodeOID(char *buf, size_t buflen, - const char *beg, const char *end) -{ - size_t i; - unsigned int x; - unsigned int y; - - /* Process the first two numbers. */ - y = *(const unsigned char *) beg++; - x = y / 40; - y -= x * 40; - i = encodeUint(buf, buflen, x); - if(i < buflen) - buf[i] = '.'; - i++; - if(i >= buflen) - i += encodeUint(NULL, 0, y); - else - i += encodeUint(buf + i, buflen - i, y); - - /* Process the trailing numbers. */ - while(beg < end) { - if(i < buflen) - buf[i] = '.'; - i++; - x = 0; - do { - if(x & 0xFF000000) - return 0; - y = *(const unsigned char *) beg++; - x = (x << 7) | (y & 0x7F); - } while(y & 0x80); - if(i >= buflen) - i += encodeUint(NULL, 0, x); - else - i += encodeUint(buf + i, buflen - i, x); - } - if(i < buflen) - buf[i] = '\0'; - return i; -} - -/* - * Convert an ASN.1 OID into its dotted or symbolic string representation. - * Return the dynamically allocated string, or NULL if an error occurs. - */ - -static const char *OID2str(const char *beg, const char *end, bool symbolic) -{ - char *buf = NULL; - if(beg < end) { - size_t buflen = encodeOID(NULL, 0, beg, end); - if(buflen) { - buf = malloc(buflen + 1); /* one extra for the zero byte */ - if(buf) { - encodeOID(buf, buflen, beg, end); - buf[buflen] = '\0'; - - if(symbolic) { - const struct Curl_OID *op = searchOID(buf); - if(op) { - free(buf); - buf = strdup(op->textoid); - } - } - } - } - } - return buf; -} - -static const char *GTime2str(const char *beg, const char *end) -{ - const char *tzp; - const char *fracp; - char sec1, sec2; - size_t fracl; - size_t tzl; - const char *sep = ""; - - /* Convert an ASN.1 Generalized time to a printable string. - Return the dynamically allocated string, or NULL if an error occurs. */ - - for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) - ; - - /* Get seconds digits. */ - sec1 = '0'; - switch(fracp - beg - 12) { - case 0: - sec2 = '0'; - break; - case 2: - sec1 = fracp[-2]; - /* FALLTHROUGH */ - case 1: - sec2 = fracp[-1]; - break; - default: - return NULL; - } - - /* Scan for timezone, measure fractional seconds. */ - tzp = fracp; - fracl = 0; - if(fracp < end && (*fracp == '.' || *fracp == ',')) { - fracp++; - do - tzp++; - while(tzp < end && *tzp >= '0' && *tzp <= '9'); - /* Strip leading zeroes in fractional seconds. */ - for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) - ; - } - - /* Process timezone. */ - if(tzp >= end) - ; /* Nothing to do. */ - else if(*tzp == 'Z') { - tzp = " GMT"; - end = tzp + 4; - } - else { - sep = " "; - tzp++; - } - - tzl = end - tzp; - return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", - beg, beg + 4, beg + 6, - beg + 8, beg + 10, sec1, sec2, - fracl? ".": "", (int)fracl, fracp, - sep, (int)tzl, tzp); -} - -/* - * Convert an ASN.1 UTC time to a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *UTime2str(const char *beg, const char *end) -{ - const char *tzp; - size_t tzl; - const char *sec; - - for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) - ; - /* Get the seconds. */ - sec = beg + 10; - switch(tzp - sec) { - case 0: - sec = "00"; - case 2: - break; - default: - return NULL; - } - - /* Process timezone. */ - if(tzp >= end) - return NULL; - if(*tzp == 'Z') { - tzp = "GMT"; - end = tzp + 3; - } - else - tzp++; - - tzl = end - tzp; - return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", - 20 - (*beg >= '5'), beg, beg + 2, beg + 4, - beg + 6, beg + 8, sec, - (int)tzl, tzp); -} - -/* - * Convert an ASN.1 element to a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *ASN1tostr(struct Curl_asn1Element *elem, int type) -{ - if(elem->constructed) - return NULL; /* No conversion of structured elements. */ - - if(!type) - type = elem->tag; /* Type not forced: use element tag as type. */ - - switch(type) { - case CURL_ASN1_BOOLEAN: - return bool2str(elem->beg, elem->end); - case CURL_ASN1_INTEGER: - case CURL_ASN1_ENUMERATED: - return int2str(elem->beg, elem->end); - case CURL_ASN1_BIT_STRING: - return bit2str(elem->beg, elem->end); - case CURL_ASN1_OCTET_STRING: - return octet2str(elem->beg, elem->end); - case CURL_ASN1_NULL: - return strdup(""); - case CURL_ASN1_OBJECT_IDENTIFIER: - return OID2str(elem->beg, elem->end, TRUE); - case CURL_ASN1_UTC_TIME: - return UTime2str(elem->beg, elem->end); - case CURL_ASN1_GENERALIZED_TIME: - return GTime2str(elem->beg, elem->end); - case CURL_ASN1_UTF8_STRING: - case CURL_ASN1_NUMERIC_STRING: - case CURL_ASN1_PRINTABLE_STRING: - case CURL_ASN1_TELETEX_STRING: - case CURL_ASN1_IA5_STRING: - case CURL_ASN1_VISIBLE_STRING: - case CURL_ASN1_UNIVERSAL_STRING: - case CURL_ASN1_BMP_STRING: - return string2str(type, elem->beg, elem->end); - } - - return NULL; /* Unsupported. */ -} - -/* - * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at - * `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) -{ - struct Curl_asn1Element rdn; - struct Curl_asn1Element atv; - struct Curl_asn1Element oid; - struct Curl_asn1Element value; - size_t l = 0; - const char *p1; - const char *p2; - const char *p3; - const char *str; - - for(p1 = dn->beg; p1 < dn->end;) { - p1 = getASN1Element(&rdn, p1, dn->end); - if(!p1) - return -1; - for(p2 = rdn.beg; p2 < rdn.end;) { - p2 = getASN1Element(&atv, p2, rdn.end); - if(!p2) - return -1; - p3 = getASN1Element(&oid, atv.beg, atv.end); - if(!p3) - return -1; - if(!getASN1Element(&value, p3, atv.end)) - return -1; - str = ASN1tostr(&oid, 0); - if(!str) - return -1; - - /* Encode delimiter. - If attribute has a short uppercase name, delimiter is ", ". */ - if(l) { - for(p3 = str; isupper(*p3); p3++) - ; - for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) { - if(l < buflen) - buf[l] = *p3; - l++; - } - } - - /* Encode attribute name. */ - for(p3 = str; *p3; p3++) { - if(l < buflen) - buf[l] = *p3; - l++; - } - free((char *) str); - - /* Generate equal sign. */ - if(l < buflen) - buf[l] = '='; - l++; - - /* Generate value. */ - str = ASN1tostr(&value, 0); - if(!str) - return -1; - for(p3 = str; *p3; p3++) { - if(l < buflen) - buf[l] = *p3; - l++; - } - free((char *) str); - } - } - - return l; -} - -/* - * Convert an ASN.1 distinguished name into a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *DNtostr(struct Curl_asn1Element *dn) -{ - char *buf = NULL; - ssize_t buflen = encodeDN(NULL, 0, dn); - - if(buflen >= 0) { - buf = malloc(buflen + 1); - if(buf) { - if(encodeDN(buf, buflen + 1, dn) == -1) { - free(buf); - return NULL; - } - buf[buflen] = '\0'; - } - } - return buf; -} - -/* - * ASN.1 parse an X509 certificate into structure subfields. - * Syntax is assumed to have already been checked by the SSL backend. - * See RFC 5280. - */ -int Curl_parseX509(struct Curl_X509certificate *cert, - const char *beg, const char *end) -{ - struct Curl_asn1Element elem; - struct Curl_asn1Element tbsCertificate; - const char *ccp; - static const char defaultVersion = 0; /* v1. */ - - cert->certificate.header = NULL; - cert->certificate.beg = beg; - cert->certificate.end = end; - - /* Get the sequence content. */ - if(!getASN1Element(&elem, beg, end)) - return -1; /* Invalid bounds/size. */ - beg = elem.beg; - end = elem.end; - - /* Get tbsCertificate. */ - beg = getASN1Element(&tbsCertificate, beg, end); - if(!beg) - return -1; - /* Skip the signatureAlgorithm. */ - beg = getASN1Element(&cert->signatureAlgorithm, beg, end); - if(!beg) - return -1; - /* Get the signatureValue. */ - if(!getASN1Element(&cert->signature, beg, end)) - return -1; - - /* Parse TBSCertificate. */ - beg = tbsCertificate.beg; - end = tbsCertificate.end; - /* Get optional version, get serialNumber. */ - cert->version.header = NULL; - cert->version.beg = &defaultVersion; - cert->version.end = &defaultVersion + sizeof(defaultVersion); - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - if(elem.tag == 0) { - if(!getASN1Element(&cert->version, elem.beg, elem.end)) - return -1; - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - cert->serialNumber = elem; - /* Get signature algorithm. */ - beg = getASN1Element(&cert->signatureAlgorithm, beg, end); - /* Get issuer. */ - beg = getASN1Element(&cert->issuer, beg, end); - if(!beg) - return -1; - /* Get notBefore and notAfter. */ - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); - if(!ccp) - return -1; - if(!getASN1Element(&cert->notAfter, ccp, elem.end)) - return -1; - /* Get subject. */ - beg = getASN1Element(&cert->subject, beg, end); - if(!beg) - return -1; - /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ - beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); - if(!beg) - return -1; - ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, - cert->subjectPublicKeyInfo.beg, - cert->subjectPublicKeyInfo.end); - if(!ccp) - return -1; - if(!getASN1Element(&cert->subjectPublicKey, ccp, - cert->subjectPublicKeyInfo.end)) - return -1; - /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ - cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; - cert->extensions.tag = elem.tag = 0; - cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; - cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; - cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; - cert->extensions.header = NULL; - cert->extensions.beg = cert->extensions.end = ""; - if(beg < end) { - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - if(elem.tag == 1) { - cert->issuerUniqueID = elem; - if(beg < end) { - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - } - if(elem.tag == 2) { - cert->subjectUniqueID = elem; - if(beg < end) { - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - } - if(elem.tag == 3) - if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) - return -1; - return 0; -} - - -/* - * Copy at most 64-characters, terminate with a newline and returns the - * effective number of stored characters. - */ -static size_t copySubstring(char *to, const char *from) -{ - size_t i; - for(i = 0; i < 64; i++) { - to[i] = *from; - if(!*from++) - break; - } - - to[i++] = '\n'; - return i; -} - -static const char *dumpAlgo(struct Curl_asn1Element *param, - const char *beg, const char *end) -{ - struct Curl_asn1Element oid; - - /* Get algorithm parameters and return algorithm name. */ - - beg = getASN1Element(&oid, beg, end); - if(!beg) - return NULL; - param->header = NULL; - param->tag = 0; - param->beg = param->end = end; - if(beg < end) - if(!getASN1Element(param, beg, end)) - return NULL; - return OID2str(oid.beg, oid.end, TRUE); -} - -/* 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) - 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; -} - -/* 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; - const char *p; - - /* Generate all information records for the public key. */ - - /* Get the public key (single element). */ - if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) - return 1; - - if(strcasecompare(algo, "rsaEncryption")) { - const char *q; - unsigned long len; - - p = getASN1Element(&elem, pk.beg, pk.end); - if(!p) - return 1; - - /* Compute key length. */ - for(q = elem.beg; !*q && q < elem.end; q++) - ; - len = (unsigned long)((elem.end - q) * 8); - if(len) { - unsigned int i; - for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) - len--; - } - if(len > 32) - elem.beg = q; /* Strip leading zero bytes. */ - if(!certnum) - infof(data, " RSA Public Key (%lu bits)", len); - if(data->set.ssl.certinfo) { - q = curl_maprintf("%lu", len); - if(q) { - CURLcode result = - Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q); - free((char *) q); - if(result) - return 1; - } - } - /* Generate coefficients. */ - if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) - return 1; - if(!getASN1Element(&elem, p, pk.end)) - 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) { - if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) - return 1; - p = getASN1Element(&elem, p, param->end); - if(p) { - if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) - return 1; - if(getASN1Element(&elem, p, param->end)) { - if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) - return 1; - if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) - return 1; - } - } - } - } - else if(strcasecompare(algo, "dhpublicnumber")) { - p = getASN1Element(&elem, param->beg, param->end); - if(p) { - if(do_pubkey_field(data, certnum, "dh(p)", &elem)) - return 1; - if(getASN1Element(&elem, param->beg, param->end)) { - 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, - int certnum, - const char *beg, - const char *end) -{ - struct Curl_X509certificate cert; - struct Curl_asn1Element param; - const char *ccp; - char *cp1; - size_t cl1; - char *cp2; - CURLcode result = CURLE_OK; - unsigned long version; - size_t i; - size_t j; - - if(!data->set.ssl.certinfo) - if(certnum) - return CURLE_OK; - - /* Prepare the certificate information for curl_easy_getinfo(). */ - - /* Extract the certificate ASN.1 elements. */ - if(Curl_parseX509(&cert, beg, end)) - return CURLE_PEER_FAILED_VERIFICATION; - - /* Subject. */ - ccp = DNtostr(&cert.subject); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - 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); - - /* Issuer. */ - ccp = DNtostr(&cert.issuer); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - 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; - for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) - version = (version << 8) | *(const unsigned char *) ccp; - if(data->set.ssl.certinfo) { - ccp = curl_maprintf("%lx", version); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - 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); - - /* Serial number. */ - ccp = ASN1tostr(&cert.serialNumber, 0); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - 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, - cert.signatureAlgorithm.end); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - 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) - 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) - 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, - cert.subjectPublicKeyAlgorithm.end); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - 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) - 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, - cert.certificate.end - cert.certificate.beg, - &cp1, &cl1); - if(result) - return result; - /* Compute the number of characters in final certificate string. Format is: - -----BEGIN CERTIFICATE-----\n - \n - . - . - . - -----END CERTIFICATE-----\n - */ - i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26; - cp2 = malloc(i + 1); - if(!cp2) { - free(cp1); - return CURLE_OUT_OF_MEMORY; - } - /* Build the certificate string. */ - i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----"); - for(j = 0; j < cl1; j += 64) - i += copySubstring(cp2 + i, cp1 + j); - i += copySubstring(cp2 + i, "-----END CERTIFICATE-----"); - cp2[i] = '\0'; - free(cp1); - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); - if(!certnum) - infof(data, "%s", cp2); - free(cp2); - return result; -} - -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ - -#if defined(USE_GSKIT) - -static const char *checkOID(const char *beg, const char *end, - const char *oid) -{ - struct Curl_asn1Element e; - const char *ccp; - const char *p; - bool matched; - - /* Check if first ASN.1 element at `beg' is the given OID. - Return a pointer in the source after the OID if found, else NULL. */ - - ccp = getASN1Element(&e, beg, end); - if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER) - return NULL; - - p = OID2str(e.beg, e.end, FALSE); - if(!p) - return NULL; - - matched = !strcmp(p, oid); - free((char *) p); - return matched? ccp: NULL; -} - -CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - const char *beg, const char *end) -{ - struct Curl_X509certificate cert; - struct Curl_asn1Element dn; - struct Curl_asn1Element elem; - struct Curl_asn1Element ext; - struct Curl_asn1Element name; - const char *p; - const char *q; - char *dnsname; - int matched = -1; - size_t addrlen = (size_t) -1; - ssize_t len; - const char * const hostname = SSL_HOST_NAME(); - const char * const dispname = SSL_HOST_DISPNAME(); -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif - - /* Verify that connection server matches info in X509 certificate at - `beg'..`end'. */ - - if(!SSL_CONN_CONFIG(verifyhost)) - return CURLE_OK; - - if(Curl_parseX509(&cert, beg, end)) - return CURLE_PEER_FAILED_VERIFICATION; - - /* Get the server IP address. */ -#ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, hostname, &addr)) - addrlen = sizeof(struct in6_addr); - else -#endif - if(Curl_inet_pton(AF_INET, hostname, &addr)) - addrlen = sizeof(struct in_addr); - - /* Process extensions. */ - for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) { - p = getASN1Element(&ext, p, cert.extensions.end); - if(!p) - return CURLE_PEER_FAILED_VERIFICATION; - - /* Check if extension is a subjectAlternativeName. */ - ext.beg = checkOID(ext.beg, ext.end, sanOID); - if(ext.beg) { - ext.beg = getASN1Element(&elem, ext.beg, ext.end); - if(!ext.beg) - return CURLE_PEER_FAILED_VERIFICATION; - /* Skip critical if present. */ - if(elem.tag == CURL_ASN1_BOOLEAN) { - ext.beg = getASN1Element(&elem, ext.beg, ext.end); - if(!ext.beg) - return CURLE_PEER_FAILED_VERIFICATION; - } - /* Parse the octet string contents: is a single sequence. */ - if(!getASN1Element(&elem, elem.beg, elem.end)) - return CURLE_PEER_FAILED_VERIFICATION; - /* Check all GeneralNames. */ - for(q = elem.beg; matched != 1 && q < elem.end;) { - q = getASN1Element(&name, q, elem.end); - if(!q) - break; - switch(name.tag) { - case 2: /* DNS name. */ - len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, - name.beg, name.end); - if(len > 0 && (size_t)len == strlen(dnsname)) - matched = Curl_cert_hostcheck(dnsname, hostname); - else - matched = 0; - free(dnsname); - break; - - case 7: /* IP address. */ - matched = (size_t) (name.end - name.beg) == addrlen && - !memcmp(&addr, name.beg, addrlen); - break; - } - } - } - } - - switch(matched) { - case 1: - /* an alternative name matched the server hostname */ - infof(data, " subjectAltName: %s matched", dispname); - return CURLE_OK; - case 0: - /* an alternative name field existed, but didn't match and then - we MUST fail */ - infof(data, " subjectAltName does not match %s", dispname); - return CURLE_PEER_FAILED_VERIFICATION; - } - - /* Process subject. */ - name.header = NULL; - name.beg = name.end = ""; - q = cert.subject.beg; - /* we have to look to the last occurrence of a commonName in the - distinguished one to get the most significant one. */ - while(q < cert.subject.end) { - q = getASN1Element(&dn, q, cert.subject.end); - if(!q) - break; - for(p = dn.beg; p < dn.end;) { - p = getASN1Element(&elem, p, dn.end); - if(!p) - return CURLE_PEER_FAILED_VERIFICATION; - /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */ - elem.beg = checkOID(elem.beg, elem.end, cnOID); - if(elem.beg) - name = elem; /* Latch CN. */ - } - } - - /* Check the CN if found. */ - if(!getASN1Element(&elem, name.beg, name.end)) - failf(data, "SSL: unable to obtain common name from peer certificate"); - else { - len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end); - if(len < 0) { - free(dnsname); - return CURLE_OUT_OF_MEMORY; - } - if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ - failf(data, "SSL: illegal cert name field"); - else if(Curl_cert_hostcheck((const char *) dnsname, hostname)) { - infof(data, " common name: %s (matched)", dnsname); - free(dnsname); - return CURLE_OK; - } - else - failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", dnsname, dispname); - free(dnsname); - } - - return CURLE_PEER_FAILED_VERIFICATION; -} - -#endif /* USE_GSKIT */ diff --git a/lib/x509asn1.h b/lib/x509asn1.h deleted file mode 100644 index 3b51eee..0000000 --- a/lib/x509asn1.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef HEADER_CURL_X509ASN1_H -#define HEADER_CURL_X509ASN1_H - -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) - -#include "urldata.h" - -/* - * Constants. - */ - -/* Largest supported ASN.1 structure. */ -#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ - -/* ASN.1 classes. */ -#define CURL_ASN1_UNIVERSAL 0 -#define CURL_ASN1_APPLICATION 1 -#define CURL_ASN1_CONTEXT_SPECIFIC 2 -#define CURL_ASN1_PRIVATE 3 - -/* ASN.1 types. */ -#define CURL_ASN1_BOOLEAN 1 -#define CURL_ASN1_INTEGER 2 -#define CURL_ASN1_BIT_STRING 3 -#define CURL_ASN1_OCTET_STRING 4 -#define CURL_ASN1_NULL 5 -#define CURL_ASN1_OBJECT_IDENTIFIER 6 -#define CURL_ASN1_OBJECT_DESCRIPTOR 7 -#define CURL_ASN1_INSTANCE_OF 8 -#define CURL_ASN1_REAL 9 -#define CURL_ASN1_ENUMERATED 10 -#define CURL_ASN1_EMBEDDED 11 -#define CURL_ASN1_UTF8_STRING 12 -#define CURL_ASN1_RELATIVE_OID 13 -#define CURL_ASN1_SEQUENCE 16 -#define CURL_ASN1_SET 17 -#define CURL_ASN1_NUMERIC_STRING 18 -#define CURL_ASN1_PRINTABLE_STRING 19 -#define CURL_ASN1_TELETEX_STRING 20 -#define CURL_ASN1_VIDEOTEX_STRING 21 -#define CURL_ASN1_IA5_STRING 22 -#define CURL_ASN1_UTC_TIME 23 -#define CURL_ASN1_GENERALIZED_TIME 24 -#define CURL_ASN1_GRAPHIC_STRING 25 -#define CURL_ASN1_VISIBLE_STRING 26 -#define CURL_ASN1_GENERAL_STRING 27 -#define CURL_ASN1_UNIVERSAL_STRING 28 -#define CURL_ASN1_CHARACTER_STRING 29 -#define CURL_ASN1_BMP_STRING 30 - - -/* - * Types. - */ - -/* ASN.1 parsed element. */ -struct Curl_asn1Element { - const char *header; /* Pointer to header byte. */ - const char *beg; /* Pointer to element data. */ - const char *end; /* Pointer to 1st byte after element. */ - unsigned char class; /* ASN.1 element class. */ - unsigned char tag; /* ASN.1 element tag. */ - bool constructed; /* Element is constructed. */ -}; - - -/* ASN.1 OID table entry. */ -struct Curl_OID { - const char *numoid; /* Dotted-numeric OID. */ - const char *textoid; /* OID name. */ -}; - - -/* X509 certificate: RFC 5280. */ -struct Curl_X509certificate { - struct Curl_asn1Element certificate; - struct Curl_asn1Element version; - struct Curl_asn1Element serialNumber; - struct Curl_asn1Element signatureAlgorithm; - struct Curl_asn1Element signature; - struct Curl_asn1Element issuer; - struct Curl_asn1Element notBefore; - struct Curl_asn1Element notAfter; - struct Curl_asn1Element subject; - struct Curl_asn1Element subjectPublicKeyInfo; - struct Curl_asn1Element subjectPublicKeyAlgorithm; - struct Curl_asn1Element subjectPublicKey; - struct Curl_asn1Element issuerUniqueID; - struct Curl_asn1Element subjectUniqueID; - struct Curl_asn1Element extensions; -}; - -/* - * Prototypes. - */ - -const char *Curl_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end); -const char *Curl_ASN1tostr(struct Curl_asn1Element *elem, int type); -const char *Curl_DNtostr(struct Curl_asn1Element *dn); -int Curl_parseX509(struct Curl_X509certificate *cert, - const char *beg, const char *end); -CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, - const char *beg, const char *end); -CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - const char *beg, const char *end); -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ -#endif /* HEADER_CURL_X509ASN1_H */ -- cgit v0.12