diff options
author | Curl Upstream <curl-library@cool.haxx.se> | 2015-08-11 18:13:01 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2015-08-12 18:18:06 (GMT) |
commit | 706542615828488a5ad197d0ef3dd5e42eb739c4 (patch) | |
tree | 66bac7b097b4502a0e41eaba51c6b82312bc7f16 | |
parent | 3fe5d9bff98b4716e219516c30d71462495324f4 (diff) | |
download | CMake-706542615828488a5ad197d0ef3dd5e42eb739c4.zip CMake-706542615828488a5ad197d0ef3dd5e42eb739c4.tar.gz CMake-706542615828488a5ad197d0ef3dd5e42eb739c4.tar.bz2 |
curl 7.44.0 (reduced)
Extract upstream curl using the following shell code.
url=git://github.com/bagder/curl.git &&
v=7.44.0 &&
r=1a7f66a3 &&
paths="
CMake/*
CMakeLists.txt
COPYING
include/curl/*.h
include/curl/curlbuild.h.cmake
lib/*.c
lib/*.h
lib/CMakeLists.txt
lib/Makefile.inc
lib/curl_config.h.cmake
lib/libcurl.rc
lib/vtls/*.c
lib/vtls/*.h
" &&
mkdir curl-$v-g$r-reduced &&
git clone $url curl-git &&
date=$(cd curl-git && git log -n 1 --format='%cd' $r) &&
(cd curl-git && git checkout $r &&
git archive --format=tar $r -- $paths) |
(cd curl-$v-g$r-reduced && tar xv &&
rm lib/config-*.h) &&
echo "g$r date: $date"
181 files changed, 14792 insertions, 11390 deletions
diff --git a/CMake/CurlCheckCSourceCompiles.cmake b/CMake/CurlCheckCSourceCompiles.cmake deleted file mode 100644 index c5d2bab..0000000 --- a/CMake/CurlCheckCSourceCompiles.cmake +++ /dev/null @@ -1,71 +0,0 @@ -# - Check if the source code provided in the SOURCE argument compiles. -# CURL_CHECK_C_SOURCE_COMPILES(SOURCE VAR) -# - macro which checks if the source code compiles -# SOURCE - source code to try to compile -# VAR - variable to store whether the source code compiled -# -# The following variables may be set before calling this macro to -# modify the way the check is run: -# -# CMAKE_REQUIRED_FLAGS = string of compile command line flags -# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) -# CMAKE_REQUIRED_INCLUDES = list of include directories -# CMAKE_REQUIRED_LIBRARIES = list of libraries to link - -macro(CURL_CHECK_C_SOURCE_COMPILES SOURCE VAR) - if("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") - set(message "${VAR}") - # If the number of arguments is greater than 2 (SOURCE VAR) - if(${ARGC} GREATER 2) - # then add the third argument as a message - set(message "${ARGV2} (${VAR})") - endif() - set(MACRO_CHECK_FUNCTION_DEFINITIONS - "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") - if(CMAKE_REQUIRED_LIBRARIES) - set(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - endif() - if(CMAKE_REQUIRED_INCLUDES) - set(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - endif() - set(src "") - foreach(def ${EXTRA_DEFINES}) - set(src "${src}#define ${def} 1\n") - endforeach(def) - foreach(inc ${HEADER_INCLUDES}) - set(src "${src}#include <${inc}>\n") - endforeach(inc) - - set(src "${src}\nint main() { ${SOURCE} ; return 0; }") - set(CMAKE_CONFIGURABLE_FILE_CONTENT "${src}") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CMakeConfigurableFile.in - "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c" - IMMEDIATE) - message(STATUS "Performing Test ${message}") - try_compile(${VAR} - ${CMAKE_BINARY_DIR} - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c - COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} - "${CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}" - "${CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}" - OUTPUT_VARIABLE OUTPUT) - if(${VAR}) - set(${VAR} 1 CACHE INTERNAL "Test ${message}") - message(STATUS "Performing Test ${message} - Success") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Performing C SOURCE FILE Test ${message} succeded with the following output:\n" - "${OUTPUT}\n" - "Source file was:\n${src}\n") - else() - message(STATUS "Performing Test ${message} - Failed") - set(${VAR} "" CACHE INTERNAL "Test ${message}") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Performing C SOURCE FILE Test ${message} failed with the following output:\n" - "${OUTPUT}\n" - "Source file was:\n${src}\n") - endif() - endif() -endmacro() diff --git a/CMake/CurlCheckCSourceRuns.cmake b/CMake/CurlCheckCSourceRuns.cmake deleted file mode 100644 index 6b14af8..0000000 --- a/CMake/CurlCheckCSourceRuns.cmake +++ /dev/null @@ -1,83 +0,0 @@ -# - Check if the source code provided in the SOURCE argument compiles and runs. -# CURL_CHECK_C_SOURCE_RUNS(SOURCE VAR) -# - macro which checks if the source code runs -# SOURCE - source code to try to compile -# VAR - variable to store size if the type exists. -# -# The following variables may be set before calling this macro to -# modify the way the check is run: -# -# CMAKE_REQUIRED_FLAGS = string of compile command line flags -# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) -# CMAKE_REQUIRED_INCLUDES = list of include directories -# CMAKE_REQUIRED_LIBRARIES = list of libraries to link - -macro(CURL_CHECK_C_SOURCE_RUNS SOURCE VAR) - if("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") - set(message "${VAR}") - # If the number of arguments is greater than 2 (SOURCE VAR) - if(${ARGC} GREATER 2) - # then add the third argument as a message - set(message "${ARGV2} (${VAR})") - endif(${ARGC} GREATER 2) - set(MACRO_CHECK_FUNCTION_DEFINITIONS - "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") - if(CMAKE_REQUIRED_LIBRARIES) - set(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - else(CMAKE_REQUIRED_LIBRARIES) - set(CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES) - endif(CMAKE_REQUIRED_LIBRARIES) - if(CMAKE_REQUIRED_INCLUDES) - set(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - else(CMAKE_REQUIRED_INCLUDES) - set(CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES) - endif(CMAKE_REQUIRED_INCLUDES) - set(src "") - foreach(def ${EXTRA_DEFINES}) - set(src "${src}#define ${def} 1\n") - endforeach(def) - foreach(inc ${HEADER_INCLUDES}) - set(src "${src}#include <${inc}>\n") - endforeach(inc) - - set(src "${src}\nint main() { ${SOURCE} ; return 0; }") - set(CMAKE_CONFIGURABLE_FILE_CONTENT "${src}") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CMakeConfigurableFile.in - "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c" - IMMEDIATE) - message(STATUS "Performing Test ${message}") - try_run(${VAR} ${VAR}_COMPILED - ${CMAKE_BINARY_DIR} - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c - COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} - "${CURL_CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}" - "${CURL_CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}" - OUTPUT_VARIABLE OUTPUT) - # if it did not compile make the return value fail code of 1 - if(NOT ${VAR}_COMPILED) - set(${VAR} 1) - endif(NOT ${VAR}_COMPILED) - # if the return value was 0 then it worked - set(result_var ${${VAR}}) - if("${result_var}" EQUAL 0) - set(${VAR} 1 CACHE INTERNAL "Test ${message}") - message(STATUS "Performing Test ${message} - Success") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Performing C SOURCE FILE Test ${message} succeded with the following output:\n" - "${OUTPUT}\n" - "Return value: ${${VAR}}\n" - "Source file was:\n${src}\n") - else("${result_var}" EQUAL 0) - message(STATUS "Performing Test ${message} - Failed") - set(${VAR} "" CACHE INTERNAL "Test ${message}") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Performing C SOURCE FILE Test ${message} failed with the following output:\n" - "${OUTPUT}\n" - "Return value: ${result_var}\n" - "Source file was:\n${src}\n") - endif("${result_var}" EQUAL 0) - endif("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN") -endmacro(CURL_CHECK_C_SOURCE_RUNS) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 199871a..04d5e7e 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -71,264 +71,88 @@ main () } #endif -#ifdef HAVE_GETHOSTBYADDR_R_5 +/* tests for gethostbyaddr_r or gethostbyname_r */ +#if defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \ + defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \ + defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT) +# define _REENTRANT + /* no idea whether _REENTRANT is always set, just invent a new flag */ +# define TEST_GETHOSTBYFOO_REENTRANT +#endif +#if defined(HAVE_GETHOSTBYADDR_R_5) || \ + defined(HAVE_GETHOSTBYADDR_R_7) || \ + defined(HAVE_GETHOSTBYADDR_R_8) || \ + defined(HAVE_GETHOSTBYNAME_R_3) || \ + defined(HAVE_GETHOSTBYNAME_R_5) || \ + defined(HAVE_GETHOSTBYNAME_R_6) || \ + defined(TEST_GETHOSTBYFOO_REENTRANT) #include <sys/types.h> #include <netdb.h> -int -main () -{ - -char * address; -int length; -int type; -struct hostent h; -struct hostent_data hdata; -int rc; -#ifndef gethostbyaddr_r - (void)gethostbyaddr_r; -#endif -rc = gethostbyaddr_r(address, length, type, &h, &hdata); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYADDR_R_5_REENTRANT -#define _REENTRANT -#include <sys/types.h> -#include <netdb.h> -int -main () -{ - -char * address; -int length;q -int type; -struct hostent h; -struct hostent_data hdata; -int rc; -#ifndef gethostbyaddr_r - (void)gethostbyaddr_r; -#endif -rc = gethostbyaddr_r(address, length, type, &h, &hdata); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYADDR_R_7 -#include <sys/types.h> -#include <netdb.h> -int -main () -{ - -char * address; -int length; -int type; -struct hostent h; -char buffer[8192]; -int h_errnop; -struct hostent * hp; - -#ifndef gethostbyaddr_r - (void)gethostbyaddr_r; -#endif -hp = gethostbyaddr_r(address, length, type, &h, - buffer, 8192, &h_errnop); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYADDR_R_7_REENTRANT -#define _REENTRANT -#include <sys/types.h> -#include <netdb.h> -int -main () +int main(void) { - -char * address; -int length; -int type; -struct hostent h; -char buffer[8192]; -int h_errnop; -struct hostent * hp; - -#ifndef gethostbyaddr_r - (void)gethostbyaddr_r; -#endif -hp = gethostbyaddr_r(address, length, type, &h, - buffer, 8192, &h_errnop); - ; - return 0; -} + char *address = "example.com"; + int length = 0; + int type = 0; + struct hostent h; + int rc = 0; +#if defined(HAVE_GETHOSTBYADDR_R_5) || \ + defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \ + \ + defined(HAVE_GETHOSTBYNAME_R_3) || \ + defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) + struct hostent_data hdata; +#elif defined(HAVE_GETHOSTBYADDR_R_7) || \ + defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \ + defined(HAVE_GETHOSTBYADDR_R_8) || \ + defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \ + \ + defined(HAVE_GETHOSTBYNAME_R_5) || \ + defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \ + defined(HAVE_GETHOSTBYNAME_R_6) || \ + defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT) + char buffer[8192]; + int h_errnop; + struct hostent *hp; #endif -#ifdef HAVE_GETHOSTBYADDR_R_8 -#include <sys/types.h> -#include <netdb.h> -int -main () -{ - -char * address; -int length; -int type; -struct hostent h; -char buffer[8192]; -int h_errnop; -struct hostent * hp; -int rc; - -#ifndef gethostbyaddr_r - (void)gethostbyaddr_r; -#endif -rc = gethostbyaddr_r(address, length, type, &h, - buffer, 8192, &hp, &h_errnop); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYADDR_R_8_REENTRANT -#define _REENTRANT -#include <sys/types.h> -#include <netdb.h> -int -main () -{ - -char * address; -int length; -int type; -struct hostent h; -char buffer[8192]; -int h_errnop; -struct hostent * hp; -int rc; #ifndef gethostbyaddr_r (void)gethostbyaddr_r; #endif -rc = gethostbyaddr_r(address, length, type, &h, - buffer, 8192, &hp, &h_errnop); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYNAME_R_3 -#include <string.h> -#include <sys/types.h> -#include <netdb.h> -#undef NULL -#define NULL (void *)0 - -int -main () -{ - -struct hostent_data data; -#ifndef gethostbyname_r - (void)gethostbyname_r; -#endif -gethostbyname_r(NULL, NULL, NULL); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYNAME_R_3_REENTRANT -#define _REENTRANT -#include <string.h> -#include <sys/types.h> -#include <netdb.h> -#undef NULL -#define NULL (void *)0 - -int -main () -{ - -struct hostent_data data; -#ifndef gethostbyname_r - (void)gethostbyname_r; -#endif -gethostbyname_r(NULL, NULL, NULL); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYNAME_R_5 -#include <sys/types.h> -#include <netinet/in.h> -#include <netdb.h> -#undef NULL -#define NULL (void *)0 - -int -main () -{ -#ifndef gethostbyname_r - (void)gethostbyname_r; -#endif -gethostbyname_r(NULL, NULL, NULL, 0, NULL); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYNAME_R_5_REENTRANT -#define _REENTRANT -#include <sys/types.h> -#include <netdb.h> -#undef NULL -#define NULL (void *)0 -int -main () -{ - -#ifndef gethostbyname_r - (void)gethostbyname_r; -#endif -gethostbyname_r(NULL, NULL, NULL, 0, NULL); - ; +#if defined(HAVE_GETHOSTBYADDR_R_5) || \ + defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) + rc = gethostbyaddr_r(address, length, type, &h, &hdata); +#elif defined(HAVE_GETHOSTBYADDR_R_7) || \ + defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) + hp = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &h_errnop); + (void)hp; +#elif defined(HAVE_GETHOSTBYADDR_R_8) || \ + defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) + rc = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &hp, &h_errnop); +#endif + +#if defined(HAVE_GETHOSTBYNAME_R_3) || \ + defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) + rc = gethostbyname_r(address, &h, &hdata); +#elif defined(HAVE_GETHOSTBYNAME_R_5) || \ + defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) + rc = gethostbyname_r(address, &h, buffer, 8192, 0, &h_errnop); + (void)hp; /* not used for test */ +#elif defined(HAVE_GETHOSTBYNAME_R_6) || \ + defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT) + rc = gethostbyname_r(address, &h, buffer, 8192, &hp, &h_errnop); +#endif + + (void)length; + (void)type; + (void)rc; return 0; } #endif -#ifdef HAVE_GETHOSTBYNAME_R_6 -#include <sys/types.h> -#include <netdb.h> -#undef NULL -#define NULL (void *)0 - -int -main () -{ -#ifndef gethostbyname_r - (void)gethostbyname_r; -#endif -gethostbyname_r(NULL, NULL, NULL, 0, NULL, NULL); - ; - return 0; -} -#endif -#ifdef HAVE_GETHOSTBYNAME_R_6_REENTRANT -#define _REENTRANT -#include <sys/types.h> -#include <netdb.h> -#undef NULL -#define NULL (void *)0 - -int -main () -{ - -#ifndef gethostbyname_r - (void)gethostbyname_r; -#endif -gethostbyname_r(NULL, NULL, NULL, 0, NULL, NULL); - ; - return 0; -} -#endif #ifdef HAVE_SOCKLEN_T #ifdef _WIN32 #include <ws2tcpip.h> diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake new file mode 100644 index 0000000..dfaeaf3 --- /dev/null +++ b/CMake/FindGSS.cmake @@ -0,0 +1,289 @@ +# - Try to find the GSS Kerberos library +# Once done this will define +# +# GSS_ROOT_DIR - Set this variable to the root installation of GSS +# +# Read-Only variables: +# GSS_FOUND - system has the Heimdal library +# GSS_FLAVOUR - "MIT" or "Heimdal" if anything found. +# GSS_INCLUDE_DIR - the Heimdal include directory +# GSS_LIBRARIES - The libraries needed to use GSS +# GSS_LINK_DIRECTORIES - Directories to add to linker search path +# GSS_LINKER_FLAGS - Additional linker flags +# GSS_COMPILER_FLAGS - Additional compiler flags +# GSS_VERSION - This is set to version advertised by pkg-config or read from manifest. +# In case the library is found but no version info availabe it'll be set to "unknown" + +set(_MIT_MODNAME mit-krb5-gssapi) +set(_HEIMDAL_MODNAME heimdal-gssapi) + +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckTypeSize) + +set(_GSS_ROOT_HINTS + "${GSS_ROOT_DIR}" + "$ENV{GSS_ROOT_DIR}" +) + +# try to find library using system pkg-config if user didn't specify root dir +if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") + if(UNIX) + find_package(PkgConfig QUIET) + pkg_search_module(_GSS_PKG ${_MIT_MODNAME} ${_HEIMDAL_MODNAME}) + list(APPEND _GSS_ROOT_HINTS "${_GSS_PKG_PREFIX}") + elseif(WIN32) + list(APPEND _GSS_ROOT_HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]") + endif() +endif() + +if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approach. + find_file(_GSS_CONFIGURE_SCRIPT + NAMES + "krb5-config" + HINTS + ${_GSS_ROOT_HINTS} + PATH_SUFFIXES + bin + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + ) + + # if not found in user-supplied directories, maybe system knows better + find_file(_GSS_CONFIGURE_SCRIPT + NAMES + "krb5-config" + PATH_SUFFIXES + bin + ) + + if(_GSS_CONFIGURE_SCRIPT) + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--cflags" "gssapi" + OUTPUT_VARIABLE _GSS_CFLAGS + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + ) +message(STATUS "CFLAGS: ${_GSS_CFLAGS}") + if(NOT _GSS_CONFIGURE_FAILED) # 0 means success + # should also work in an odd case when multiple directories are given + string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS) + string(REGEX REPLACE " +-I" ";" _GSS_CFLAGS "${_GSS_CFLAGS}") + string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1"_GSS_CFLAGS "${_GSS_CFLAGS}") + + foreach(_flag ${_GSS_CFLAGS}) + if(_flag MATCHES "^-I.*") + string(REGEX REPLACE "^-I" "" _val "${_flag}") + list(APPEND _GSS_INCLUDE_DIR "${_val}") + else() + list(APPEND _GSS_COMPILER_FLAGS "${_flag}") + endif() + endforeach() + endif() + + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--libs" "gssapi" + OUTPUT_VARIABLE _GSS_LIB_FLAGS + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + ) +message(STATUS "LDFLAGS: ${_GSS_LIB_FLAGS}") + if(NOT _GSS_CONFIGURE_FAILED) # 0 means success + # this script gives us libraries and link directories. Blah. We have to deal with it. + string(STRIP "${_GSS_LIB_FLAGS}" _GSS_LIB_FLAGS) + string(REGEX REPLACE " +-(L|l)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}") + string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1"_GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}") + + foreach(_flag ${_GSS_LIB_FLAGS}) + if(_flag MATCHES "^-l.*") + string(REGEX REPLACE "^-l" "" _val "${_flag}") + list(APPEND _GSS_LIBRARIES "${_val}") + elseif(_flag MATCHES "^-L.*") + string(REGEX REPLACE "^-L" "" _val "${_flag}") + list(APPEND _GSS_LINK_DIRECTORIES "${_val}") + else() + list(APPEND _GSS_LINKER_FLAGS "${_flag}") + endif() + endforeach() + endif() + + + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--version" + OUTPUT_VARIABLE _GSS_VERSION + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + ) + + # older versions may not have the "--version" parameter. In this case we just don't care. + if(_GSS_CONFIGURE_FAILED) + set(_GSS_VERSION 0) + endif() + + + execute_process( + COMMAND ${_GSS_CONFIGURE_SCRIPT} "--vendor" + OUTPUT_VARIABLE _GSS_VENDOR + RESULT_VARIABLE _GSS_CONFIGURE_FAILED + ) + + # older versions may not have the "--vendor" parameter. In this case we just don't care. + if(_GSS_CONFIGURE_FAILED) + set(GSS_FLAVOUR "Heimdal") # most probably, shouldn't really matter + else() + if(_GSS_VENDOR MATCHES ".*H|heimdal.*") + set(GSS_FLAVOUR "Heimdal") + else() + set(GSS_FLAVOUR "MIT") + endif() + endif() + + else() # either there is no config script or we are on platform that doesn't provide one (Windows?) + + find_path(_GSS_INCLUDE_DIR + NAMES + "gssapi/gssapi.h" + HINTS + ${_GSS_ROOT_HINTS} + PATH_SUFFIXES + include + inc + ) + + if(_GSS_INCLUDE_DIR) #jay, we've found something + set(CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIR}") + check_include_files( "gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _GSS_HAVE_MIT_HEADERS) + + if(_GSS_HAVE_MIT_HEADERS) + set(GSS_FLAVOUR "MIT") + else() + # prevent compiling the header - just check if we can include it + set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D__ROKEN_H__") + check_include_file( "roken.h" _GSS_HAVE_ROKEN_H) + + check_include_file( "heimdal/roken.h" _GSS_HAVE_HEIMDAL_ROKEN_H) + if(_GSS_HAVE_ROKEN_H OR _GSS_HAVE_HEIMDAL_ROKEN_H) + set(GSS_FLAVOUR "Heimdal") + endif() + set(CMAKE_REQUIRED_DEFINITIONS "") + endif() + else() + # I'm not convienced if this is the right way but this is what autotools do at the moment + find_path(_GSS_INCLUDE_DIR + NAMES + "gssapi.h" + HINTS + ${_GSS_ROOT_HINTS} + PATH_SUFFIXES + include + inc + ) + + if(_GSS_INCLUDE_DIR) + set(GSS_FLAVOUR "Heimdal") + endif() + endif() + + # if we have headers, check if we can link libraries + if(GSS_FLAVOUR) + set(_GSS_LIBDIR_SUFFIXES "") + set(_GSS_LIBDIR_HINTS ${_GSS_ROOT_HINTS}) + get_filename_component(_GSS_CALCULATED_POTENTIAL_ROOT "${_GSS_INCLUDE_DIR}" PATH) + list(APPEND _GSS_LIBDIR_HINTS ${_GSS_CALCULATED_POTENTIAL_ROOT}) + + if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND _GSS_LIBDIR_SUFFIXES "lib/AMD64") + if(GSS_FLAVOUR STREQUAL "MIT") + set(_GSS_LIBNAME "gssapi64") + else() + set(_GSS_LIBNAME "libgssapi") + endif() + else() + list(APPEND _GSS_LIBDIR_SUFFIXES "lib/i386") + if(GSS_FLAVOUR STREQUAL "MIT") + set(_GSS_LIBNAME "gssapi32") + else() + set(_GSS_LIBNAME "libgssapi") + endif() + endif() + else() + list(APPEND _GSS_LIBDIR_SUFFIXES "lib;lib64") # those suffixes are not checked for HINTS + if(GSS_FLAVOUR STREQUAL "MIT") + set(_GSS_LIBNAME "gssapi_krb5") + else() + set(_GSS_LIBNAME "gssapi") + endif() + endif() + + find_library(_GSS_LIBRARIES + NAMES + ${_GSS_LIBNAME} + HINTS + ${_GSS_LIBDIR_HINTS} + PATH_SUFFIXES + ${_GSS_LIBDIR_SUFFIXES} + ) + + endif() + + endif() +else() + if(_GSS_PKG_${_MIT_MODNAME}_VERSION) + set(GSS_FLAVOUR "MIT") + set(_GSS_VERSION _GSS_PKG_${_MIT_MODNAME}_VERSION) + else() + set(GSS_FLAVOUR "Heimdal") + set(_GSS_VERSION _GSS_PKG_${_MIT_HEIMDAL}_VERSION) + endif() +endif() + +set(GSS_INCLUDE_DIR ${_GSS_INCLUDE_DIR}) +set(GSS_LIBRARIES ${_GSS_LIBRARIES}) +set(GSS_LINK_DIRECTORIES ${_GSS_LINK_DIRECTORIES}) +set(GSS_LINKER_FLAGS ${_GSS_LINKER_FLAGS}) +set(GSS_COMPILER_FLAGS ${_GSS_COMPILER_FLAGS}) +set(GSS_VERSION ${_GSS_VERSION}) + +if(GSS_FLAVOUR) + + if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal") + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(HEIMDAL_MANIFEST_FILE "Heimdal.Application.amd64.manifest") + else() + set(HEIMDAL_MANIFEST_FILE "Heimdal.Application.x86.manifest") + endif() + + if(EXISTS "${GSS_INCLUDE_DIR}/${HEIMDAL_MANIFEST_FILE}") + file(STRINGS "${GSS_INCLUDE_DIR}/${HEIMDAL_MANIFEST_FILE}" heimdal_version_str + REGEX "^.*version=\"[0-9]\\.[^\"]+\".*$") + + string(REGEX MATCH "[0-9]\\.[^\"]+" + GSS_VERSION "${heimdal_version_str}") + endif() + + if(NOT GSS_VERSION) + set(GSS_VERSION "Heimdal Unknown") + endif() + elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "MIT") + get_filename_component(_MIT_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE) + if(WIN32 AND _MIT_VERSION) + set(GSS_VERSION "${_MIT_VERSION}") + else() + set(GSS_VERSION "MIT Unknown") + endif() + endif() +endif() + + +include(FindPackageHandleStandardArgs) + +set(_GSS_REQUIRED_VARS GSS_LIBRARIES GSS_FLAVOUR) + +find_package_handle_standard_args(GSS + REQUIRED_VARS + ${_GSS_REQUIRED_VARS} + VERSION_VAR + GSS_VERSION + FAIL_MESSAGE + "Could NOT find GSS, try to set the path to GSS root folder in the system variable GSS_ROOT_DIR" +) + +mark_as_advanced(GSS_INCLUDE_DIR GSS_LIBRARIES) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 80a8833..dab005f 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -1,7 +1,10 @@ #File defines convenience macros for available feature testing # This macro checks if the symbol exists in the library and if it -# does, it prepends library to the list. +# does, it prepends library to the list. It is intended to be called +# multiple times with a sequence of possibly dependent libraries in +# order of least-to-most-dependent. Some libraries depend on others +# to link correctly. macro(CHECK_LIBRARY_EXISTS_CONCAT LIBRARY SYMBOL VARIABLE) check_library_exists("${LIBRARY};${CURL_LIBS}" ${SYMBOL} "${CMAKE_LIBRARY_PATH}" ${VARIABLE}) @@ -11,8 +14,11 @@ macro(CHECK_LIBRARY_EXISTS_CONCAT LIBRARY SYMBOL VARIABLE) endmacro(CHECK_LIBRARY_EXISTS_CONCAT) # Check if header file exists and add it to the list. +# This macro is intended to be called multiple times with a sequence of +# possibly dependent header files. Some headers depend on others to be +# compiled correctly. macro(CHECK_INCLUDE_FILE_CONCAT FILE VARIABLE) - check_include_file("${FILE}" ${VARIABLE}) + check_include_files("${CURL_INCLUDES};${FILE}" ${VARIABLE}) if(${VARIABLE}) set(CURL_INCLUDES ${CURL_INCLUDES} ${FILE}) set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${VARIABLE}") @@ -21,7 +27,7 @@ endmacro(CHECK_INCLUDE_FILE_CONCAT) # For other curl specific tests, use this macro. macro(CURL_INTERNAL_TEST CURL_TEST) - if("${CURL_TEST}" MATCHES "^${CURL_TEST}$") + if(NOT DEFINED "${CURL_TEST}") set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${CURL_TEST} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS}") if(CMAKE_REQUIRED_LIBRARIES) @@ -49,11 +55,11 @@ macro(CURL_INTERNAL_TEST CURL_TEST) "Performing Curl Test ${CURL_TEST} failed with the following output:\n" "${OUTPUT}\n") endif(${CURL_TEST}) - endif("${CURL_TEST}" MATCHES "^${CURL_TEST}$") + endif() endmacro(CURL_INTERNAL_TEST) macro(CURL_INTERNAL_TEST_RUN CURL_TEST) - if("${CURL_TEST}_COMPILE" MATCHES "^${CURL_TEST}_COMPILE$") + if(NOT DEFINED "${CURL_TEST}_COMPILE") set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${CURL_TEST} ${CMAKE_REQUIRED_FLAGS}") if(CMAKE_REQUIRED_LIBRARIES) @@ -85,5 +91,5 @@ macro(CURL_INTERNAL_TEST_RUN CURL_TEST) file(APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log" "\n\n") endif(${CURL_TEST}_COMPILE AND NOT ${CURL_TEST}) - endif("${CURL_TEST}_COMPILE" MATCHES "^${CURL_TEST}_COMPILE$") + endif() endmacro(CURL_INTERNAL_TEST_RUN) diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 9cd5eac..4f07f22 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -1,15 +1,10 @@ -include(CurlCheckCSourceCompiles) -set(EXTRA_DEFINES "__unused1\n#undef inline\n#define __unused2") -set(HEADER_INCLUDES) -set(headers_hack) +include(CheckCSourceCompiles) +# The begin of the sources (macros and includes) +set(_source_epilogue "#undef inline") macro(add_header_include check header) if(${check}) - set(headers_hack - "${headers_hack}\n#include <${header}>") - #SET(HEADER_INCLUDES - # ${HEADER_INCLUDES} - # "${header}") + set(_source_epilogue "${_source_epilogue}\n#include <${header}>") endif(${check}) endmacro(add_header_include) @@ -18,8 +13,8 @@ if(HAVE_WINDOWS_H) add_header_include(HAVE_WINDOWS_H "windows.h") add_header_include(HAVE_WINSOCK2_H "winsock2.h") add_header_include(HAVE_WINSOCK_H "winsock.h") - set(EXTRA_DEFINES ${EXTRA_DEFINES} - "__unused7\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#define __unused3") + set(_source_epilogue + "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif") set(signature_call_conv "PASCAL") if(HAVE_LIBWS2_32) set(CMAKE_REQUIRED_LIBRARIES ws2_32) @@ -29,14 +24,12 @@ else(HAVE_WINDOWS_H) add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") endif(HAVE_WINDOWS_H) -set(EXTRA_DEFINES_BACKUP "${EXTRA_DEFINES}") -set(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5") -curl_check_c_source_compiles("recv(0, 0, 0, 0)" curl_cv_recv) +check_c_source_compiles("${_source_epilogue} +int main(void) { + recv(0, 0, 0, 0); + return 0; +}" curl_cv_recv) if(curl_cv_recv) - # AC_CACHE_CHECK([types of arguments and return type for recv], - #[curl_cv_func_recv_args], [ - #SET(curl_cv_func_recv_args "unknown") - #for recv_retv in 'int' 'ssize_t'; do if(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") foreach(recv_retv "int" "ssize_t" ) foreach(recv_arg1 "int" "ssize_t" "SOCKET") @@ -44,17 +37,23 @@ if(curl_cv_recv) foreach(recv_arg3 "size_t" "int" "socklen_t" "unsigned int") foreach(recv_arg4 "int" "unsigned int") if(NOT curl_cv_func_recv_done) - set(curl_cv_func_recv_test "UNKNOWN") - set(extern_line "extern ${recv_retv} ${signature_call_conv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})\;") - set(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5") - curl_check_c_source_compiles(" + unset(curl_cv_func_recv_test CACHE) + check_c_source_compiles(" + ${_source_epilogue} + extern ${recv_retv} ${signature_call_conv} + recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4}); + int main(void) { ${recv_arg1} s=0; ${recv_arg2} buf=0; ${recv_arg3} len=0; ${recv_arg4} flags=0; - ${recv_retv} res = recv(s, buf, len, flags)" - curl_cv_func_recv_test - "${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})") + ${recv_retv} res = recv(s, buf, len, flags); + (void) res; + return 0; + }" + curl_cv_func_recv_test) + message(STATUS + "Tested: ${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})") if(curl_cv_func_recv_test) set(curl_cv_func_recv_args "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}") @@ -72,18 +71,13 @@ if(curl_cv_recv) endforeach(recv_arg2) endforeach(recv_arg1) endforeach(recv_retv) - else(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") + else() string(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG1 "${curl_cv_func_recv_args}") string(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG2 "${curl_cv_func_recv_args}") string(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG3 "${curl_cv_func_recv_args}") string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" RECV_TYPE_ARG4 "${curl_cv_func_recv_args}") string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" RECV_TYPE_RETV "${curl_cv_func_recv_args}") - #MESSAGE("RECV_TYPE_ARG1 ${RECV_TYPE_ARG1}") - #MESSAGE("RECV_TYPE_ARG2 ${RECV_TYPE_ARG2}") - #MESSAGE("RECV_TYPE_ARG3 ${RECV_TYPE_ARG3}") - #MESSAGE("RECV_TYPE_ARG4 ${RECV_TYPE_ARG4}") - #MESSAGE("RECV_TYPE_RETV ${RECV_TYPE_RETV}") - endif(NOT DEFINED curl_cv_func_recv_args OR "${curl_cv_func_recv_args}" STREQUAL "unknown") + endif() if("${curl_cv_func_recv_args}" STREQUAL "unknown") message(FATAL_ERROR "Cannot find proper types to use for recv args") @@ -94,12 +88,12 @@ endif(curl_cv_recv) set(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv") set(HAVE_RECV 1) -curl_check_c_source_compiles("send(0, 0, 0, 0)" curl_cv_send) +check_c_source_compiles("${_source_epilogue} +int main(void) { + send(0, 0, 0, 0); + return 0; +}" curl_cv_send) if(curl_cv_send) - # AC_CACHE_CHECK([types of arguments and return type for send], - #[curl_cv_func_send_args], [ - #SET(curl_cv_func_send_args "unknown") - #for send_retv in 'int' 'ssize_t'; do if(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown") foreach(send_retv "int" "ssize_t" ) foreach(send_arg1 "int" "ssize_t" "SOCKET") @@ -107,19 +101,24 @@ if(curl_cv_send) foreach(send_arg3 "size_t" "int" "socklen_t" "unsigned int") foreach(send_arg4 "int" "unsigned int") if(NOT curl_cv_func_send_done) - set(curl_cv_func_send_test "UNKNOWN") - set(extern_line "extern ${send_retv} ${signature_call_conv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})\;") - set(EXTRA_DEFINES "${EXTRA_DEFINES_BACKUP}\n${headers_hack}\n${extern_line}\n#define __unused5") - curl_check_c_source_compiles(" + unset(curl_cv_func_send_test CACHE) + check_c_source_compiles(" + ${_source_epilogue} + extern ${send_retv} ${signature_call_conv} + send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4}); + int main(void) { ${send_arg1} s=0; ${send_arg2} buf=0; ${send_arg3} len=0; ${send_arg4} flags=0; - ${send_retv} res = send(s, buf, len, flags)" - curl_cv_func_send_test - "${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})") + ${send_retv} res = send(s, buf, len, flags); + (void) res; + return 0; + }" + curl_cv_func_send_test) + message(STATUS + "Tested: ${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})") if(curl_cv_func_send_test) - #MESSAGE("Found arguments: ${curl_cv_func_send_test}") string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}") string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}") set(curl_cv_func_send_args @@ -138,20 +137,14 @@ if(curl_cv_send) endforeach(send_arg2) endforeach(send_arg1) endforeach(send_retv) - else(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown") + else() string(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG1 "${curl_cv_func_send_args}") string(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG2 "${curl_cv_func_send_args}") string(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG3 "${curl_cv_func_send_args}") string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG4 "${curl_cv_func_send_args}") string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" SEND_TYPE_RETV "${curl_cv_func_send_args}") string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" SEND_QUAL_ARG2 "${curl_cv_func_send_args}") - #MESSAGE("SEND_TYPE_ARG1 ${SEND_TYPE_ARG1}") - #MESSAGE("SEND_TYPE_ARG2 ${SEND_TYPE_ARG2}") - #MESSAGE("SEND_TYPE_ARG3 ${SEND_TYPE_ARG3}") - #MESSAGE("SEND_TYPE_ARG4 ${SEND_TYPE_ARG4}") - #MESSAGE("SEND_TYPE_RETV ${SEND_TYPE_RETV}") - #MESSAGE("SEND_QUAL_ARG2 ${SEND_QUAL_ARG2}") - endif(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown") + endif() if("${curl_cv_func_send_args}" STREQUAL "unknown") message(FATAL_ERROR "Cannot find proper types to use for send args") @@ -163,88 +156,71 @@ endif(curl_cv_send) set(curl_cv_func_send_args "${curl_cv_func_send_args}" CACHE INTERNAL "Arguments for send") set(HAVE_SEND 1) -set(EXTRA_DEFINES "${EXTRA_DEFINES}\n${headers_hack}\n#define __unused5") -curl_check_c_source_compiles("int flag = MSG_NOSIGNAL" HAVE_MSG_NOSIGNAL) - -set(EXTRA_DEFINES "__unused1\n#undef inline\n#define __unused2") -set(HEADER_INCLUDES) -set(headers_hack) +check_c_source_compiles("${_source_epilogue} + int main(void) { + int flag = MSG_NOSIGNAL; + (void)flag; + return 0; + }" HAVE_MSG_NOSIGNAL) -macro(add_header_include check header) - if(${check}) - set(headers_hack - "${headers_hack}\n#include <${header}>") - #SET(HEADER_INCLUDES - # ${HEADER_INCLUDES} - # "${header}") - endif(${check}) -endmacro(add_header_include header) - -if(HAVE_WINDOWS_H) - set(EXTRA_DEFINES ${EXTRA_DEFINES} - "__unused7\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#define __unused3") - add_header_include(HAVE_WINDOWS_H "windows.h") - add_header_include(HAVE_WINSOCK2_H "winsock2.h") - add_header_include(HAVE_WINSOCK_H "winsock.h") -else(HAVE_WINDOWS_H) - add_header_include(HAVE_SYS_TYPES_H "sys/types.h") +if(NOT HAVE_WINDOWS_H) add_header_include(HAVE_SYS_TIME_H "sys/time.h") add_header_include(TIME_WITH_SYS_TIME "time.h") add_header_include(HAVE_TIME_H "time.h") -endif(HAVE_WINDOWS_H) -set(EXTRA_DEFINES "${EXTRA_DEFINES}\n${headers_hack}\n#define __unused5") -curl_check_c_source_compiles("struct timeval ts;\nts.tv_sec = 0;\nts.tv_usec = 0" HAVE_STRUCT_TIMEVAL) - - -include(CurlCheckCSourceRuns) -set(EXTRA_DEFINES) -set(HEADER_INCLUDES) +endif() +check_c_source_compiles("${_source_epilogue} +int main(void) { + struct timeval ts; + ts.tv_sec = 0; + ts.tv_usec = 0; + (void)ts; + return 0; +}" HAVE_STRUCT_TIMEVAL) + + +include(CheckCSourceRuns) +set(CMAKE_REQUIRED_FLAGS) if(HAVE_SYS_POLL_H) - set(HEADER_INCLUDES "sys/poll.h") + set(CMAKE_REQUIRED_FLAGS "-DHAVE_SYS_POLL_H") endif(HAVE_SYS_POLL_H) -curl_check_c_source_runs("return poll((void *)0, 0, 10 /*ms*/)" HAVE_POLL_FINE) +check_c_source_runs(" + #ifdef HAVE_SYS_POLL_H + # include <sys/poll.h> + #endif + int main(void) { + return poll((void *)0, 0, 10 /*ms*/); + }" HAVE_POLL_FINE) set(HAVE_SIG_ATOMIC_T 1) -set(EXTRA_DEFINES) -set(HEADER_INCLUDES) +set(CMAKE_REQUIRED_FLAGS) if(HAVE_SIGNAL_H) - set(HEADER_INCLUDES "signal.h") + set(CMAKE_REQUIRED_FLAGS "-DHAVE_SIGNAL_H") set(CMAKE_EXTRA_INCLUDE_FILES "signal.h") endif(HAVE_SIGNAL_H) check_type_size("sig_atomic_t" SIZEOF_SIG_ATOMIC_T) if(HAVE_SIZEOF_SIG_ATOMIC_T) - curl_check_c_source_compiles("static volatile sig_atomic_t dummy = 0" HAVE_SIG_ATOMIC_T_NOT_VOLATILE) + check_c_source_compiles(" + #ifdef HAVE_SIGNAL_H + # include <signal.h> + #endif + int main(void) { + static volatile sig_atomic_t dummy = 0; + (void)dummy; + return 0; + }" HAVE_SIG_ATOMIC_T_NOT_VOLATILE) if(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE) set(HAVE_SIG_ATOMIC_T_VOLATILE 1) endif(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE) endif(HAVE_SIZEOF_SIG_ATOMIC_T) -set(CHECK_TYPE_SIZE_PREINCLUDE - "#undef inline") - if(HAVE_WINDOWS_H) - set(CHECK_TYPE_SIZE_PREINCLUDE "${CHECK_TYPE_SIZE_PREINCLUDE} - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include <windows.h>") - if(HAVE_WINSOCK2_H) - set(CHECK_TYPE_SIZE_PREINCLUDE "${CHECK_TYPE_SIZE_PREINCLUDE}\n#include <winsock2.h>") - endif(HAVE_WINSOCK2_H) -else(HAVE_WINDOWS_H) + set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h) +else() + set(CMAKE_EXTRA_INCLUDE_FILES) if(HAVE_SYS_SOCKET_H) - set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} - "sys/socket.h") + set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) endif(HAVE_SYS_SOCKET_H) - if(HAVE_NETINET_IN_H) - set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} - "netinet/in.h") - endif(HAVE_NETINET_IN_H) - if(HAVE_ARPA_INET_H) - set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} - "arpa/inet.h") - endif(HAVE_ARPA_INET_H) -endif(HAVE_WINDOWS_H) +endif() check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE) if(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE) diff --git a/CMake/Platforms/WindowsCache.cmake b/CMake/Platforms/WindowsCache.cmake index 3533a54..6fc2991 100644 --- a/CMake/Platforms/WindowsCache.cmake +++ b/CMake/Platforms/WindowsCache.cmake @@ -5,6 +5,7 @@ if(NOT UNIX) set(HAVE_LIBSOCKET 0) set(NOT_NEED_LIBNSL 0) set(HAVE_LIBNSL 0) + set(HAVE_GETHOSTNAME 1) set(HAVE_LIBZ 0) set(HAVE_LIBCRYPTO 0) @@ -14,7 +15,6 @@ if(NOT UNIX) set(HAVE_ARPA_INET_H 0) set(HAVE_DLFCN_H 0) set(HAVE_FCNTL_H 1) - set(HAVE_FEATURES_H 0) set(HAVE_INTTYPES_H 0) set(HAVE_IO_H 1) set(HAVE_MALLOC_H 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 845c330..9a42cc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. +# Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -48,27 +48,17 @@ project( CURL C ) message(WARNING "the curl cmake build system is poorly maintained. Be aware") file (READ ${CURL_SOURCE_DIR}/include/curl/curlver.h CURL_VERSION_H_CONTENTS) -string (REGEX MATCH "LIBCURL_VERSION_MAJOR[ \t]+([0-9]+)" - LIBCURL_VERSION_MJ ${CURL_VERSION_H_CONTENTS}) -string (REGEX MATCH "([0-9]+)" - LIBCURL_VERSION_MJ ${LIBCURL_VERSION_MJ}) -string (REGEX MATCH - "LIBCURL_VERSION_MINOR[ \t]+([0-9]+)" - LIBCURL_VERSION_MI ${CURL_VERSION_H_CONTENTS}) -string (REGEX MATCH "([0-9]+)" LIBCURL_VERSION_MI ${LIBCURL_VERSION_MI}) -string (REGEX MATCH - "LIBCURL_VERSION_PATCH[ \t]+([0-9]+)" - LIBCURL_VERSION_PT ${CURL_VERSION_H_CONTENTS}) -string (REGEX MATCH "([0-9]+)" LIBCURL_VERSION_PT ${LIBCURL_VERSION_PT}) -set (CURL_MAJOR_VERSION ${LIBCURL_VERSION_MJ}) -set (CURL_MINOR_VERSION ${LIBCURL_VERSION_MI}) -set (CURL_PATCH_VERSION ${LIBCURL_VERSION_PT}) +string (REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" + CURL_VERSION ${CURL_VERSION_H_CONTENTS}) +string (REGEX REPLACE "[^\"]+\"" "" CURL_VERSION ${CURL_VERSION}) +string (REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" + CURL_VERSION_NUM ${CURL_VERSION_H_CONTENTS}) +string (REGEX REPLACE "[^0]+0x" "" CURL_VERSION_NUM ${CURL_VERSION_NUM}) include_regular_expression("^.*$") # Sukender: Is it necessary? # Setup package meta-data # SET(PACKAGE "curl") -set(CURL_VERSION ${CURL_MAJOR_VERSION}.${CURL_MINOR_VERSION}.${CURL_PATCH_VERSION}) message(STATUS "curl version=[${CURL_VERSION}]") # SET(PACKAGE_TARNAME "curl") # SET(PACKAGE_NAME "curl") @@ -84,12 +74,35 @@ include_directories( ${CURL_SOURCE_DIR}/include ) option(BUILD_CURL_EXE "Set to ON to build cURL executable." ON) option(BUILD_CURL_TESTS "Set to ON to build cURL tests." ON) option(CURL_STATICLIB "Set to ON to build libcurl with static linking." OFF) -option(CURL_USE_ARES "Set to ON to enable c-ares support" OFF) +option(ENABLE_ARES "Set to ON to enable c-ares support" OFF) +option(ENABLE_THREADED_RESOLVER "Set to ON to enable POSIX threaded DNS lookup" OFF) + +option(ENABLE_DEBUG "Set to ON to enable curl debug features" OFF) +option(ENABLE_CURLDEBUG "Set to ON to build with TrackMemory feature enabled" OFF) + +if (ENABLE_DEBUG) + # DEBUGBUILD will be defined only for Debug builds + if(NOT CMAKE_VERSION VERSION_LESS 3.0) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:DEBUGBUILD>) + else() + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG DEBUGBUILD) + endif() + set(ENABLE_CURLDEBUG ON) +endif() + +if (ENABLE_CURLDEBUG) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS CURLDEBUG) +endif() + # initialize CURL_LIBS set(CURL_LIBS "") -if(CURL_USE_ARES) - set(USE_ARES ${CURL_USE_ARES}) +if(ENABLE_THREADED_RESOLVER AND ENABLE_ARES) + message(FATAL_ERROR "Options ENABLE_THREADED_RESOLVER and ENABLE_ARES are mutually exclusive") +endif() + +if(ENABLE_ARES) + set(USE_ARES 1) find_package(CARES REQUIRED) list(APPEND CURL_LIBS ${CARES_LIBRARY} ) set(CURL_LIBS ${CURL_LIBS} ${CARES_LIBRARY}) @@ -171,9 +184,52 @@ option(CURL_DISABLE_VERBOSE_STRINGS "to disable verbose strings" OFF) mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) option(DISABLED_THREADSAFE "Set to explicitly specify we don't want to use thread-safe functions" OFF) mark_as_advanced(DISABLED_THREADSAFE) -option(ENABLE_IPV6 "Define if you want to enable IPv6 support" OFF) +option(ENABLE_IPV6 "Define if you want to enable IPv6 support" ON) mark_as_advanced(ENABLE_IPV6) +if(ENABLE_IPV6) + include(CheckStructHasMember) + check_struct_has_member("struct sockaddr_in6" sin6_addr "netinet/in.h" + HAVE_SOCKADDR_IN6_SIN6_ADDR) + check_struct_has_member("struct sockaddr_in6" sin6_scope_id "netinet/in.h" + HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR) + message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support") + # Force the feature off as this name is used as guard macro... + set(ENABLE_IPV6 OFF + CACHE BOOL "Define if you want to enable IPv6 support" FORCE) + endif() +endif() +option(ENABLE_MANUAL "to provide the built-in manual" ON) +unset(USE_MANUAL CACHE) # TODO: cache NROFF/NROFF_MANOPT/USE_MANUAL vars? +if(ENABLE_MANUAL) + find_program(NROFF NAMES gnroff nroff) + if(NROFF) + # Need a way to write to stdin, this will do + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt" "test") + # Tests for a valid nroff option to generate a manpage + foreach(_MANOPT "-man" "-mandoc") + execute_process(COMMAND "${NROFF}" ${_MANOPT} + OUTPUT_VARIABLE NROFF_MANOPT_OUTPUT + INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt" + ERROR_QUIET) + # Save the option if it was valid + if(NROFF_MANOPT_OUTPUT) + message("Found *nroff option: -- ${_MANOPT}") + set(NROFF_MANOPT ${_MANOPT}) + set(USE_MANUAL 1) + break() + endif() + endforeach() + # No need for the temporary file + file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt") + if(NOT USE_MANUAL) + message(WARNING "Found no *nroff option to get plaintext from man pages") + endif() + else() + message(WARNING "Found no *nroff program") + endif() +endif() # We need ansi c-flags, especially on HP set(CMAKE_C_FLAGS "${CMAKE_ANSI_CFLAGS} ${CMAKE_C_FLAGS}") @@ -200,9 +256,22 @@ include (CheckCSourceCompiles) # On windows preload settings if(WIN32) + set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WINSOCKAPI_") include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake) endif(WIN32) +if(ENABLE_THREADED_RESOLVER) + check_include_file_concat("pthread.h" HAVE_PTHREAD_H) + if(HAVE_PTHREAD_H) + set(CMAKE_THREAD_PREFER_PTHREAD 1) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + set(USE_THREADS_POSIX 1) + endif() + endif() +endif() + # Check for all needed libraries check_library_exists_concat("dl" dlopen HAVE_LIBDL) check_library_exists_concat("socket" connect HAVE_LIBSOCKET) @@ -219,19 +288,49 @@ if(NOT NOT_NEED_LIBNSL) check_library_exists_concat("nsl" gethostbyname HAVE_LIBNSL) endif(NOT NOT_NEED_LIBNSL) +check_function_exists(gethostname HAVE_GETHOSTNAME) + if(WIN32) check_library_exists_concat("ws2_32" getch HAVE_LIBWS2_32) check_library_exists_concat("winmm" getch HAVE_LIBWINMM) endif() +option(CMAKE_USE_OPENSSL "Use OpenSSL code. Experimental" ON) +mark_as_advanced(CMAKE_USE_OPENSSL) + +set(USE_OPENSSL OFF) +set(HAVE_LIBCRYPTO OFF) +set(HAVE_LIBSSL OFF) + +if(CMAKE_USE_OPENSSL) + find_package(OpenSSL) + if(OPENSSL_FOUND) + list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES}) + set(USE_OPENSSL ON) + set(HAVE_LIBCRYPTO ON) + set(HAVE_LIBSSL ON) + include_directories(${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + check_include_file("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H) + check_include_file("openssl/engine.h" HAVE_OPENSSL_ENGINE_H) + check_include_file("openssl/err.h" HAVE_OPENSSL_ERR_H) + check_include_file("openssl/pem.h" HAVE_OPENSSL_PEM_H) + check_include_file("openssl/pkcs12.h" HAVE_OPENSSL_PKCS12_H) + check_include_file("openssl/rsa.h" HAVE_OPENSSL_RSA_H) + check_include_file("openssl/ssl.h" HAVE_OPENSSL_SSL_H) + check_include_file("openssl/x509.h" HAVE_OPENSSL_X509_H) + check_include_file("openssl/rand.h" HAVE_OPENSSL_RAND_H) + endif() +endif() + if(NOT CURL_DISABLE_LDAP) if(WIN32) - option(CURL_LDAP_WIN "Use Windows LDAP implementation" ON) - if(CURL_LDAP_WIN) + option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) + if(USE_WIN32_LDAP) check_library_exists("wldap32" cldap_open "" HAVE_WLDAP32) if(NOT HAVE_WLDAP32) - set(CURL_LDAP_WIN OFF) + set(USE_WIN32_LDAP OFF) endif() endif() endif() @@ -241,13 +340,14 @@ if(NOT CURL_DISABLE_LDAP) set(CMAKE_LDAP_LIB "ldap" CACHE STRING "Name or full path to ldap library") set(CMAKE_LBER_LIB "lber" CACHE STRING "Name or full path to lber library") - if(CMAKE_USE_OPENLDAP AND CURL_LDAP_WIN) - message(FATAL_ERROR "Cannot use CURL_LDAP_WIN and CMAKE_USE_OPENLDAP at the same time") + if(CMAKE_USE_OPENLDAP AND USE_WIN32_LDAP) + message(FATAL_ERROR "Cannot use USE_WIN32_LDAP and CMAKE_USE_OPENLDAP at the same time") endif() # Now that we know, we're not using windows LDAP... - if(NOT CURL_LDAP_WIN) + if(NOT USE_WIN32_LDAP) # Check for LDAP + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) check_library_exists_concat(${CMAKE_LDAP_LIB} ldap_init HAVE_LIBLDAP) check_library_exists_concat(${CMAKE_LBER_LIB} ber_init HAVE_LIBLBER) else() @@ -301,8 +401,8 @@ if(NOT CURL_DISABLE_LDAP) return 0; }" ) - set(CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1" "-DWIN32_LEAN_AND_MEAN") - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) + set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DLDAP_DEPRECATED=1") + list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) if(HAVE_LIBLBER) list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB}) endif() @@ -336,9 +436,6 @@ check_library_exists_concat("idn" idna_to_ascii_lz HAVE_LIBIDN) # Check for symbol dlopen (same as HAVE_LIBDL) check_library_exists("${CURL_LIBS}" dlopen "" HAVE_DLOPEN) -# For other tests to use the same libraries -set(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBS}) - option(CURL_ZLIB "Set to ON to enable building cURL with zlib support." ON) set(HAVE_LIBZ OFF) set(HAVE_ZLIB_H OFF) @@ -350,39 +447,10 @@ if(CURL_ZLIB) set(HAVE_ZLIB ON) set(HAVE_LIBZ ON) list(APPEND CURL_LIBS ${ZLIB_LIBRARIES}) + include_directories(${ZLIB_INCLUDE_DIRS}) endif() endif() -option(CMAKE_USE_OPENSSL "Use OpenSSL code. Experimental" ON) -mark_as_advanced(CMAKE_USE_OPENSSL) - -set(USE_SSLEAY OFF) -set(USE_OPENSSL OFF) -set(HAVE_LIBCRYPTO OFF) -set(HAVE_LIBSSL OFF) - -if(CMAKE_USE_OPENSSL) - find_package(OpenSSL) - if(OPENSSL_FOUND) - list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES}) - set(USE_SSLEAY ON) - set(USE_OPENSSL ON) - set(HAVE_LIBCRYPTO ON) - set(HAVE_LIBSSL ON) - include_directories(${OPENSSL_INCLUDE_DIR}) - set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) - check_include_file_concat("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H) - check_include_file_concat("openssl/engine.h" HAVE_OPENSSL_ENGINE_H) - check_include_file_concat("openssl/err.h" HAVE_OPENSSL_ERR_H) - check_include_file_concat("openssl/pem.h" HAVE_OPENSSL_PEM_H) - check_include_file_concat("openssl/pkcs12.h" HAVE_OPENSSL_PKCS12_H) - check_include_file_concat("openssl/rsa.h" HAVE_OPENSSL_RSA_H) - check_include_file_concat("openssl/ssl.h" HAVE_OPENSSL_SSL_H) - check_include_file_concat("openssl/x509.h" HAVE_OPENSSL_X509_H) - check_include_file_concat("openssl/rand.h" HAVE_OPENSSL_RAND_H) - endif(OPENSSL_FOUND) -endif(CMAKE_USE_OPENSSL) - #libSSH2 option(CMAKE_USE_LIBSSH2 "Use libSSH2" ON) mark_as_advanced(CMAKE_USE_LIBSSH2) @@ -396,6 +464,7 @@ if(CMAKE_USE_LIBSSH2) list(APPEND CURL_LIBS ${LIBSSH2_LIBRARY}) set(CMAKE_REQUIRED_LIBRARIES ${LIBSSH2_LIBRARY}) set(CMAKE_REQUIRED_INCLUDES "${LIBSSH2_INCLUDE_DIR}") + include_directories("${LIBSSH2_INCLUDE_DIR}") set(HAVE_LIBSSH2 ON) set(USE_LIBSSH2 ON) @@ -416,20 +485,82 @@ if(CMAKE_USE_LIBSSH2) endif(LIBSSH2_FOUND) endif(CMAKE_USE_LIBSSH2) -# If we have features.h, then do the _BSD_SOURCE magic -check_include_file("features.h" HAVE_FEATURES_H) +option(CMAKE_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF) +mark_as_advanced(CMAKE_USE_GSSAPI) + +if(CMAKE_USE_GSSAPI) + find_package(GSS) + + set(HAVE_GSSAPI ${GSS_FOUND}) + if(GSS_FOUND) + + message(STATUS "Found ${GSS_FLAVOUR} GSSAPI version: \"${GSS_VERSION}\"") + + set(CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIR}) + check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) + check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) + check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H) + + if(GSS_FLAVOUR STREQUAL "Heimdal") + set(HAVE_GSSHEIMDAL ON) + else() # MIT + set(HAVE_GSSMIT ON) + set(_INCLUDE_LIST "") + if(HAVE_GSSAPI_GSSAPI_H) + list(APPEND _INCLUDE_LIST "gssapi/gssapi.h") + endif() + if(HAVE_GSSAPI_GSSAPI_GENERIC_H) + list(APPEND _INCLUDE_LIST "gssapi/gssapi_generic.h") + endif() + if(HAVE_GSSAPI_GSSAPI_KRB5_H) + list(APPEND _INCLUDE_LIST "gssapi/gssapi_krb5.h") + endif() + + string(REPLACE ";" " " _COMPILER_FLAGS_STR "${GSS_COMPILER_FLAGS}") + string(REPLACE ";" " " _LINKER_FLAGS_STR "${GSS_LINKER_FLAGS}") + + foreach(_dir ${GSS_LINK_DIRECTORIES}) + set(_LINKER_FLAGS_STR "${_LINKER_FLAGS_STR} -L\"${_dir}\"") + endforeach() + + set(CMAKE_REQUIRED_FLAGS "${_COMPILER_FLAGS_STR} ${_LINKER_FLAGS_STR}") + set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) + check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" ${_INCLUDE_LIST} HAVE_GSS_C_NT_HOSTBASED_SERVICE) + if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE) + set(HAVE_OLD_GSSMIT ON) + endif() + + endif() + + include_directories(${GSS_INCLUDE_DIR}) + link_directories(${GSS_LINK_DIRECTORIES}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_COMPILER_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") + list(APPEND CURL_LIBS ${GSS_LIBRARIES}) + + else() + message(WARNING "GSSAPI support has been requested but no supporting libraries found. Skipping.") + endif() +endif() + +option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON) +if(ENABLE_UNIX_SOCKETS) + include(CheckStructHasMember) + check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS) +else() + unset(USE_UNIX_SOCKETS CACHE) +endif() # Check for header files if(NOT UNIX) - check_include_file_concat("ws2tcpip.h" HAVE_WS2TCPIP_H) - check_include_file_concat("winsock2.h" HAVE_WINSOCK2_H) -endif(NOT UNIX) -check_include_file_concat("stdio.h" HAVE_STDIO_H) -if(NOT UNIX) check_include_file_concat("windows.h" HAVE_WINDOWS_H) check_include_file_concat("winsock.h" HAVE_WINSOCK_H) + check_include_file_concat("ws2tcpip.h" HAVE_WS2TCPIP_H) + check_include_file_concat("winsock2.h" HAVE_WINSOCK2_H) endif(NOT UNIX) +check_include_file_concat("stdio.h" HAVE_STDIO_H) check_include_file_concat("inttypes.h" HAVE_INTTYPES_H) check_include_file_concat("sys/filio.h" HAVE_SYS_FILIO_H) check_include_file_concat("sys/ioctl.h" HAVE_SYS_IOCTL_H) @@ -454,9 +585,6 @@ check_include_file_concat("des.h" HAVE_DES_H) check_include_file_concat("err.h" HAVE_ERR_H) check_include_file_concat("errno.h" HAVE_ERRNO_H) check_include_file_concat("fcntl.h" HAVE_FCNTL_H) -check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) -check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) -check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H) check_include_file_concat("idn-free.h" HAVE_IDN_FREE_H) check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) check_include_file_concat("io.h" HAVE_IO_H) @@ -576,6 +704,12 @@ find_file(RANDOM_FILE urandom /dev) mark_as_advanced(RANDOM_FILE) # Check for some functions that are used +if(HAVE_LIBWS2_32) + set(CMAKE_REQUIRED_LIBRARIES ws2_32) +elseif(HAVE_LIBSOCKET) + set(CMAKE_REQUIRED_LIBRARIES socket) +endif() + check_symbol_exists(basename "${CURL_INCLUDES}" HAVE_BASENAME) check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) check_symbol_exists(poll "${CURL_INCLUDES}" HAVE_POLL) @@ -618,7 +752,6 @@ if(CMAKE_USE_OPENSSL) HAVE_CRYPTO_CLEANUP_ALL_EX_DATA) if(HAVE_LIBCRYPTO AND HAVE_LIBSSL) set(USE_OPENSSL 1) - set(USE_SSLEAY 1) endif(HAVE_LIBCRYPTO AND HAVE_LIBSSL) endif(CMAKE_USE_OPENSSL) check_symbol_exists(gmtime_r "${CURL_INCLUDES}" HAVE_GMTIME_R) @@ -639,6 +772,7 @@ check_symbol_exists(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R) check_symbol_exists(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT) check_symbol_exists(perror "${CURL_INCLUDES}" HAVE_PERROR) check_symbol_exists(fork "${CURL_INCLUDES}" HAVE_FORK) +check_symbol_exists(getaddrinfo "${CURL_INCLUDES}" HAVE_GETADDRINFO) check_symbol_exists(freeaddrinfo "${CURL_INCLUDES}" HAVE_FREEADDRINFO) check_symbol_exists(freeifaddrs "${CURL_INCLUDES}" HAVE_FREEIFADDRS) check_symbol_exists(pipe "${CURL_INCLUDES}" HAVE_PIPE) @@ -677,12 +811,7 @@ if(NOT HAVE_STRICMP) set(HAVE_LDAP_URL_PARSE 1) endif(NOT HAVE_STRICMP) - - # Do curl specific tests -if(HAVE_LIBWS2_32) - set(CMAKE_REQUIRED_LIBRARIES ws2_32) -endif() foreach(CURL_TEST HAVE_FCNTL_O_NONBLOCK HAVE_IOCTLSOCKET @@ -869,24 +998,6 @@ if(MSVC) add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) endif(MSVC) -# Sets up the dependencies (zlib, OpenSSL, etc.) of a cURL subproject according to options. -# TODO This is far to be complete! -function(SETUP_CURL_DEPENDENCIES TARGET_NAME) - if(CURL_ZLIB AND ZLIB_FOUND) - include_directories(${ZLIB_INCLUDE_DIR}) - endif() - - if(CMAKE_USE_OPENSSL AND OPENSSL_FOUND) - include_directories(${OPENSSL_INCLUDE_DIR}) - endif() - - if(CMAKE_USE_LIBSSH2 AND LIBSSH2_FOUND) - include_directories(${LIBSSH2_INCLUDE_DIR}) - endif() - - target_link_libraries(${TARGET_NAME} ${CURL_LIBS}) -endfunction() - # Ugly (but functional) way to include "Makefile.inc" by transforming it (= regenerate it). function(TRANSFORM_MAKEFILE_INC INPUT_FILE OUTPUT_FILE) file(READ ${INPUT_FILE} MAKEFILE_INC_TEXT) @@ -911,6 +1022,133 @@ if(BUILD_CURL_TESTS) add_subdirectory(tests) endif() +# TODO support GNUTLS, NSS, POLARSSL, AXTLS, CYASSL, WINSSL, DARWINSSL +if(USE_OPENSSL) + set(SSL_ENABLED 1) +endif() + +# Helper to populate a list (_items) with a label when conditions (the remaining +# args) are satisfied +function(_add_if label) + # TODO need to disable policy CMP0054 (CMake 3.1) to allow this indirection + if(${ARGN}) + set(_items ${_items} "${label}" PARENT_SCOPE) + endif() +endfunction() + +# Clear list and try to detect available features +set(_items) +_add_if("SSL" SSL_ENABLED) +_add_if("IPv6" ENABLE_IPV6) +_add_if("unix-sockets" USE_UNIX_SOCKETS) +_add_if("libz" HAVE_LIBZ) +_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX) +_add_if("IDN" HAVE_LIBIDN) +# TODO SSP1 (WinSSL) check is missing +_add_if("SSPI" USE_WINDOWS_SSPI) +_add_if("GSS-API" HAVE_GSSAPI) +# TODO SSP1 missing for SPNEGO +_add_if("SPNEGO" NOT CURL_DISABLE_CRYPTO_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +_add_if("Kerberos" NOT CURL_DISABLE_CRYPTO_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +# NTLM support requires crypto function adaptions from various SSL libs +# TODO alternative SSL libs tests for SSP1, GNUTLS, NSS, DARWINSSL +if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR + USE_WINDOWS_SSPI OR GNUTLS_ENABLED OR NSS_ENABLED OR DARWINSSL_ENABLED)) + _add_if("NTLM" 1) + # TODO missing option (autoconf: --enable-ntlm-wb) + _add_if("NTLM_WB" NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) +endif() +# TODO missing option (--enable-tls-srp), depends on GNUTLS_SRP/OPENSSL_SRP +_add_if("TLS-SRP" USE_TLS_SRP) +# TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header +_add_if("HTTP2" USE_NGHTTP2) +string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") +message(STATUS "Enabled features: ${SUPPORT_FEATURES}") + +# Clear list and try to detect available protocols +set(_items) +_add_if("HTTP" NOT CURL_DISABLE_HTTP) +_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND SSL_ENABLED) +_add_if("FTP" NOT CURL_DISABLE_FTP) +_add_if("FTPS" NOT CURL_DISABLE_FTP AND SSL_ENABLED) +_add_if("FILE" NOT CURL_DISABLE_FILE) +_add_if("TELNET" NOT CURL_DISABLE_TELNET) +_add_if("LDAP" NOT CURL_DISABLE_LDAP) +# CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS +# TODO check HAVE_LDAP_SSL (in autoconf this is enabled with --enable-ldaps) +_add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND + ((USE_OPENLDAP AND SSL_ENABLED) OR + (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) +_add_if("DICT" NOT CURL_DISABLE_DICT) +_add_if("TFTP" NOT CURL_DISABLE_TFTP) +_add_if("GOPHER" NOT CURL_DISABLE_GOPHER) +_add_if("POP3" NOT CURL_DISABLE_POP3) +_add_if("POP3S" NOT CURL_DISABLE_POP3 AND SSL_ENABLED) +_add_if("IMAP" NOT CURL_DISABLE_IMAP) +_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND SSL_ENABLED) +_add_if("SMTP" NOT CURL_DISABLE_SMTP) +_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND SSL_ENABLED) +_add_if("SCP" USE_LIBSSH2) +_add_if("SFTP" USE_LIBSSH2) +_add_if("RTSP" NOT CURL_DISABLE_RTSP) +_add_if("RTMP" USE_LIBRTMP) +list(SORT _items) +string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") +message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}") + +# curl-config needs the following options to be set. +set(CC "${CMAKE_C_COMPILER}") +# TODO probably put a -D... options here? +set(CONFIGURE_OPTIONS "") +# TODO when to set "-DCURL_STATICLIB" for CPPFLAG_CURL_STATICLIB? +set(CPPFLAG_CURL_STATICLIB "") +# TODO need to set this (see CURL_CHECK_CA_BUNDLE in acinclude.m4) +set(CURL_CA_BUNDLE "") +set(CURLVERSION "${CURL_VERSION}") +set(ENABLE_SHARED "yes") +if(CURL_STATICLIB) + # Broken: LIBCURL_LIBS below; .a lib is not built + message(WARNING "Static linking is broken!") + set(ENABLE_STATIC "no") +else() + set(ENABLE_STATIC "no") +endif() +set(exec_prefix "\${prefix}") +set(includedir "\${prefix}/include") +set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") +set(LIBCURL_LIBS "") +set(libdir "${CMAKE_INSTALL_PREFIX}/lib") +# TODO CURL_LIBS also contains absolute paths which don't work with static -l... +foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) + set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}") +endforeach() +# "a" (Linux) or "lib" (Windows) +string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") +set(prefix "${CMAKE_INSTALL_PREFIX}") +# Set this to "yes" to append all libraries on which -lcurl is dependent +set(REQUIRE_LIB_DEPS "no") +# SUPPORT_FEATURES +# SUPPORT_PROTOCOLS +set(VERSIONNUM "${CURL_VERSION_NUM}") + +# Finally generate a "curl-config" matching this config +configure_file("${CURL_SOURCE_DIR}/curl-config.in" + "${CURL_BINARY_DIR}/curl-config" @ONLY) +install(FILES "${CMAKE_BINARY_DIR}/curl-config" + DESTINATION bin + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + +# Finally generate a pkg-config file matching this config +configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in" + "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) +install(FILES "${CMAKE_BINARY_DIR}/libcurl.pc" + DESTINATION lib/pkgconfig) + # This needs to be run very last so other parts of the scripts can take advantage of this. if(NOT CURL_CONFIG_HAS_BEEN_RUN_BEFORE) set(CURL_CONFIG_HAS_BEEN_RUN_BEFORE 1 CACHE INTERNAL "Flag to track whether this is the first time running CMake or if CMake has been configured before") @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2014, Daniel Stenberg, <daniel@haxx.se>. +Copyright (c) 1996 - 2015, Daniel Stenberg, <daniel@haxx.se>. All rights reserved. diff --git a/include/curl/curl.h b/include/curl/curl.h index d40b2db..64f9261 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -521,6 +521,9 @@ typedef enum { CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ CURL_LAST /* never use! */ } CURLcode; @@ -722,6 +725,10 @@ typedef enum { servers, a user can this way allow the vulnerability back. */ #define CURLSSLOPT_ALLOW_BEAST (1<<0) +/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those + SSL backends where such behavior is present. */ +#define CURLSSLOPT_NO_REVOKE (1<<1) + #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ @@ -803,6 +810,8 @@ typedef enum { #define CURLPROTO_RTMPS (1<<23) #define CURLPROTO_RTMPTS (1<<24) #define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) #define CURLPROTO_ALL (~0) /* enable everything */ /* long may be 32 or 64 bits, but we should never depend on anything else @@ -841,7 +850,7 @@ typedef enum { CINIT(WRITEDATA, OBJECTPOINT, 1), /* The full URL to get/put */ - CINIT(URL, OBJECTPOINT, 2), + CINIT(URL, OBJECTPOINT, 2), /* Port number to connect to, if other than default. */ CINIT(PORT, LONG, 3), @@ -985,7 +994,7 @@ typedef enum { CINIT(HEADER, LONG, 42), /* throw the header out too */ CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ - CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ CINIT(UPLOAD, LONG, 46), /* this is an upload */ CINIT(POST, LONG, 47), /* HTTP POST method */ CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ @@ -1611,6 +1620,31 @@ typedef enum { /* Pass in a bitmask of "header options" */ CINIT(HEADEROPT, LONG, 229), + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CINIT(PINNEDPUBLICKEY, OBJECTPOINT, 230), + + /* Path to Unix domain socket */ + CINIT(UNIX_SOCKET_PATH, OBJECTPOINT, 231), + + /* Set if we should verify the certificate status. */ + CINIT(SSL_VERIFYSTATUS, LONG, 232), + + /* Set if we should enable TLS false start. */ + CINIT(SSL_FALSESTART, LONG, 233), + + /* Do not squash dot-dot sequences */ + CINIT(PATH_AS_IS, LONG, 234), + + /* Proxy Service Name */ + CINIT(PROXY_SERVICE_NAME, OBJECTPOINT, 235), + + /* Service Name */ + CINIT(SERVICE_NAME, OBJECTPOINT, 236), + + /* Wait/don't wait for pipe/mutex to clarify */ + CINIT(PIPEWAIT, LONG, 237), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -1647,8 +1681,8 @@ typedef enum { option might be handy to force libcurl to use a specific IP version. */ #define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP versions that your system allows */ -#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ -#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ /* three convenient "aliases" that follow the name scheme better */ #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER @@ -1665,6 +1699,11 @@ enum { CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ }; +/* Convenience definition simple because the name of the version is HTTP/2 and + not 2.0. The 2_0 version of the enum name was set while the version was + still planned to be 2.0 and we stick to it for compatibility. */ +#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0 + /* * Public API enums for RTSP requests */ @@ -2028,7 +2067,7 @@ typedef enum { CURLSSLBACKEND_OPENSSL = 1, CURLSSLBACKEND_GNUTLS = 2, CURLSSLBACKEND_NSS = 3, - CURLSSLBACKEND_QSOSSL = 4, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ CURLSSLBACKEND_GSKIT = 5, CURLSSLBACKEND_POLARSSL = 6, CURLSSLBACKEND_CYASSL = 7, @@ -2235,25 +2274,30 @@ typedef struct { } curl_version_info_data; -#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ -#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */ -#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ -#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ -#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ -#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support - (deprecated) */ -#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */ -#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */ -#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ -#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ -#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ -#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ -#define CURL_VERSION_CONV (1<<12) /* character conversions supported */ -#define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */ -#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ -#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegating to winbind helper */ -#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ -#define CURL_VERSION_GSSAPI (1<<17) /* GSS-API is supported */ +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is suported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ /* * NAME curl_version_info() diff --git a/include/curl/curlver.h b/include/curl/curlver.h index ac23158..f73d7de 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,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 - 2014 Daniel Stenberg, <daniel@haxx.se>." +#define LIBCURL_COPYRIGHT "1996 - 2015 Daniel Stenberg, <daniel@haxx.se>." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.38.0-DEV" +#define LIBCURL_VERSION "7.44.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 38 +#define LIBCURL_VERSION_MINOR 44 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier @@ -52,8 +52,12 @@ This 6-digit (24 bits) hexadecimal number does not show pre-release number, and it is always a greater number in a more recent release. It makes comparisons with greater than and less than work. + + Note: This define is the full hex number and _does not_ use the + 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 0x072600 +#define LIBCURL_VERSION_NUM 0x072C00 /* * This is the date and time when the full source package was created. The @@ -66,4 +70,8 @@ */ #define LIBCURL_TIMESTAMP "DEV" +#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z) +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) + #endif /* __CURL_CURLVER_H */ diff --git a/include/curl/mprintf.h b/include/curl/mprintf.h index cc9e7f5..c6b0d76 100644 --- a/include/curl/mprintf.h +++ b/include/curl/mprintf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -57,15 +57,8 @@ CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); # undef vaprintf # define printf curl_mprintf # define fprintf curl_mfprintf -#ifdef CURLDEBUG -/* When built with CURLDEBUG we define away the sprintf functions since we - don't want internal code to be using them */ -# define sprintf sprintf_was_used -# define vsprintf vsprintf_was_used -#else # define sprintf curl_msprintf # define vsprintf curl_mvsprintf -#endif # define snprintf curl_msnprintf # define vprintf curl_mvprintf # define vfprintf curl_mvfprintf diff --git a/include/curl/multi.h b/include/curl/multi.h index 3c4acb0..36e2e94 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -74,6 +74,11 @@ typedef enum { curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ #define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM +/* bitmask bits for CURLMOPT_PIPELINING */ +#define CURLPIPE_NOTHING 0L +#define CURLPIPE_HTTP1 1L +#define CURLPIPE_MULTIPLEX 2L + typedef enum { CURLMSG_NONE, /* first, not used */ CURLMSG_DONE, /* This easy handle has completed. 'result' contains @@ -365,6 +370,12 @@ typedef enum { /* maximum number of open connections in total */ CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + /* This is the server push callback function pointer */ + CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14), + + /* This is the argument passed to the server push callback */ + CINIT(PUSHDATA, OBJECTPOINT, 15), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; @@ -392,6 +403,31 @@ CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t sockfd, void *sockp); + +/* + * Name: curl_push_callback + * + * Desc: This callback gets called when a new stream is being pushed by the + * server. It approves or denies the new stream. + * + * Returns: CURL_PUSH_OK or CURL_PUSH_DENY. + */ +#define CURL_PUSH_OK 0 +#define CURL_PUSH_DENY 1 + +struct curl_pushheaders; /* forward declaration only */ + +CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h, + size_t num); +CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h, + const char *name); + +typedef int (*curl_push_callback)(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *userp); + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index 69d41a2..13fb0fa 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -270,6 +270,8 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, (option) == CURLOPT_DNS_LOCAL_IP4 || \ (option) == CURLOPT_DNS_LOCAL_IP6 || \ (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_SERVICE_NAME || \ 0) /* evaluates to true if option takes a curl_write_callback argument */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b2bcf09..49a3409 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -48,25 +48,6 @@ endif() # ) # ENDIF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64) -if(HAVE_FEATURES_H) - set_source_files_properties( - cookie.c - easy.c - formdata.c - getenv.c - nonblock.c - hash.c - http.c - if2ip.c - mprintf.c - multi.c - sendf.c - telnet.c - transfer.c - url.c - COMPILE_FLAGS -D_BSD_SOURCE) -endif(HAVE_FEATURES_H) - # The rest of the build @@ -76,7 +57,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -if(CURL_USE_ARES) +if(USE_ARES) include_directories(${CARES_INCLUDE_DIR}) endif() @@ -106,8 +87,6 @@ endif() set_target_properties(${LIB_NAME} PROPERTIES COMPILE_DEFINITIONS BUILDING_LIBCURL) -setup_curl_dependencies(${LIB_NAME}) - # Remove the "lib" prefix since the library is already named "libcurl". set_target_properties(${LIB_NAME} PROPERTIES PREFIX "") set_target_properties(${LIB_NAME} PROPERTIES IMPORT_PREFIX "") @@ -119,4 +98,7 @@ if(WIN32) endif() endif() -install(TARGETS ${LIB_NAME} DESTINATION lib) +install(TARGETS ${LIB_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 462d72a..d444a6b 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. +# Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -20,50 +20,52 @@ # ########################################################################### -LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c \ - vtls/qssl.c vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c \ - vtls/cyassl.c vtls/curl_schannel.c vtls/curl_darwinssl.c vtls/gskit.c +LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c \ + vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c \ + vtls/cyassl.c vtls/schannel.c vtls/darwinssl.c vtls/gskit.c -LIB_VTLS_HFILES = vtls/qssl.h vtls/openssl.h vtls/vtls.h vtls/gtls.h \ - vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h \ - vtls/cyassl.h vtls/curl_schannel.h vtls/curl_darwinssl.h vtls/gskit.h +LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h \ + vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h \ + vtls/cyassl.h vtls/schannel.h vtls/darwinssl.h vtls/gskit.h -LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ - cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \ - ldap.c version.c getenv.c escape.c mprintf.c telnet.c netrc.c \ - getinfo.c transfer.c strequal.c easy.c security.c curl_fnmatch.c \ - fileinfo.c ftplistparser.c wildcard.c krb5.c memdebug.c http_chunks.c \ - strtok.c connect.c llist.c hash.c multi.c content_encoding.c share.c \ - http_digest.c md4.c md5.c http_negotiate.c inet_pton.c strtoofft.c \ - strerror.c amigaos.c hostasyn.c hostip4.c hostip6.c hostsyn.c \ - inet_ntop.c parsedate.c select.c tftp.c splay.c strdup.c socks.c \ - ssh.c rawstr.c curl_addrinfo.c socks_gssapi.c socks_sspi.c \ - curl_sspi.c slist.c nonblock.c curl_memrchr.c imap.c pop3.c smtp.c \ - pingpong.c rtsp.c curl_threads.c warnless.c hmac.c curl_rtmp.c \ - openldap.c curl_gethostname.c gopher.c idn_win32.c \ - http_negotiate_sspi.c http_proxy.c non-ascii.c asyn-ares.c \ - asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c \ - curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_multibyte.c \ - hostcheck.c bundles.c conncache.c pipeline.c dotdot.c x509asn1.c \ - http2.c curl_sasl_sspi.c +LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ + cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \ + ldap.c version.c getenv.c escape.c mprintf.c telnet.c netrc.c \ + getinfo.c transfer.c strequal.c easy.c security.c curl_fnmatch.c \ + fileinfo.c ftplistparser.c wildcard.c krb5.c memdebug.c http_chunks.c \ + strtok.c connect.c llist.c hash.c multi.c content_encoding.c share.c \ + http_digest.c md4.c md5.c http_negotiate.c inet_pton.c strtoofft.c \ + strerror.c amigaos.c hostasyn.c hostip4.c hostip6.c hostsyn.c \ + inet_ntop.c parsedate.c select.c tftp.c splay.c strdup.c socks.c \ + ssh.c rawstr.c curl_addrinfo.c socks_gssapi.c socks_sspi.c \ + curl_sspi.c slist.c nonblock.c curl_memrchr.c imap.c pop3.c smtp.c \ + pingpong.c rtsp.c curl_threads.c warnless.c hmac.c curl_rtmp.c \ + openldap.c curl_gethostname.c gopher.c idn_win32.c \ + http_negotiate_sspi.c http_proxy.c non-ascii.c asyn-ares.c \ + asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c \ + curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_multibyte.c \ + hostcheck.c conncache.c pipeline.c dotdot.c x509asn1.c \ + http2.c curl_sasl_sspi.c smb.c curl_sasl_gssapi.c curl_endian.c \ + curl_des.c -LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ - formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ - speedcheck.h urldata.h curl_ldap.h escape.h telnet.h getinfo.h \ - strequal.h curl_sec.h memdebug.h http_chunks.h curl_fnmatch.h \ - wildcard.h fileinfo.h ftplistparser.h strtok.h connect.h llist.h \ - hash.h content_encoding.h share.h curl_md4.h curl_md5.h http_digest.h \ - http_negotiate.h inet_pton.h amigaos.h strtoofft.h strerror.h \ - inet_ntop.h curlx.h curl_memory.h curl_setup.h transfer.h select.h \ - easyif.h multiif.h parsedate.h tftp.h sockaddr.h splay.h strdup.h \ - socks.h ssh.h curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h \ - slist.h nonblock.h curl_memrchr.h imap.h pop3.h smtp.h pingpong.h \ - rtsp.h curl_threads.h warnless.h curl_hmac.h curl_rtmp.h \ - curl_gethostname.h gopher.h http_proxy.h non-ascii.h asyn.h \ - curl_ntlm.h curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h \ - curl_ntlm_msgs.h curl_sasl.h curl_multibyte.h hostcheck.h bundles.h \ - conncache.h curl_setup_once.h multihandle.h setup-vms.h pipeline.h \ - dotdot.h x509asn1.h http2.h sigpipe.h +LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ + formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ + speedcheck.h urldata.h curl_ldap.h escape.h telnet.h getinfo.h \ + strequal.h curl_sec.h memdebug.h http_chunks.h curl_fnmatch.h \ + wildcard.h fileinfo.h ftplistparser.h strtok.h connect.h llist.h \ + hash.h content_encoding.h share.h curl_md4.h curl_md5.h http_digest.h \ + http_negotiate.h inet_pton.h amigaos.h strtoofft.h strerror.h \ + inet_ntop.h curlx.h curl_memory.h curl_setup.h transfer.h select.h \ + easyif.h multiif.h parsedate.h tftp.h sockaddr.h splay.h strdup.h \ + socks.h ssh.h curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h \ + slist.h nonblock.h curl_memrchr.h imap.h pop3.h smtp.h pingpong.h \ + rtsp.h curl_threads.h warnless.h curl_hmac.h curl_rtmp.h \ + curl_gethostname.h gopher.h http_proxy.h non-ascii.h asyn.h \ + curl_ntlm.h curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h \ + curl_ntlm_msgs.h curl_sasl.h curl_multibyte.h hostcheck.h \ + conncache.h curl_setup_once.h multihandle.h setup-vms.h pipeline.h \ + dotdot.h x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \ + curl_printf.h LIB_RCFILES = libcurl.rc diff --git a/lib/amigaos.c b/lib/amigaos.c index 34f95e9..e3ff85f 100644 --- a/lib/amigaos.c +++ b/lib/amigaos.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -71,7 +71,7 @@ bool Curl_amiga_init() } #ifdef __libnix__ -ADD2EXIT(Curl_amiga_cleanup,-50); +ADD2EXIT(Curl_amiga_cleanup, -50); #endif #endif /* __AMIGA__ && ! __ixemul__ */ diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 01a9c9b..98ecdfd 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -68,9 +68,7 @@ #include "connect.h" #include "select.h" #include "progress.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) @@ -166,7 +164,7 @@ void Curl_resolver_cleanup(void *resolver) int Curl_resolver_duphandle(void **to, void *from) { /* Clone the ares channel for the new handle */ - if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from)) + if(ARES_SUCCESS != ares_dup((ares_channel*)to, (ares_channel)from)) return CURLE_FAILED_INIT; return CURLE_OK; } @@ -178,7 +176,7 @@ static void destroy_async_data (struct Curl_async *async); */ void Curl_resolver_cancel(struct connectdata *conn) { - if(conn && conn->data && conn->data->state.resolver) + if(conn->data && conn->data->state.resolver) ares_cancel((ares_channel)conn->data->state.resolver); destroy_async_data(&conn->async); } @@ -188,8 +186,7 @@ void Curl_resolver_cancel(struct connectdata *conn) */ static void destroy_async_data (struct Curl_async *async) { - if(async->hostname) - free(async->hostname); + free(async->hostname); if(async->os_specific) { struct ResolverResults *res = (struct ResolverResults *)async->os_specific; @@ -315,7 +312,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, struct SessionHandle *data = conn->data; struct ResolverResults *res = (struct ResolverResults *) conn->async.os_specific; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; *dns = NULL; @@ -329,7 +326,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, if(!conn->async.dns) { failf(data, "Could not resolve: %s (%s)", conn->async.hostname, ares_strerror(conn->async.status)); - rc = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: + result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } else @@ -338,7 +335,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, destroy_async_data(&conn->async); } - return rc; + return result; } /* @@ -355,7 +352,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { - CURLcode rc=CURLE_OK; + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; long timeout; struct timeval now = Curl_tvnow(); @@ -388,13 +385,13 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, timeout_ms = 1000; waitperform(conn, timeout_ms); - Curl_resolver_is_resolved(conn,&temp_entry); + Curl_resolver_is_resolved(conn, &temp_entry); if(conn->async.done) break; if(Curl_pgrsUpdate(conn)) { - rc = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; timeout = -1; /* trigger the cancel below */ } else { @@ -403,6 +400,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, timeout -= timediff?timediff:1; /* always deduct at least 1 */ now = now2; /* for next loop */ } + if(timeout < 0) { /* our timeout, so we cancel the ares operation */ ares_cancel((ares_channel)data->state.resolver); @@ -412,18 +410,17 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, /* Operation complete, if the lookup was successful we now have the entry in the cache. */ - if(entry) *entry = conn->async.dns; - if(rc) + if(result) /* close the connection, since we can't return failure here without cleaning up this connection properly. TODO: remove this action from here, it is not a name resolver decision. */ connclose(conn, "c-ares resolve failed"); - return rc; + return result; } /* Connects results to the list */ @@ -536,15 +533,15 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, bufp = strdup(hostname); if(bufp) { struct ResolverResults *res = NULL; - Curl_safefree(conn->async.hostname); + free(conn->async.hostname); conn->async.hostname = bufp; conn->async.port = port; conn->async.done = FALSE; /* not done */ conn->async.status = 0; /* clear */ conn->async.dns = NULL; /* clear */ - res = calloc(sizeof(struct ResolverResults),1); + res = calloc(sizeof(struct ResolverResults), 1); if(!res) { - Curl_safefree(conn->async.hostname); + free(conn->async.hostname); conn->async.hostname = NULL; return NULL; } diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index e4ad32b..bd47d5a 100644 --- a/lib/asyn-thread.c +++ b/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -69,11 +69,9 @@ #include "inet_ntop.h" #include "curl_threads.h" #include "connect.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" + /* The last #include file should be: */ #include "memdebug.h" @@ -192,13 +190,12 @@ void destroy_thread_sync_data(struct thread_sync_data * tsd) free(tsd->mtx); } - if(tsd->hostname) - free(tsd->hostname); + free(tsd->hostname); if(tsd->res) Curl_freeaddrinfo(tsd->res); - memset(tsd,0,sizeof(*tsd)); + memset(tsd, 0, sizeof(*tsd)); } /* Initialize resolver thread synchronization data */ @@ -366,9 +363,7 @@ static void destroy_async_data (struct Curl_async *async) } async->os_specific = NULL; - if(async->hostname) - free(async->hostname); - + free(async->hostname); async->hostname = NULL; } @@ -398,7 +393,7 @@ static bool init_resolve_thread (struct connectdata *conn, if(!init_thread_sync_data(td, hostname, port, hints)) goto err_exit; - Curl_safefree(conn->async.hostname); + free(conn->async.hostname); conn->async.hostname = strdup(hostname); if(!conn->async.hostname) goto err_exit; @@ -434,19 +429,21 @@ static bool init_resolve_thread (struct connectdata *conn, static CURLcode resolver_error(struct connectdata *conn) { const char *host_or_proxy; - CURLcode rc; + CURLcode result; + if(conn->bits.httpproxy) { host_or_proxy = "proxy"; - rc = CURLE_COULDNT_RESOLVE_PROXY; + result = CURLE_COULDNT_RESOLVE_PROXY; } else { host_or_proxy = "host"; - rc = CURLE_COULDNT_RESOLVE_HOST; + result = CURLE_COULDNT_RESOLVE_HOST; } failf(conn->data, "Could not resolve %s: %s", host_or_proxy, conn->async.hostname); - return rc; + + return result; } /* @@ -463,13 +460,13 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; DEBUGASSERT(conn && td); /* wait for the thread to resolve the name */ if(Curl_thread_join(&td->thread_hnd)) - rc = getaddrinfo_complete(conn); + result = getaddrinfo_complete(conn); else DEBUGASSERT(0); @@ -480,14 +477,14 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, if(!conn->async.dns) /* a name was not resolved, report error */ - rc = resolver_error(conn); + result = resolver_error(conn); destroy_async_data(&conn->async); if(!conn->async.dns) connclose(conn, "asynch resolve failed"); - return (rc); + return result; } /* @@ -517,9 +514,9 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, getaddrinfo_complete(conn); if(!conn->async.dns) { - CURLcode rc = resolver_error(conn); + CURLcode result = resolver_error(conn); destroy_async_data(&conn->async); - return rc; + return result; } destroy_async_data(&conn->async); *entry = conn->async.dns; @@ -541,7 +538,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, td->poll_interval = 250; td->interval_end = elapsed + td->poll_interval; - Curl_expire_latest(conn->data, td->poll_interval); + Curl_expire(conn->data, td->poll_interval); } return CURLE_OK; @@ -633,7 +630,7 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, } if((pf != PF_INET) && !Curl_ipv6works()) - /* the stack seems to be a non-ipv6 one */ + /* The stack seems to be a non-IPv6 one */ pf = PF_INET; #endif /* CURLRES_IPV6 */ diff --git a/lib/base64.c b/lib/base64.c index bd9ba35..6b87eed 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,17 +23,14 @@ /* Base64 encoding/decoding */ #include "curl_setup.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "urldata.h" /* for the SessionHandle definition */ #include "warnless.h" #include "curl_base64.h" -#include "curl_memory.h" #include "non-ascii.h" -/* include memdebug.h last */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* ---- Base64 Encoding/Decoding Table --- */ @@ -49,10 +46,10 @@ static size_t decodeQuantum(unsigned char *dest, const char *src) { size_t padding = 0; const char *s, *p; - unsigned long i, v, x = 0; + unsigned long i, x = 0; for(i = 0, s = src; i < 4; i++, s++) { - v = 0; + unsigned long v = 0; if(*s == '=') { x = (x << 6); @@ -107,7 +104,6 @@ CURLcode Curl_base64_decode(const char *src, size_t length = 0; size_t padding = 0; size_t i; - size_t result; size_t numQuantums; size_t rawlen = 0; unsigned char *pos; @@ -151,9 +147,9 @@ CURLcode Curl_base64_decode(const char *src, /* Decode the quantums */ for(i = 0; i < numQuantums; i++) { - result = decodeQuantum(pos, src); + size_t result = decodeQuantum(pos, src); if(!result) { - Curl_safefree(newstr); + free(newstr); return CURLE_BAD_CONTENT_ENCODING; } @@ -256,8 +252,7 @@ static CURLcode base64_encode(const char *table64, *output = '\0'; *outptr = base64data; /* return pointer to new data, allocated memory */ - if(convbuf) - free(convbuf); + free(convbuf); *outlen = strlen(base64data); /* return the length of the new data */ diff --git a/lib/bundles.c b/lib/bundles.c deleted file mode 100644 index aadf026..0000000 --- a/lib/bundles.c +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.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 <curl/curl.h> - -#include "urldata.h" -#include "url.h" -#include "progress.h" -#include "multiif.h" -#include "bundles.h" -#include "sendf.h" -#include "rawstr.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -static void conn_llist_dtor(void *user, void *element) -{ - struct connectdata *data = element; - (void)user; - - data->bundle = NULL; -} - -CURLcode Curl_bundle_create(struct SessionHandle *data, - struct connectbundle **cb_ptr) -{ - (void)data; - DEBUGASSERT(*cb_ptr == NULL); - *cb_ptr = malloc(sizeof(struct connectbundle)); - if(!*cb_ptr) - return CURLE_OUT_OF_MEMORY; - - (*cb_ptr)->num_connections = 0; - (*cb_ptr)->server_supports_pipelining = FALSE; - - (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor); - if(!(*cb_ptr)->conn_list) { - Curl_safefree(*cb_ptr); - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} - -void Curl_bundle_destroy(struct connectbundle *cb_ptr) -{ - if(!cb_ptr) - return; - - if(cb_ptr->conn_list) { - Curl_llist_destroy(cb_ptr->conn_list, NULL); - cb_ptr->conn_list = NULL; - } - Curl_safefree(cb_ptr); -} - -/* Add a connection to a bundle */ -CURLcode Curl_bundle_add_conn(struct connectbundle *cb_ptr, - struct connectdata *conn) -{ - if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn)) - return CURLE_OUT_OF_MEMORY; - - conn->bundle = cb_ptr; - - cb_ptr->num_connections++; - return CURLE_OK; -} - -/* Remove a connection from a bundle */ -int Curl_bundle_remove_conn(struct connectbundle *cb_ptr, - struct connectdata *conn) -{ - struct curl_llist_element *curr; - - curr = cb_ptr->conn_list->head; - while(curr) { - if(curr->ptr == conn) { - Curl_llist_remove(cb_ptr->conn_list, curr, NULL); - cb_ptr->num_connections--; - conn->bundle = NULL; - return 1; /* we removed a handle */ - } - curr = curr->next; - } - return 0; -} diff --git a/lib/conncache.c b/lib/conncache.c index 5bbcf3c..c712ed7 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,66 +31,134 @@ #include "multiif.h" #include "sendf.h" #include "rawstr.h" -#include "bundles.h" #include "conncache.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" -static void free_bundle_hash_entry(void *freethis) +static void conn_llist_dtor(void *user, void *element) { - struct connectbundle *b = (struct connectbundle *) freethis; + struct connectdata *data = element; + (void)user; - Curl_bundle_destroy(b); + data->bundle = NULL; } -struct conncache *Curl_conncache_init(int size) +static CURLcode bundle_create(struct SessionHandle *data, + struct connectbundle **cb_ptr) { - struct conncache *connc; - - connc = calloc(1, sizeof(struct conncache)); - if(!connc) - return NULL; + (void)data; + DEBUGASSERT(*cb_ptr == NULL); + *cb_ptr = malloc(sizeof(struct connectbundle)); + if(!*cb_ptr) + return CURLE_OUT_OF_MEMORY; + + (*cb_ptr)->num_connections = 0; + (*cb_ptr)->multiuse = BUNDLE_UNKNOWN; + + (*cb_ptr)->conn_list = Curl_llist_alloc((curl_llist_dtor) conn_llist_dtor); + if(!(*cb_ptr)->conn_list) { + Curl_safefree(*cb_ptr); + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} - connc->hash = Curl_hash_alloc(size, Curl_hash_str, - Curl_str_key_compare, free_bundle_hash_entry); +static void bundle_destroy(struct connectbundle *cb_ptr) +{ + if(!cb_ptr) + return; - if(!connc->hash) { - free(connc); - return NULL; + if(cb_ptr->conn_list) { + Curl_llist_destroy(cb_ptr->conn_list, NULL); + cb_ptr->conn_list = NULL; } + free(cb_ptr); +} + +/* Add a connection to a bundle */ +static CURLcode bundle_add_conn(struct connectbundle *cb_ptr, + struct connectdata *conn) +{ + if(!Curl_llist_insert_next(cb_ptr->conn_list, cb_ptr->conn_list->tail, conn)) + return CURLE_OUT_OF_MEMORY; + + conn->bundle = cb_ptr; - return connc; + cb_ptr->num_connections++; + return CURLE_OK; } -void Curl_conncache_destroy(struct conncache *connc) +/* Remove a connection from a bundle */ +static int bundle_remove_conn(struct connectbundle *cb_ptr, + struct connectdata *conn) { - if(connc) { - Curl_hash_destroy(connc->hash); - connc->hash = NULL; - free(connc); + struct curl_llist_element *curr; + + curr = cb_ptr->conn_list->head; + while(curr) { + if(curr->ptr == conn) { + Curl_llist_remove(cb_ptr->conn_list, curr, NULL); + cb_ptr->num_connections--; + conn->bundle = NULL; + return 1; /* we removed a handle */ + } + curr = curr->next; } + return 0; } -struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc, - char *hostname) +static void free_bundle_hash_entry(void *freethis) { - struct connectbundle *bundle = NULL; + struct connectbundle *b = (struct connectbundle *) freethis; + + bundle_destroy(b); +} +int Curl_conncache_init(struct conncache *connc, int size) +{ + return Curl_hash_init(&connc->hash, size, Curl_hash_str, + Curl_str_key_compare, free_bundle_hash_entry); +} + +void Curl_conncache_destroy(struct conncache *connc) +{ if(connc) - bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1); + Curl_hash_destroy(&connc->hash); +} + +/* returns an allocated key to find a bundle for this connection */ +static char *hashkey(struct connectdata *conn) +{ + return aprintf("%s:%d", + conn->bits.proxy?conn->proxy.name:conn->host.name, + conn->localport); +} + +/* Look up the bundle with all the connections to the same host this + connectdata struct is setup to use. */ +struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, + struct conncache *connc) +{ + struct connectbundle *bundle = NULL; + if(connc) { + char *key = hashkey(conn); + if(key) { + bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); + free(key); + } + } return bundle; } static bool conncache_add_bundle(struct conncache *connc, - char *hostname, + char *key, struct connectbundle *bundle) { - void *p; - - p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle); + void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle); return p?TRUE:FALSE; } @@ -104,14 +172,14 @@ static void conncache_remove_bundle(struct conncache *connc, if(!connc) return; - Curl_hash_start_iterate(connc->hash, &iter); + Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { if(he->ptr == bundle) { /* The bundle is destroyed by the hash destructor function, free_bundle_hash_entry() */ - Curl_hash_delete(connc->hash, he->key, he->key_len); + Curl_hash_delete(&connc->hash, he->key, he->key_len); return; } @@ -127,23 +195,32 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectbundle *new_bundle = NULL; struct SessionHandle *data = conn->data; - bundle = Curl_conncache_find_bundle(data->state.conn_cache, - conn->host.name); + bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); if(!bundle) { - result = Curl_bundle_create(data, &new_bundle); - if(result != CURLE_OK) + char *key; + int rc; + + result = bundle_create(data, &new_bundle); + if(result) return result; - if(!conncache_add_bundle(data->state.conn_cache, - conn->host.name, new_bundle)) { - Curl_bundle_destroy(new_bundle); + key = hashkey(conn); + if(!key) { + bundle_destroy(new_bundle); + return CURLE_OUT_OF_MEMORY; + } + + rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle); + free(key); + if(!rc) { + bundle_destroy(new_bundle); return CURLE_OUT_OF_MEMORY; } bundle = new_bundle; } - result = Curl_bundle_add_conn(bundle, conn); - if(result != CURLE_OK) { + result = bundle_add_conn(bundle, conn); + if(result) { if(new_bundle) conncache_remove_bundle(data->state.conn_cache, new_bundle); return result; @@ -152,6 +229,10 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, conn->connection_id = connc->next_connection_id++; connc->num_connections++; + DEBUGF(infof(conn->data, "Added connection %ld. " + "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n", + conn->connection_id, (curl_off_t) connc->num_connections)); + return CURLE_OK; } @@ -163,7 +244,7 @@ void Curl_conncache_remove_conn(struct conncache *connc, /* The bundle pointer can be NULL, since this function can be called due to a failed connection attempt, before being added to a bundle */ if(bundle) { - Curl_bundle_remove_conn(bundle, conn); + bundle_remove_conn(bundle, conn); if(bundle->num_connections == 0) { conncache_remove_bundle(connc, bundle); } @@ -171,8 +252,9 @@ void Curl_conncache_remove_conn(struct conncache *connc, if(connc) { connc->num_connections--; - DEBUGF(infof(conn->data, "The cache now contains %d members\n", - connc->num_connections)); + DEBUGF(infof(conn->data, "The cache now contains %" + CURL_FORMAT_CURL_OFF_TU " members\n", + (curl_off_t) connc->num_connections)); } } } @@ -194,12 +276,11 @@ void Curl_conncache_foreach(struct conncache *connc, if(!connc) return; - Curl_hash_start_iterate(connc->hash, &iter); + Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { struct connectbundle *bundle; - struct connectdata *conn; bundle = he->ptr; he = Curl_hash_next_element(&iter); @@ -208,7 +289,7 @@ void Curl_conncache_foreach(struct conncache *connc, while(curr) { /* Yes, we need to update curr before calling func(), because func() might decide to remove the connection */ - conn = curr->ptr; + struct connectdata *conn = curr->ptr; curr = curr->next; if(1 == func(conn, param)) @@ -223,14 +304,14 @@ struct connectdata * Curl_conncache_find_first_connection(struct conncache *connc) { struct curl_hash_iterator iter; - struct curl_llist_element *curr; struct curl_hash_element *he; struct connectbundle *bundle; - Curl_hash_start_iterate(connc->hash, &iter); + Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { + struct curl_llist_element *curr; bundle = he->ptr; curr = bundle->conn_list->head; diff --git a/lib/conncache.h b/lib/conncache.h index d793f24..59181bf 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -7,6 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * + * Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, <linus@haxx.se> * * This software is licensed as described in the file COPYING, which @@ -23,18 +24,30 @@ ***************************************************************************/ struct conncache { - struct curl_hash *hash; + struct curl_hash hash; size_t num_connections; long next_connection_id; struct timeval last_cleanup; }; -struct conncache *Curl_conncache_init(int size); +#define BUNDLE_NO_MULTIUSE -1 +#define BUNDLE_UNKNOWN 0 /* initial value */ +#define BUNDLE_PIPELINING 1 +#define BUNDLE_MULTIPLEX 2 + +struct connectbundle { + int multiuse; /* supports multi-use */ + size_t num_connections; /* Number of connections in the bundle */ + struct curl_llist *conn_list; /* The connectdata members of the bundle */ +}; + +int Curl_conncache_init(struct conncache *, int size); void Curl_conncache_destroy(struct conncache *connc); -struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc, - char *hostname); +/* return the correct bundle, to a host or a proxy */ +struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, + struct conncache *connc); CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectdata *conn); diff --git a/lib/connect.c b/lib/connect.c index fb315fc..18ac32c 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -56,15 +56,12 @@ #include <inet.h> #endif -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "urldata.h" #include "sendf.h" #include "if2ip.h" #include "strerror.h" #include "connect.h" -#include "curl_memory.h" #include "select.h" #include "url.h" /* for Curl_safefree() */ #include "multiif.h" @@ -77,7 +74,8 @@ #include "conncache.h" #include "multihandle.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #ifdef __SYMBIAN32__ @@ -238,7 +236,7 @@ long Curl_timeleft(struct SessionHandle *data, } static CURLcode bindlocal(struct connectdata *conn, - curl_socket_t sockfd, int af) + curl_socket_t sockfd, int af, unsigned int scope) { struct SessionHandle *data = conn->data; @@ -257,12 +255,6 @@ static CURLcode bindlocal(struct connectdata *conn, int portnum = data->set.localportrange; const char *dev = data->set.str[STRING_DEVICE]; int error; - char myhost[256] = ""; - int done = 0; /* -1 for error, 1 for address found */ - bool is_interface = FALSE; - bool is_host = FALSE; - static const char *if_prefix = "if!"; - static const char *host_prefix = "host!"; /************************************************************* * Select device to bind socket to @@ -274,6 +266,13 @@ static CURLcode bindlocal(struct connectdata *conn, memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); if(dev && (strlen(dev)<255) ) { + char myhost[256] = ""; + int done = 0; /* -1 for error, 1 for address found */ + bool is_interface = FALSE; + bool is_host = FALSE; + static const char *if_prefix = "if!"; + static const char *host_prefix = "host!"; + if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { dev += strlen(if_prefix); is_interface = TRUE; @@ -285,7 +284,8 @@ static CURLcode bindlocal(struct connectdata *conn, /* interface */ if(!is_host) { - switch(Curl_if2ip(af, conn->scope, dev, myhost, sizeof(myhost))) { + switch(Curl_if2ip(af, scope, conn->scope_id, dev, + myhost, sizeof(myhost))) { case IF2IP_NOT_FOUND: if(is_interface) { /* Do not fall back to treating it as a host name */ @@ -374,7 +374,7 @@ static CURLcode bindlocal(struct connectdata *conn, if(done > 0) { #ifdef ENABLE_IPV6 - /* ipv6 address */ + /* IPv6 address */ if(af == AF_INET6) { #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID char *scope_ptr = strchr(myhost, '%'); @@ -397,7 +397,7 @@ static CURLcode bindlocal(struct connectdata *conn, } else #endif - /* ipv4 address */ + /* IPv4 address */ if((af == AF_INET) && (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { si4->sin_family = AF_INET; @@ -540,7 +540,8 @@ static CURLcode trynextip(struct connectdata *conn, int sockindex, int tempindex) { - CURLcode rc = CURLE_COULDNT_CONNECT; + const int other = tempindex ^ 1; + CURLcode result = CURLE_COULDNT_CONNECT; /* First clean up after the failed socket. Don't close it yet to ensure that the next IP's socket gets a different @@ -558,27 +559,29 @@ static CURLcode trynextip(struct connectdata *conn, family = conn->tempaddr[tempindex]->ai_family; ai = conn->tempaddr[tempindex]->ai_next; } +#ifdef ENABLE_IPV6 else if(conn->tempaddr[0]) { /* happy eyeballs - try the other protocol family */ int firstfamily = conn->tempaddr[0]->ai_family; -#ifdef ENABLE_IPV6 family = (firstfamily == AF_INET) ? AF_INET6 : AF_INET; -#else - family = firstfamily; -#endif ai = conn->tempaddr[0]->ai_next; } +#endif while(ai) { - while(ai && ai->ai_family != family) - ai = ai->ai_next; + if(conn->tempaddr[other]) { + /* we can safely skip addresses of the other protocol family */ + while(ai && ai->ai_family != family) + ai = ai->ai_next; + } if(ai) { - rc = singleipconnect(conn, ai, &conn->tempsock[tempindex]); - if(rc == CURLE_COULDNT_CONNECT) { + result = singleipconnect(conn, ai, &conn->tempsock[tempindex]); + if(result == CURLE_COULDNT_CONNECT) { ai = ai->ai_next; continue; } + conn->tempaddr[tempindex] = ai; } break; @@ -588,7 +591,7 @@ static CURLcode trynextip(struct connectdata *conn, if(fd_to_close != CURL_SOCKET_BAD) Curl_closesocket(conn, fd_to_close); - return rc; + return result; } /* Copies connection info into the session handle to make it available @@ -656,7 +659,6 @@ static bool getaddressinfo(struct sockaddr* sa, char* addr, connection */ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) { - int error; curl_socklen_t len; struct Curl_sockaddr_storage ssrem; struct Curl_sockaddr_storage ssloc; @@ -667,6 +669,7 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) return; if(!conn->bits.reuse) { + int error; len = sizeof(struct Curl_sockaddr_storage); if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) { @@ -677,6 +680,7 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) } len = sizeof(struct Curl_sockaddr_storage); + memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) { error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", @@ -716,11 +720,11 @@ CURLcode Curl_is_connected(struct connectdata *conn, bool *connected) { struct SessionHandle *data = conn->data; - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; long allow; int error = 0; struct timeval now; - int result; + int rc; int i; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); @@ -745,6 +749,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, } for(i=0; i<2; i++) { + const int other = i ^ 1; if(conn->tempsock[i] == CURL_SOCKET_BAD) continue; @@ -756,9 +761,9 @@ CURLcode Curl_is_connected(struct connectdata *conn, #endif /* check socket for connect */ - result = Curl_socket_ready(CURL_SOCKET_BAD, conn->tempsock[i], 0); + rc = Curl_socket_ready(CURL_SOCKET_BAD, conn->tempsock[i], 0); - if(result == 0) { /* no connection yet */ + if(rc == 0) { /* no connection yet */ if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) { infof(data, "After %ldms connect time, move on!\n", conn->timeoutms_per_addr); @@ -771,10 +776,9 @@ CURLcode Curl_is_connected(struct connectdata *conn, trynextip(conn, sockindex, 1); } } - else if(result == CURL_CSELECT_OUT) { + else if(rc == CURL_CSELECT_OUT) { if(verifyconnect(conn->tempsock[i], &error)) { /* we are connected with TCP, awesome! */ - int other = i ^ 1; /* use this socket from now on */ conn->sock[sockindex] = conn->tempsock[i]; @@ -788,9 +792,9 @@ CURLcode Curl_is_connected(struct connectdata *conn, } /* see if we need to do any proxy magic first once we connected */ - code = Curl_connected_proxy(conn, sockindex); - if(code) - return code; + result = Curl_connected_proxy(conn, sockindex); + if(result) + return result; conn->bits.tcpconnect[sockindex] = TRUE; @@ -805,7 +809,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, else infof(data, "Connection failed\n"); } - else if(result & CURL_CSELECT_ERR) + else if(rc & CURL_CSELECT_ERR) (void)verifyconnect(conn->tempsock[i], &error); /* @@ -813,10 +817,11 @@ CURLcode Curl_is_connected(struct connectdata *conn, * address" for the given host. But first remember the latest error. */ if(error) { - char ipaddress[MAX_IPADR_LEN]; data->state.os_errno = error; SET_SOCKERRNO(error); if(conn->tempaddr[i]) { + CURLcode status; + char ipaddress[MAX_IPADR_LEN]; Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN); infof(data, "connect to %s port %ld failed: %s\n", ipaddress, conn->port, Curl_strerror(conn, error)); @@ -824,21 +829,24 @@ CURLcode Curl_is_connected(struct connectdata *conn, conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ? allow : allow / 2; - code = trynextip(conn, sockindex, i); + status = trynextip(conn, sockindex, i); + if(status != CURLE_COULDNT_CONNECT + || conn->tempsock[other] == CURL_SOCKET_BAD) + /* the last attempt failed and no other sockets remain open */ + result = status; } } } - if(code) { + if(result) { /* no more addresses to try */ /* if the first address family runs out of addresses to try before the happy eyeball timeout, go ahead and try the next family now */ if(conn->tempaddr[1] == NULL) { - int rc; - rc = trynextip(conn, sockindex, 1); - if(rc == CURLE_OK) - return CURLE_OK; + result = trynextip(conn, sockindex, 1); + if(!result) + return result; } failf(data, "Failed to connect to %s port %ld: %s", @@ -846,7 +854,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, conn->port, Curl_strerror(conn, error)); } - return code; + return result; } static void tcpnodelay(struct connectdata *conn, @@ -875,7 +883,7 @@ static void tcpnodelay(struct connectdata *conn, infof(data, "Could not set TCP_NODELAY: %s\n", Curl_strerror(conn, SOCKERRNO)); else - infof(data,"TCP_NODELAY set\n"); + infof(data, "TCP_NODELAY set\n"); #else (void)conn; (void)sockfd; @@ -941,16 +949,21 @@ void Curl_sndbufset(curl_socket_t sockfd) detectOsState = DETECT_OS_VISTA_OR_LATER; } #else - ULONGLONG majorVersionMask; + ULONGLONG cm; OSVERSIONINFOEX osver; memset(&osver, 0, sizeof(osver)); osver.dwOSVersionInfoSize = sizeof(osver); osver.dwMajorVersion = majorVersion; - majorVersionMask = VerSetConditionMask(0, VER_MAJORVERSION, - VER_GREATER_EQUAL); - if(VerifyVersionInfo(&osver, VER_MAJORVERSION, majorVersionMask)) + cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); + + if(VerifyVersionInfo(&osver, (VER_MAJORVERSION | VER_MINORVERSION | + VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR), + cm)) detectOsState = DETECT_OS_VISTA_OR_LATER; else detectOsState = DETECT_OS_PREVISTA; @@ -977,10 +990,9 @@ void Curl_sndbufset(curl_socket_t sockfd) * singleipconnect() connects to the given IP only, and it may return without * having connected. */ -static CURLcode -singleipconnect(struct connectdata *conn, - const Curl_addrinfo *ai, - curl_socket_t *sockp) +static CURLcode singleipconnect(struct connectdata *conn, + const Curl_addrinfo *ai, + curl_socket_t *sockp) { struct Curl_sockaddr_ex addr; int rc; @@ -988,14 +1000,15 @@ singleipconnect(struct connectdata *conn, bool isconnected = FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd; - CURLcode res = CURLE_OK; + CURLcode result; char ipaddress[MAX_IPADR_LEN]; long port; + bool is_tcp; *sockp = CURL_SOCKET_BAD; - res = Curl_socket(conn, ai, &addr, &sockfd); - if(res) + result = Curl_socket(conn, ai, &addr, &sockfd); + if(result) /* Failed to create the socket, but still return OK since we signal the lack of socket as well. This allows the parent function to keep looping over alternative addresses/socket families etc. */ @@ -1013,14 +1026,20 @@ singleipconnect(struct connectdata *conn, } infof(data, " Trying %s...\n", ipaddress); - if(data->set.tcp_nodelay) +#ifdef ENABLE_IPV6 + is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) && + addr.socktype == SOCK_STREAM; +#else + is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM; +#endif + if(is_tcp && data->set.tcp_nodelay) tcpnodelay(conn, sockfd); nosigpipe(conn, sockfd); Curl_sndbufset(sockfd); - if(data->set.tcp_keepalive) + if(is_tcp && data->set.tcp_keepalive) tcpkeepalive(data, sockfd); if(data->set.fsockopt) { @@ -1038,19 +1057,26 @@ singleipconnect(struct connectdata *conn, } /* possibly bind the local end to an IP, interface or port */ - res = bindlocal(conn, sockfd, addr.family); - if(res) { - Curl_closesocket(conn, sockfd); /* close socket and bail out */ - if(res == CURLE_UNSUPPORTED_PROTOCOL) { - /* The address family is not supported on this interface. - We can continue trying addresses */ - return CURLE_OK; + if(addr.family == AF_INET +#ifdef ENABLE_IPV6 + || addr.family == AF_INET6 +#endif + ) { + result = bindlocal(conn, sockfd, addr.family, + Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr)); + if(result) { + Curl_closesocket(conn, sockfd); /* close socket and bail out */ + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + /* The address family is not supported on this interface. + We can continue trying addresses */ + return CURLE_COULDNT_CONNECT; + } + return result; } - return res; } /* set socket non-blocking */ - curlx_nonblock(sockfd, TRUE); + (void)curlx_nonblock(sockfd, TRUE); conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) @@ -1084,25 +1110,25 @@ singleipconnect(struct connectdata *conn, case EAGAIN: #endif #endif - res = CURLE_OK; + result = CURLE_OK; break; default: /* unknown error, fallthrough and try another address! */ infof(data, "Immediate connect fail for %s: %s\n", - ipaddress, Curl_strerror(conn,error)); + ipaddress, Curl_strerror(conn, error)); data->state.os_errno = error; /* connect failed */ Curl_closesocket(conn, sockfd); - res = CURLE_COULDNT_CONNECT; + result = CURLE_COULDNT_CONNECT; } } - if(!res) + if(!result) *sockp = sockfd; - return res; + return result; } /* @@ -1116,7 +1142,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ { struct SessionHandle *data = conn->data; struct timeval before = Curl_tvnow(); - CURLcode res = CURLE_COULDNT_CONNECT; + CURLcode result = CURLE_COULDNT_CONNECT; long timeout_ms = Curl_timeleft(data, &before, TRUE); @@ -1139,14 +1165,17 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ /* start connecting to first IP */ while(conn->tempaddr[0]) { - res = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0])); - if(res == CURLE_OK) - break; + result = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0])); + if(!result) + break; conn->tempaddr[0] = conn->tempaddr[0]->ai_next; } - if(conn->tempsock[0] == CURL_SOCKET_BAD) - return res; + if(conn->tempsock[0] == CURL_SOCKET_BAD) { + if(!result) + result = CURLE_COULDNT_CONNECT; + return result; + } data->info.numconnects++; /* to track the number of connections made */ @@ -1181,15 +1210,20 @@ curl_socket_t Curl_getconnectinfo(struct SessionHandle *data, DEBUGASSERT(data); - /* this only works for an easy handle that has been used for - curl_easy_perform()! */ - if(data->state.lastconnect && data->multi_easy) { + /* this works for an easy handle: + * - that has been used for curl_easy_perform() + * - that is associated with a multi handle, and whose connection + * was detached with CURLOPT_CONNECT_ONLY + */ + if(data->state.lastconnect && (data->multi_easy || data->multi)) { struct connectdata *c = data->state.lastconnect; struct connfind find; find.tofind = data->state.lastconnect; find.found = FALSE; - Curl_conncache_foreach(data->multi_easy->conn_cache, &find, conn_is_conn); + Curl_conncache_foreach(data->multi_easy? + &data->multi_easy->conn_cache: + &data->multi->conn_cache, &find, conn_is_conn); if(!find.found) { data->state.lastconnect = NULL; @@ -1240,15 +1274,18 @@ int Curl_closesocket(struct connectdata *conn, accept, then we MUST NOT call the callback but clear the accepted status */ conn->sock_accepted[SECONDARYSOCKET] = FALSE; - else + else { + Curl_multi_closed(conn, sock); return conn->fclosesocket(conn->closesocket_client, sock); + } } - sclose(sock); if(conn) /* tell the multi-socket code about this */ Curl_multi_closed(conn, sock); + sclose(sock); + return 0; } @@ -1312,9 +1349,9 @@ CURLcode Curl_socket(struct connectdata *conn, return CURLE_COULDNT_CONNECT; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(conn->scope && (addr->family == AF_INET6)) { + if(conn->scope_id && (addr->family == AF_INET6)) { struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; - sa6->sin6_scope_id = conn->scope; + sa6->sin6_scope_id = conn->scope_id; } #endif @@ -1325,16 +1362,22 @@ CURLcode Curl_socket(struct connectdata *conn, #ifdef CURLDEBUG /* * Curl_conncontrol() is used to set the conn->bits.close bit on or off. It - * MUST be called with the connclose() or connclose() macros with a stated + * MUST be called with the connclose() or connkeep() macros with a stated * reason. The reason is only shown in debug builds but helps to figure out * decision paths when connections are or aren't re-used as expected. */ void Curl_conncontrol(struct connectdata *conn, bool closeit, const char *reason) { - infof(conn->data, "Marked for [%s]: %s\n", closeit?"closure":"keep alive", - reason); - conn->bits.close = closeit; /* the only place in the source code that should - assign this bit */ +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) reason; +#endif + if(closeit != conn->bits.close) { + infof(conn->data, "Marked for [%s]: %s\n", closeit?"closure":"keep alive", + reason); + + conn->bits.close = closeit; /* the only place in the source code that + should assign this bit */ + } } #endif diff --git a/lib/connect.h b/lib/connect.h index 7bc391b..91646c7 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -41,7 +41,7 @@ long Curl_timeleft(struct SessionHandle *data, #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ #define HAPPY_EYEBALLS_TIMEOUT 200 /* milliseconds to wait between - ipv4/ipv6 connection attempts */ + IPv4/IPv6 connection attempts */ /* * Used to extract socket and connectdata struct for the most recent diff --git a/lib/cookie.c b/lib/cookie.c index 375485f..22730cf 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,14 +26,17 @@ RECEIVING COOKIE INFORMATION ============================ -struct CookieInfo *cookie_init(char *file); +struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, + const char *file, struct CookieInfo *inc, bool newsession); Inits a cookie struct to store data in a local file. This is always called before any cookies are set. -int cookies_set(struct CookieInfo *cookie, char *cookie_line); +struct Cookie *Curl_cookie_add(struct SessionHandle *data, + struct CookieInfo *c, bool httpheader, char *lineptr, + const char *domain, const char *path); - The 'cookie_line' parameter is a full "Set-cookie:" line as + The 'lineptr' parameter is a full "Set-cookie:" line as received from a server. The function need to replace previously stored lines that this new @@ -47,8 +50,8 @@ int cookies_set(struct CookieInfo *cookie, char *cookie_line); SENDING COOKIE INFORMATION ========================== -struct Cookies *cookie_getlist(struct CookieInfo *cookie, - char *host, char *path, bool secure); +struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, + char *host, char *path, bool secure); For a given host and path, return a linked list of cookies that the client should send to the server if used now. The secure @@ -81,44 +84,33 @@ Example set of cookies: #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) -#define _MPRINTF_REPLACE -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "urldata.h" #include "cookie.h" #include "strequal.h" #include "strtok.h" #include "sendf.h" #include "slist.h" -#include "curl_memory.h" #include "share.h" #include "strtoofft.h" #include "rawstr.h" #include "curl_memrchr.h" #include "inet_pton.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" static void freecookie(struct Cookie *co) { - if(co->expirestr) - free(co->expirestr); - if(co->domain) - free(co->domain); - if(co->path) - free(co->path); - if(co->spath) - free(co->spath); - if(co->name) - free(co->name); - if(co->value) - free(co->value); - if(co->maxage) - free(co->maxage); - if(co->version) - free(co->version); - + free(co->expirestr); + free(co->domain); + free(co->path); + free(co->spath); + free(co->name); + free(co->value); + free(co->maxage); + free(co->version); free(co); } @@ -233,11 +225,14 @@ static char *sanitize_cookie_path(const char *cookie_path) return NULL; /* some stupid site sends path attribute with '"'. */ + len = strlen(new_path); if(new_path[0] == '\"') { - memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path)); + memmove((void *)new_path, (const void *)(new_path + 1), len); + len--; } - if(new_path[strlen(new_path) - 1] == '\"') { - new_path[strlen(new_path) - 1] = 0x0; + if(len && (new_path[len - 1] == '\"')) { + new_path[len - 1] = 0x0; + len--; } /* RFC6265 5.2.4 The Path Attribute */ @@ -249,8 +244,7 @@ static char *sanitize_cookie_path(const char *cookie_path) } /* convert /hoge/ to /hoge */ - len = strlen(new_path); - if(1 < len && new_path[len - 1] == '/') { + if(len && new_path[len - 1] == '/') { new_path[len - 1] = 0x0; } @@ -259,6 +253,8 @@ static char *sanitize_cookie_path(const char *cookie_path) /* * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). + * + * NOTE: OOM or cookie parsing failures are ignored. */ void Curl_cookie_loadfiles(struct SessionHandle *data) { @@ -266,10 +262,17 @@ void Curl_cookie_loadfiles(struct SessionHandle *data) if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { - data->cookies = Curl_cookie_init(data, - list->data, - data->cookies, - data->set.cookiesession); + struct CookieInfo *newcookies = Curl_cookie_init(data, + list->data, + data->cookies, + data->set.cookiesession); + if(!newcookies) + /* Failure may be due to OOM or a bad cookie; both are ignored + * but only the first should be + */ + infof(data, "ignoring failed cookie_init for %s\n", list->data); + else + data->cookies = newcookies; list = list->next; } curl_slist_free_all(data->change.cookielist); /* clean up list */ @@ -286,8 +289,7 @@ void Curl_cookie_loadfiles(struct SessionHandle *data) */ static void strstore(char **str, const char *newstr) { - if(*str) - free(*str); + free(*str); *str = strdup(newstr); } @@ -351,6 +353,8 @@ static bool isip(const char *domain) * Be aware that sometimes we get an IP-only host name, and that might also be * a numerical IPv6 address. * + * Returns NULL on out of memory or invalid cookie. This is suboptimal, + * as they should be treated separately. ***************************************************************************/ struct Cookie * @@ -405,7 +409,7 @@ Curl_cookie_add(struct SessionHandle *data, do { /* we have a <what>=<this> pair or a stand-alone word here */ name[0]=what[0]=0; /* init the buffers */ - if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%" + if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =] =%" MAX_COOKIE_LINE_TXT "[^;\r\n]", name, what)) { /* Use strstore() below to properly deal with received cookie @@ -820,21 +824,13 @@ Curl_cookie_add(struct SessionHandle *data, /* then free all the old pointers */ free(clist->name); - if(clist->value) - free(clist->value); - if(clist->domain) - free(clist->domain); - if(clist->path) - free(clist->path); - if(clist->spath) - free(clist->spath); - if(clist->expirestr) - free(clist->expirestr); - - if(clist->version) - free(clist->version); - if(clist->maxage) - free(clist->maxage); + free(clist->value); + free(clist->domain); + free(clist->path); + free(clist->spath); + free(clist->expirestr); + free(clist->version); + free(clist->maxage); *clist = *co; /* then store all the new data */ @@ -882,6 +878,7 @@ Curl_cookie_add(struct SessionHandle *data, * * If 'newsession' is TRUE, discard all "session cookies" on read from file. * + * Returns NULL on out of memory. Invalid cookies are ignored. ****************************************************************************/ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, const char *file, @@ -889,8 +886,9 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, bool newsession) { struct CookieInfo *c; - FILE *fp; + FILE *fp = NULL; bool fromfile=TRUE; + char *line = NULL; if(NULL == inc) { /* we didn't get a struct, create one */ @@ -898,6 +896,8 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, if(!c) return NULL; /* failed to get memory */ c->filename = strdup(file?file:"none"); /* copy the name just in case */ + if(!c->filename) + goto fail; /* failed to get memory */ } else { /* we got an already existing one, use that */ @@ -914,7 +914,7 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, fp = NULL; } else - fp = file?fopen(file, "r"):NULL; + fp = file?fopen(file, FOPEN_READTEXT):NULL; c->newsession = newsession; /* new session? */ @@ -922,25 +922,26 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, char *lineptr; bool headerline; - char *line = malloc(MAX_COOKIE_LINE); - if(line) { - while(fgets(line, MAX_COOKIE_LINE, fp)) { - if(checkprefix("Set-Cookie:", line)) { - /* This is a cookie line, get it! */ - lineptr=&line[11]; - headerline=TRUE; - } - else { - lineptr=line; - headerline=FALSE; - } - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - - Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL); + line = malloc(MAX_COOKIE_LINE); + if(!line) + goto fail; + while(fgets(line, MAX_COOKIE_LINE, fp)) { + if(checkprefix("Set-Cookie:", line)) { + /* This is a cookie line, get it! */ + lineptr=&line[11]; + headerline=TRUE; + } + else { + lineptr=line; + headerline=FALSE; } - free(line); /* free the line buffer */ + while(*lineptr && ISBLANK(*lineptr)) + lineptr++; + + Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL); } + free(line); /* free the line buffer */ + if(fromfile) fclose(fp); } @@ -948,6 +949,16 @@ struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, c->running = TRUE; /* now, we're running */ return c; + +fail: + free(line); + if(!inc) + /* Only clean up if we allocated it here, as the original could still be in + * use by a share handle */ + Curl_cookie_cleanup(c); + if(fromfile && fp) + fclose(fp); + return NULL; /* out of memory */ } /* sort this so that the longest path gets before the shorter path */ @@ -1127,16 +1138,14 @@ void Curl_cookie_clearall(struct CookieInfo *cookies) void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo) { struct Cookie *next; - if(co) { - while(co) { - next = co->next; - if(cookiestoo) - freecookie(co); - else - free(co); /* we only free the struct since the "members" are all just - pointed out in the main cookie list! */ - co = next; - } + while(co) { + next = co->next; + if(cookiestoo) + freecookie(co); + else + free(co); /* we only free the struct since the "members" are all just + pointed out in the main cookie list! */ + co = next; } } @@ -1183,23 +1192,14 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies) * * Curl_cookie_cleanup() * - * Free a "cookie object" previous created with cookie_init(). + * Free a "cookie object" previous created with Curl_cookie_init(). * ****************************************************************************/ void Curl_cookie_cleanup(struct CookieInfo *c) { - struct Cookie *co; - struct Cookie *next; if(c) { - if(c->filename) - free(c->filename); - co = c->cookies; - - while(co) { - next = co->next; - freecookie(co); - co = next; - } + free(c->filename); + Curl_cookie_freelist(c->cookies, TRUE); free(c); /* free the base struct as well */ } } @@ -1262,7 +1262,7 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) use_stdout=TRUE; } else { - out = fopen(dumphere, "w"); + out = fopen(dumphere, FOPEN_WRITETEXT); if(!out) return 1; /* failure */ } @@ -1274,9 +1274,10 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) "# http://curl.haxx.se/docs/http-cookies.html\n" "# This file was generated by libcurl! Edit at your own risk.\n\n", out); - co = c->cookies; - while(co) { + for(co = c->cookies; co; co = co->next) { + if(!co->domain) + continue; format_ptr = get_netscape_format(co); if(format_ptr == NULL) { fprintf(out, "#\n# Fatal libcurl error\n"); @@ -1286,7 +1287,6 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) } fprintf(out, "%s\n", format_ptr); free(format_ptr); - co=co->next; } } @@ -1307,10 +1307,9 @@ struct curl_slist *Curl_cookie_list(struct SessionHandle *data) (data->cookies->numcookies == 0)) return NULL; - c = data->cookies->cookies; - - while(c) { - /* fill the list with _all_ the cookies we know */ + for(c = data->cookies->cookies; c; c = c->next) { + if(!c->domain) + continue; line = get_netscape_format(c); if(!line) { curl_slist_free_all(list); @@ -1323,7 +1322,6 @@ struct curl_slist *Curl_cookie_list(struct SessionHandle *data) return NULL; } list = beg; - c = c->next; } return list; diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 10652c6..6627a6b 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,6 +33,9 @@ #ifdef HAVE_ARPA_INET_H # include <arpa/inet.h> #endif +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif #ifdef __VMS # include <in.h> @@ -47,15 +50,12 @@ #include "curl_addrinfo.h" #include "inet_pton.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" + /* The last #include file should be: */ #include "memdebug.h" - /* * Curl_freeaddrinfo() * @@ -80,13 +80,8 @@ Curl_freeaddrinfo(Curl_addrinfo *cahead) Curl_addrinfo *ca; for(ca = cahead; ca != NULL; ca = canext) { - - if(ca->ai_addr) - free(ca->ai_addr); - - if(ca->ai_canonname) - free(ca->ai_canonname); - + free(ca->ai_addr); + free(ca->ai_canonname); canext = ca->ai_next; free(ca); @@ -354,7 +349,7 @@ Curl_he2ai(const struct hostent *he, int port) prevai = ai; } - if(result != CURLE_OK) { + if(result) { Curl_freeaddrinfo(firstai); firstai = NULL; } @@ -477,6 +472,42 @@ Curl_addrinfo *Curl_str2addr(char *address, int port) return NULL; /* bad input format */ } +#ifdef USE_UNIX_SOCKETS +/** + * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo + * struct initialized with this path. + */ +Curl_addrinfo *Curl_unix2addr(const char *path) +{ + Curl_addrinfo *ai; + struct sockaddr_un *sa_un; + size_t path_len; + + ai = calloc(1, sizeof(Curl_addrinfo)); + if(!ai) + return NULL; + if((ai->ai_addr = calloc(1, sizeof(struct sockaddr_un))) == NULL) { + free(ai); + return NULL; + } + /* sun_path must be able to store the NUL-terminated path */ + path_len = strlen(path); + if(path_len >= sizeof(sa_un->sun_path)) { + free(ai->ai_addr); + free(ai); + return NULL; + } + + ai->ai_family = AF_UNIX; + ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */ + ai->ai_addrlen = (curl_socklen_t) sizeof(struct sockaddr_un); + sa_un = (void *) ai->ai_addr; + sa_un->sun_family = AF_UNIX; + memcpy(sa_un->sun_path, path, path_len + 1); /* copy NUL byte */ + return ai; +} +#endif + #if defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) /* * curl_dofreeaddrinfo() diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index 6d2b753..4ef8827 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -79,6 +79,10 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port); Curl_addrinfo *Curl_str2addr(char *dotted, int port); +#ifdef USE_UNIX_SOCKETS +Curl_addrinfo *Curl_unix2addr(const char *path); +#endif + #if defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) void curl_dofreeaddrinfo(struct addrinfo *freethis, diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index 32bae39..5376aa7 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -53,7 +53,7 @@ #endif /* Use Windows LDAP implementation */ -#cmakedefine CURL_LDAP_WIN 1 +#cmakedefine USE_WIN32_LDAP 1 /* when not building a shared library */ #cmakedefine CURL_STATICLIB 1 @@ -467,6 +467,9 @@ /* Define to 1 if you have a working POSIX-style strerror_r function. */ #cmakedefine HAVE_POSIX_STRERROR_R 1 +/* Define to 1 if you have the <pthread.h> header file */ +#cmakedefine HAVE_PTHREAD_H 1 + /* Define to 1 if you have the <pwd.h> header file. */ #cmakedefine HAVE_PWD_H 1 @@ -879,6 +882,9 @@ /* Define if you want to enable c-ares support */ #cmakedefine USE_ARES 1 +/* Define if you want to enable POSIX threaded DNS lookup */ +#cmakedefine USE_THREADS_POSIX 1 + /* Define to disable non-blocking sockets. */ #cmakedefine USE_BLOCKING_SOCKETS 1 @@ -903,8 +909,8 @@ /* if OpenSSL is in use */ #cmakedefine USE_OPENSSL 1 -/* if SSL is enabled */ -#cmakedefine USE_SSLEAY 1 +/* if Unix domain sockets are enabled */ +#cmakedefine USE_UNIX_SOCKETS /* Define to 1 if you are building a Windows target without large file support. */ diff --git a/lib/curl_des.c b/lib/curl_des.c new file mode 100644 index 0000000..42c1df9 --- /dev/null +++ b/lib/curl_des.c @@ -0,0 +1,63 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2015, Steve Holme, <steve_holme@hotmail.com>. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.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_NTLM) && (!defined(USE_OPENSSL) || defined(HAVE_BORINGSSL)) + +#include "curl_des.h" + +/* + * Curl_des_set_odd_parity() + * + * This is used to apply odd parity to the given byte array. It is typically + * used by when a cryptography engines doesn't have it's own version. + * + * The function is a port of the Java based oddParity() function over at: + * + * http://davenport.sourceforge.net/ntlm.html + * + * Parameters: + * + * bytes [in/out] - The data whose parity bits are to be adjusted for + * odd parity. + * len [out] - The length of the data. + */ +void Curl_des_set_odd_parity(unsigned char *bytes, size_t len) +{ + size_t i; + + for(i = 0; i < len; i++) { + unsigned char b = bytes[i]; + + bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ + (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ + (b >> 1)) & 0x01) == 0; + + if(needs_parity) + bytes[i] |= 0x01; + else + bytes[i] &= 0xfe; + } +} + +#endif /* USE_NTLM && (!USE_OPENSSL || HAVE_BORINGSSL) */ diff --git a/lib/bundles.h b/lib/curl_des.h index 3816c40..b855db4 100644 --- a/lib/bundles.h +++ b/lib/curl_des.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_BUNDLES_H -#define HEADER_CURL_BUNDLES_H +#ifndef HEADER_CURL_DES_H +#define HEADER_CURL_DES_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se> + * Copyright (C) 2015, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,24 +22,13 @@ * ***************************************************************************/ -struct connectbundle { - bool server_supports_pipelining; /* TRUE if server supports pipelining, - set after first response */ - size_t num_connections; /* Number of connections in the bundle */ - struct curl_llist *conn_list; /* The connectdata members of the bundle */ -}; +#include "curl_setup.h" -CURLcode Curl_bundle_create(struct SessionHandle *data, - struct connectbundle **cb_ptr); +#if defined(USE_NTLM) && (!defined(USE_OPENSSL) || defined(HAVE_BORINGSSL)) -void Curl_bundle_destroy(struct connectbundle *cb_ptr); +/* Applies odd parity to the given byte array */ +void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); -CURLcode Curl_bundle_add_conn(struct connectbundle *cb_ptr, - struct connectdata *conn); - -int Curl_bundle_remove_conn(struct connectbundle *cb_ptr, - struct connectdata *conn); - - -#endif /* HEADER_CURL_BUNDLES_H */ +#endif /* USE_NTLM && (!USE_OPENSSL || HAVE_BORINGSSL) */ +#endif /* HEADER_CURL_DES_H */ diff --git a/lib/curl_endian.c b/lib/curl_endian.c new file mode 100644 index 0000000..bcd66ed --- /dev/null +++ b/lib/curl_endian.c @@ -0,0 +1,236 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.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 "curl_endian.h" + +/* + * Curl_read16_le() + * + * This function converts a 16-bit integer from the little endian format, as + * used in the incoming package to whatever endian format we're using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 2 byte buffer. + * + * Returns the integer. + */ +unsigned short Curl_read16_le(unsigned char *buf) +{ + return (unsigned short)(((unsigned short)buf[0]) | + ((unsigned short)buf[1] << 8)); +} + +/* + * Curl_read32_le() + * + * This function converts a 32-bit integer from the little endian format, as + * used in the incoming package to whatever endian format we're using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 4 byte buffer. + * + * Returns the integer. + */ +unsigned int Curl_read32_le(unsigned char *buf) +{ + return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) | + ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24); +} + +#if (CURL_SIZEOF_CURL_OFF_T > 4) +/* + * Curl_read64_le() + * + * This function converts a 64-bit integer from the little endian format, as + * used in the incoming package to whatever endian format we're using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 8 byte buffer. + * + * Returns the integer. + */ +#if defined(HAVE_LONGLONG) +unsigned long long Curl_read64_le(unsigned char *buf) +{ + return ((unsigned long long)buf[0]) | + ((unsigned long long)buf[1] << 8) | + ((unsigned long long)buf[2] << 16) | + ((unsigned long long)buf[3] << 24) | + ((unsigned long long)buf[4] << 32) | + ((unsigned long long)buf[5] << 40) | + ((unsigned long long)buf[6] << 48) | + ((unsigned long long)buf[7] << 56); +} +#else +unsigned __int64 Curl_read64_le(unsigned char *buf) +{ + return ((unsigned __int64)buf[0]) | ((unsigned __int64)buf[1] << 8) | + ((unsigned __int64)buf[2] << 16) | ((unsigned __int64)buf[3] << 24) | + ((unsigned __int64)buf[4] << 32) | ((unsigned __int64)buf[5] << 40) | + ((unsigned __int64)buf[6] << 48) | ((unsigned __int64)buf[7] << 56); +} +#endif + +#endif /* CURL_SIZEOF_CURL_OFF_T > 4 */ + +/* + * Curl_read16_be() + * + * This function converts a 16-bit integer from the big endian format, as + * used in the incoming package to whatever endian format we're using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 2 byte buffer. + * + * Returns the integer. + */ +unsigned short Curl_read16_be(unsigned char *buf) +{ + return (unsigned short)(((unsigned short)buf[0] << 8) | + ((unsigned short)buf[1])); +} + +/* + * Curl_read32_be() + * + * This function converts a 32-bit integer from the big endian format, as + * used in the incoming package to whatever endian format we're using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 4 byte buffer. + * + * Returns the integer. + */ +unsigned int Curl_read32_be(unsigned char *buf) +{ + return ((unsigned int)buf[0] << 24) | ((unsigned int)buf[1] << 16) | + ((unsigned int)buf[2] << 8) | ((unsigned int)buf[3]); +} + +#if (CURL_SIZEOF_CURL_OFF_T > 4) +/* + * Curl_read64_be() + * + * This function converts a 64-bit integer from the big endian format, as + * used in the incoming package to whatever endian format we're using + * natively. + * + * Parameters: + * + * buf [in] - A pointer to a 8 byte buffer. + * + * Returns the integer. + */ +#if defined(HAVE_LONGLONG) +unsigned long long Curl_read64_be(unsigned char *buf) +{ + return ((unsigned long long)buf[0] << 56) | + ((unsigned long long)buf[1] << 48) | + ((unsigned long long)buf[2] << 40) | + ((unsigned long long)buf[3] << 32) | + ((unsigned long long)buf[4] << 24) | + ((unsigned long long)buf[5] << 16) | + ((unsigned long long)buf[6] << 8) | + ((unsigned long long)buf[7]); +} +#else +unsigned __int64 Curl_read64_be(unsigned char *buf) +{ + return ((unsigned __int64)buf[0] << 56) | ((unsigned __int64)buf[1] << 48) | + ((unsigned __int64)buf[2] << 40) | ((unsigned __int64)buf[3] << 32) | + ((unsigned __int64)buf[4] << 24) | ((unsigned __int64)buf[5] << 16) | + ((unsigned __int64)buf[6] << 8) | ((unsigned __int64)buf[7]); +} +#endif + +#endif /* CURL_SIZEOF_CURL_OFF_T > 4 */ + +/* + * Curl_write16_le() + * + * This function converts a 16-bit integer from the native endian format, + * to little endian format ready for sending down the wire. + * + * Parameters: + * + * value [in] - The 16-bit integer value. + * buffer [in] - A pointer to the output buffer. + */ +void Curl_write16_le(const short value, unsigned char *buffer) +{ + buffer[0] = (char)(value & 0x00FF); + buffer[1] = (char)((value & 0xFF00) >> 8); +} + +/* + * Curl_write32_le() + * + * This function converts a 32-bit integer from the native endian format, + * to little endian format ready for sending down the wire. + * + * Parameters: + * + * value [in] - The 32-bit integer value. + * buffer [in] - A pointer to the output buffer. + */ +void Curl_write32_le(const int value, unsigned char *buffer) +{ + buffer[0] = (char)(value & 0x000000FF); + buffer[1] = (char)((value & 0x0000FF00) >> 8); + buffer[2] = (char)((value & 0x00FF0000) >> 16); + buffer[3] = (char)((value & 0xFF000000) >> 24); +} + +#if (CURL_SIZEOF_CURL_OFF_T > 4) +/* + * Curl_write64_le() + * + * This function converts a 64-bit integer from the native endian format, + * to little endian format ready for sending down the wire. + * + * Parameters: + * + * value [in] - The 64-bit integer value. + * buffer [in] - A pointer to the output buffer. + */ +#if defined(HAVE_LONGLONG) +void Curl_write64_le(const long long value, unsigned char *buffer) +#else +void Curl_write64_le(const __int64 value, unsigned char *buffer) +#endif +{ + Curl_write32_le((int)value, buffer); + Curl_write32_le((int)(value >> 32), buffer + 4); +} +#endif /* CURL_SIZEOF_CURL_OFF_T > 4 */ diff --git a/lib/curl_endian.h b/lib/curl_endian.h new file mode 100644 index 0000000..e384279 --- /dev/null +++ b/lib/curl_endian.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_ENDIAN_H +#define HEADER_CURL_ENDIAN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.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. + * + ***************************************************************************/ + +/* Converts a 16-bit integer from little endian */ +unsigned short Curl_read16_le(unsigned char *buf); + +/* Converts a 32-bit integer from little endian */ +unsigned int Curl_read32_le(unsigned char *buf); + +#if (CURL_SIZEOF_CURL_OFF_T > 4) +/* Converts a 64-bit integer from little endian */ +#if defined(HAVE_LONGLONG) +unsigned long long Curl_read64_le(unsigned char *buf); +#else +unsigned __int64 Curl_read64_le(unsigned char *buf); +#endif +#endif + +/* Converts a 16-bit integer from big endian */ +unsigned short Curl_read16_be(unsigned char *buf); + +/* Converts a 32-bit integer from big endian */ +unsigned int Curl_read32_be(unsigned char *buf); + +#if (CURL_SIZEOF_CURL_OFF_T > 4) +/* Converts a 64-bit integer from big endian */ +#if defined(HAVE_LONGLONG) +unsigned long long Curl_read64_be(unsigned char *buf); +#else +unsigned __int64 Curl_read64_be(unsigned char *buf); +#endif +#endif + +/* Converts a 16-bit integer to little endian */ +void Curl_write16_le(const short value, unsigned char *buffer); + +/* Converts a 32-bit integer to little endian */ +void Curl_write32_le(const int value, unsigned char *buffer); + +#if (CURL_SIZEOF_CURL_OFF_T > 4) +/* Converts a 64-bit integer to little endian */ +#if defined(HAVE_LONGLONG) +void Curl_write64_le(const long long value, unsigned char *buffer); +#else +void Curl_write64_le(const __int64 value, unsigned char *buffer); +#endif +#endif + +#endif /* HEADER_CURL_ENDIAN_H */ diff --git a/lib/curl_fnmatch.c b/lib/curl_fnmatch.c index 63f67b9..1e53918 100644 --- a/lib/curl_fnmatch.c +++ b/lib/curl_fnmatch.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,11 +23,8 @@ #include "curl_setup.h" #include "curl_fnmatch.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" + /* The last #include file should be: */ #include "memdebug.h" diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 232b3ef..9baece5 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,9 +27,9 @@ #include "curl_gssapi.h" #include "sendf.h" -static const char spnego_oid_bytes[] = "\x2b\x06\x01\x05\x05\x02"; +static char spnego_oid_bytes[] = "\x2b\x06\x01\x05\x05\x02"; gss_OID_desc Curl_spnego_mech_oid = { 6, &spnego_oid_bytes }; -static const char krb5_oid_bytes[] = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"; +static char krb5_oid_bytes[] = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"; gss_OID_desc Curl_krb5_mech_oid = { 9, &krb5_oid_bytes }; OM_uint32 Curl_gss_init_sec_context( @@ -41,9 +41,13 @@ OM_uint32 Curl_gss_init_sec_context( gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_buffer_t output_token, + const bool mutual_auth, OM_uint32 *ret_flags) { - OM_uint32 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; + OM_uint32 req_flags = GSS_C_REPLAY_FLAG; + + if(mutual_auth) + req_flags |= GSS_C_MUTUAL_FLAG; if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) { #ifdef GSS_C_DELEG_POLICY_FLAG @@ -72,4 +76,45 @@ OM_uint32 Curl_gss_init_sec_context( NULL /* time_rec */); } +/* + * Curl_gss_log_error() + * + * This is used to log a GSS-API error status. + * + * Parameters: + * + * data [in] - The session handle. + * status [in] - The status code. + * prefix [in] - The prefix of the log message. + */ +void Curl_gss_log_error(struct SessionHandle *data, OM_uint32 status, + const char *prefix) +{ + OM_uint32 maj_stat; + OM_uint32 min_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string; + char buf[1024]; + size_t len; + + snprintf(buf, sizeof(buf), "%s", prefix); + len = strlen(buf); + do { + maj_stat = gss_display_status(&min_stat, + status, + GSS_C_MECH_CODE, + GSS_C_NO_OID, + &msg_ctx, + &status_string); + if(sizeof(buf) > len + status_string.length + 1) { + snprintf(buf + len, sizeof(buf) - len, + ": %s", (char*)status_string.value); + len += status_string.length; + } + gss_release_buffer(&min_stat, &status_string); + } while(!GSS_ERROR(maj_stat) && msg_ctx != 0); + + infof(data, "%s\n", buf); +} + #endif /* HAVE_GSSAPI */ diff --git a/lib/curl_gssapi.h b/lib/curl_gssapi.h index b91bd7e..19aab64 100644 --- a/lib/curl_gssapi.h +++ b/lib/curl_gssapi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -43,7 +43,6 @@ extern gss_OID_desc Curl_spnego_mech_oid; extern gss_OID_desc Curl_krb5_mech_oid; /* Common method for using GSS-API */ - OM_uint32 Curl_gss_init_sec_context( struct SessionHandle *data, OM_uint32 *minor_status, @@ -53,8 +52,24 @@ OM_uint32 Curl_gss_init_sec_context( gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_buffer_t output_token, + const bool mutual_auth, OM_uint32 *ret_flags); +/* Helper to log a GSS-API error status */ +void Curl_gss_log_error(struct SessionHandle *data, OM_uint32 status, + const char *prefix); + +/* Provide some definitions missing in old headers */ +#ifdef HAVE_OLD_GSSMIT +#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +#define NCOMPAT 1 +#endif + +/* Define our privacy and integrity protection values */ +#define GSSAUTH_P_NONE 1 +#define GSSAUTH_P_INTEGRITY 2 +#define GSSAUTH_P_PRIVACY 4 + #endif /* HAVE_GSSAPI */ #endif /* HEADER_CURL_GSSAPI_H */ diff --git a/lib/curl_md4.h b/lib/curl_md4.h index b0be9cf..13c7903 100644 --- a/lib/curl_md4.h +++ b/lib/curl_md4.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,10 +24,12 @@ #include "curl_setup.h" -/* NSS crypto library does not provide the MD4 hash algorithm, so that we have - * a local implementation of it */ -#ifdef USE_NSS +/* NSS and OS/400 crypto library do not provide the MD4 hash algorithm, so + * that we have a local implementation of it */ +#if defined(USE_NSS) || defined(USE_OS400CRYPTO) + void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len); -#endif /* USE_NSS */ + +#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) */ #endif /* HEADER_CURL_MD4_H */ diff --git a/lib/curl_memory.h b/lib/curl_memory.h index e3cdc72..bc744cc 100644 --- a/lib/curl_memory.h +++ b/lib/curl_memory.h @@ -28,6 +28,9 @@ * File curl_memory.h must be included by _all_ *.c source files * that use memory related functions strdup, malloc, calloc, realloc * or free, and given source file is used to build libcurl library. + * It should be included immediately before memdebug.h as the last files + * included to avoid undesired interaction with other memory function + * headers in dependent libraries. * * There is nearly no exception to above rule. All libcurl source * files in 'lib' subdirectory as well as those living deep inside diff --git a/lib/curl_memrchr.c b/lib/curl_memrchr.c index a71c2bb..6722c6a 100644 --- a/lib/curl_memrchr.c +++ b/lib/curl_memrchr.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -21,13 +21,9 @@ ***************************************************************************/ #include "curl_setup.h" - #include "curl_memrchr.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" + /* The last #include file should be: */ #include "memdebug.h" diff --git a/lib/curl_multibyte.c b/lib/curl_multibyte.c index 6c02239..403d005 100644 --- a/lib/curl_multibyte.c +++ b/lib/curl_multibyte.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,18 +22,16 @@ #include "curl_setup.h" -#if defined(USE_WIN32_IDN) || (defined(USE_WINDOWS_SSPI) && defined(UNICODE)) +#if defined(USE_WIN32_IDN) || ((defined(USE_WINDOWS_SSPI) || \ + defined(USE_WIN32_LDAP)) && defined(UNICODE)) /* * MultiByte conversions using Windows kernel32 library. */ #include "curl_multibyte.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" + /* The last #include file should be: */ #include "memdebug.h" @@ -49,7 +47,8 @@ wchar_t *Curl_convert_UTF8_to_wchar(const char *str_utf8) if(str_w) { if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w, str_w_len) == 0) { - Curl_safefree(str_w); + free(str_w); + return NULL; } } } @@ -70,7 +69,8 @@ char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w) if(str_utf8) { if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, str_utf8_len, NULL, FALSE) == 0) { - Curl_safefree(str_utf8); + free(str_utf8); + return NULL; } } } @@ -79,4 +79,4 @@ char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w) return str_utf8; } -#endif /* USE_WIN32_IDN || (USE_WINDOWS_SSPI && UNICODE) */ +#endif /* USE_WIN32_IDN || ((USE_WINDOWS_SSPI || USE_WIN32_LDAP) && UNICODE) */ diff --git a/lib/curl_multibyte.h b/lib/curl_multibyte.h index 7ee5eae..dc7ed4c 100644 --- a/lib/curl_multibyte.h +++ b/lib/curl_multibyte.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,7 +23,8 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(USE_WIN32_IDN) || (defined(USE_WINDOWS_SSPI) && defined(UNICODE)) +#if defined(USE_WIN32_IDN) || ((defined(USE_WINDOWS_SSPI) || \ + defined(USE_WIN32_LDAP)) && defined(UNICODE)) /* * MultiByte conversions using Windows kernel32 library. @@ -32,10 +33,11 @@ wchar_t *Curl_convert_UTF8_to_wchar(const char *str_utf8); char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w); -#endif /* USE_WIN32_IDN || (USE_WINDOWS_SSPI && UNICODE) */ +#endif /* USE_WIN32_IDN || ((USE_WINDOWS_SSPI || USE_WIN32_LDAP) && UNICODE) */ -#if defined(USE_WIN32_IDN) || defined(USE_WINDOWS_SSPI) +#if defined(USE_WIN32_IDN) || defined(USE_WINDOWS_SSPI) || \ + defined(USE_WIN32_LDAP) /* * Macros Curl_convert_UTF8_to_tchar(), Curl_convert_tchar_to_UTF8() @@ -85,6 +87,6 @@ typedef union { #endif /* UNICODE */ -#endif /* USE_WIN32_IDN || USE_WINDOWS_SSPI */ +#endif /* USE_WIN32_IDN || USE_WINDOWS_SSPI || USE_WIN32_LDAP */ #endif /* HEADER_CURL_MULTIBYTE_H */ diff --git a/lib/curl_ntlm.c b/lib/curl_ntlm.c index 8c02aba..f9ddf50 100644 --- a/lib/curl_ntlm.c +++ b/lib/curl_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@ #include "curl_setup.h" -#ifdef USE_NTLM +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) /* * NTLM details: @@ -39,11 +39,9 @@ #include "curl_ntlm.h" #include "curl_ntlm_msgs.h" #include "curl_ntlm_wb.h" +#include "curl_sasl.h" #include "url.h" -#include "curl_memory.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #if defined(USE_NSS) #include "vtls/nssg.h" @@ -51,7 +49,8 @@ #include "curl_sspi.h" #endif -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #if DEBUG_ME @@ -69,12 +68,6 @@ CURLcode Curl_input_ntlm(struct connectdata *conn, struct ntlmdata *ntlm; CURLcode result = CURLE_OK; -#ifdef USE_NSS - result = Curl_nss_force_init(conn->data); - if(result) - return result; -#endif - ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; if(checkprefix("NTLM", header)) { @@ -84,14 +77,18 @@ CURLcode Curl_input_ntlm(struct connectdata *conn, header++; if(*header) { - result = Curl_ntlm_decode_type2_message(conn->data, header, ntlm); - if(CURLE_OK != result) + result = Curl_sasl_decode_ntlm_type2_message(conn->data, header, ntlm); + if(result) return result; ntlm->state = NTLMSTATE_TYPE2; /* We got a type-2 message */ } else { - if(ntlm->state == NTLMSTATE_TYPE3) { + if(ntlm->state == NTLMSTATE_LAST) { + infof(conn->data, "NTLM auth restarted\n"); + Curl_http_ntlm_cleanup(conn); + } + else if(ntlm->state == NTLMSTATE_TYPE3) { infof(conn->data, "NTLM handshake rejected\n"); Curl_http_ntlm_cleanup(conn); ntlm->state = NTLMSTATE_NONE; @@ -112,12 +109,11 @@ CURLcode Curl_input_ntlm(struct connectdata *conn, /* * This is for creating ntlm header output */ -CURLcode Curl_output_ntlm(struct connectdata *conn, - bool proxy) +CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy) { char *base64 = NULL; size_t len = 0; - CURLcode error; + CURLcode result; /* point to the address of the pointer that holds the string to send to the server, which is for a plain host or for a HTTP proxy */ @@ -175,38 +171,40 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ /* Create a type-1 message */ - error = Curl_ntlm_create_type1_message(userp, passwdp, ntlm, &base64, - &len); - if(error) - return error; + result = Curl_sasl_create_ntlm_type1_message(userp, passwdp, ntlm, &base64, + &len); + if(result) + return result; if(base64) { - Curl_safefree(*allocuserpwd); + free(*allocuserpwd); *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", base64); free(base64); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; + DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); } break; case NTLMSTATE_TYPE2: /* We already received the type-2 message, create a type-3 message */ - error = Curl_ntlm_create_type3_message(conn->data, userp, passwdp, - ntlm, &base64, &len); - if(error) - return error; + result = Curl_sasl_create_ntlm_type3_message(conn->data, userp, passwdp, + ntlm, &base64, &len); + if(result) + return result; if(base64) { - Curl_safefree(*allocuserpwd); + free(*allocuserpwd); *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", base64); free(base64); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; + DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); ntlm->state = NTLMSTATE_TYPE3; /* we send a type-3 */ @@ -217,6 +215,9 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, case NTLMSTATE_TYPE3: /* connection is already authenticated, * don't send a header in future requests */ + ntlm->state = NTLMSTATE_LAST; + + case NTLMSTATE_LAST: Curl_safefree(*allocuserpwd); authp->done = TRUE; break; @@ -227,22 +228,12 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, void Curl_http_ntlm_cleanup(struct connectdata *conn) { -#ifdef USE_WINDOWS_SSPI - Curl_ntlm_sspi_cleanup(&conn->ntlm); - Curl_ntlm_sspi_cleanup(&conn->proxyntlm); -#elif defined(NTLM_WB_ENABLED) - Curl_ntlm_wb_cleanup(conn); -#else - (void)conn; -#endif + Curl_sasl_ntlm_cleanup(&conn->ntlm); + Curl_sasl_ntlm_cleanup(&conn->proxyntlm); -#ifndef USE_WINDOWS_SSPI - Curl_safefree(conn->ntlm.target_info); - conn->ntlm.target_info_len = 0; - - Curl_safefree(conn->proxyntlm.target_info); - conn->proxyntlm.target_info_len = 0; +#if defined(NTLM_WB_ENABLED) + Curl_ntlm_wb_cleanup(conn); #endif } -#endif /* USE_NTLM */ +#endif /* !CURL_DISABLE_HTTP && USE_NTLM */ diff --git a/lib/curl_ntlm.h b/lib/curl_ntlm.h index 21a9e9e..947eac2 100644 --- a/lib/curl_ntlm.h +++ b/lib/curl_ntlm.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,7 +24,7 @@ #include "curl_setup.h" -#ifdef USE_NTLM +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) /* this is for ntlm header input */ CURLcode Curl_input_ntlm(struct connectdata *conn, bool proxy, @@ -35,10 +35,6 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy); void Curl_http_ntlm_cleanup(struct connectdata *conn); -#else - -#define Curl_http_ntlm_cleanup(a) Curl_nop_stmt - -#endif +#endif /* !CURL_DISABLE_HTTP && USE_NTLM */ #endif /* HEADER_CURL_NTLM_H */ diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index b011626..2e5b573 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) +#if defined(USE_NTLM) /* * NTLM details: @@ -31,7 +31,9 @@ * http://www.innovation.ch/java/ntlm.html */ -#ifdef USE_SSLEAY +#if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO) + +#ifdef USE_OPENSSL # ifdef USE_OPENSSL # include <openssl/des.h> @@ -87,6 +89,11 @@ # include <CommonCrypto/CommonCryptor.h> # include <CommonCrypto/CommonDigest.h> +#elif defined(USE_OS400CRYPTO) +# include "cipher.mih" /* mih/cipher */ +# include "curl_md4.h" +#elif defined(USE_WIN32_CRYPTO) +# include <wincrypt.h> #else # error "Can't compile NTLM support without a crypto library." #endif @@ -94,32 +101,27 @@ #include "urldata.h" #include "non-ascii.h" #include "rawstr.h" -#include "curl_memory.h" #include "curl_ntlm_core.h" #include "curl_md5.h" #include "curl_hmac.h" #include "warnless.h" +#include "curl_endian.h" +#include "curl_des.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #define NTLM_HMAC_MD5_LEN (16) #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" #define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) -#ifdef USE_SSLEAY /* - * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The - * key schedule ks is also set. - */ -static void setup_des_key(const unsigned char *key_56, - DES_key_schedule DESKEYARG(ks)) +* Turns a 56-bit key into being 64-bit wide. +*/ +static void extend_key_56_to_64(const unsigned char *key_56, char *key) { - DES_cblock key; - key[0] = key_56[0]; key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); @@ -128,36 +130,47 @@ static void setup_des_key(const unsigned char *key_56, key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); - - DES_set_odd_parity(&key); - DES_set_key(&key, ks); } -#else /* defined(USE_SSLEAY) */ - +#ifdef USE_OPENSSL /* - * Turns a 56 bit key into the 64 bit, odd parity key. Used by GnuTLS and NSS. + * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The + * key schedule ks is also set. */ -static void extend_key_56_to_64(const unsigned char *key_56, char *key) +static void setup_des_key(const unsigned char *key_56, + DES_key_schedule DESKEYARG(ks)) { - key[0] = key_56[0]; - key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); - key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); - key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); - key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); - key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); - key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); - key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); + DES_cblock key; + + /* Expand the 56-bit key to 64-bits */ + extend_key_56_to_64(key_56, (char *) key); + + /* Set the key parity to odd */ +#if defined(HAVE_BORINGSSL) + Curl_des_set_odd_parity((unsigned char *) &key, sizeof(key)); +#else + DES_set_odd_parity(&key); +#endif + + /* Set the key */ + DES_set_key(&key, ks); } -#if defined(USE_GNUTLS_NETTLE) +#elif defined(USE_GNUTLS_NETTLE) static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; + + /* Expand the 56-bit key to 64-bits */ extend_key_56_to_64(key_56, key); - des_set_key(des, (const uint8_t*)key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + + /* Set the key */ + des_set_key(des, (const uint8_t *) key); } #elif defined(USE_GNUTLS) @@ -169,8 +182,15 @@ static void setup_des_key(const unsigned char *key_56, gcry_cipher_hd_t *des) { char key[8]; + + /* Expand the 56-bit key to 64-bits */ extend_key_56_to_64(key_56, key); - gcry_cipher_setkey(*des, key, 8); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + + /* Set the key */ + gcry_cipher_setkey(*des, key, sizeof(key)); } #elif defined(USE_NSS) @@ -198,16 +218,21 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, if(!slot) return FALSE; - /* expand the 56 bit key to 64 bit and wrap by NSS */ + /* Expand the 56-bit key to 64-bits */ extend_key_56_to_64(key_56, key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + + /* Import the key */ key_item.data = (unsigned char *)key; - key_item.len = /* hard-wired */ 8; + key_item.len = sizeof(key); symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); if(!symkey) goto fail; - /* create DES encryption context */ + /* Create the DES encryption context */ param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL); if(!param) goto fail; @@ -215,7 +240,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, if(!ctx) goto fail; - /* perform the encryption */ + /* Perform the encryption */ if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8, (unsigned char *)in, /* inbuflen */ 8) && SECSuccess == PK11_Finalize(ctx)) @@ -242,16 +267,95 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, size_t out_len; CCCryptorStatus err; + /* Expand the 56-bit key to 64-bits */ extend_key_56_to_64(key_56, key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + + /* Perform the encryption */ err = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key, kCCKeySizeDES, NULL, in, 8 /* inbuflen */, out, 8 /* outbuflen */, &out_len); + return err == kCCSuccess; } -#endif /* defined(USE_DARWINSSL) */ +#elif defined(USE_OS400CRYPTO) -#endif /* defined(USE_SSLEAY) */ +static bool encrypt_des(const unsigned char *in, unsigned char *out, + const unsigned char *key_56) +{ + char key[8]; + _CIPHER_Control_T ctl; + + /* Setup the cipher control structure */ + ctl.Func_ID = ENCRYPT_ONLY; + ctl.Data_Len = sizeof(key); + + /* Expand the 56-bit key to 64-bits */ + extend_key_56_to_64(key_56, ctl.Crypto_Key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) ctl.Crypto_Key, ctl.Data_Len); + + /* Perform the encryption */ + _CIPHER((_SPCPTR *) &out, &ctl, (_SPCPTR *) &in); + + return TRUE; +} + +#elif defined(USE_WIN32_CRYPTO) + +static bool encrypt_des(const unsigned char *in, unsigned char *out, + const unsigned char *key_56) +{ + HCRYPTPROV hprov; + HCRYPTKEY hkey; + struct { + BLOBHEADER hdr; + unsigned int len; + char key[8]; + } blob; + DWORD len = 8; + + /* Acquire the crypto provider */ + if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return FALSE; + + /* Setup the key blob structure */ + memset(&blob, 0, sizeof(blob)); + blob.hdr.bType = PLAINTEXTKEYBLOB; + blob.hdr.bVersion = 2; + blob.hdr.aiKeyAlg = CALG_DES; + blob.len = sizeof(blob.key); + + /* Expand the 56-bit key to 64-bits */ + extend_key_56_to_64(key_56, blob.key); + + /* Set the key parity to odd */ + Curl_des_set_odd_parity((unsigned char *) blob.key, sizeof(blob.key)); + + /* Import the key */ + if(!CryptImportKey(hprov, (BYTE *) &blob, sizeof(blob), 0, 0, &hkey)) { + CryptReleaseContext(hprov, 0); + + return FALSE; + } + + memcpy(out, in, 8); + + /* Perform the encryption */ + CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); + + CryptDestroyKey(hkey); + CryptReleaseContext(hprov, 0); + + return TRUE; +} + +#endif /* defined(USE_WIN32_CRYPTO) */ /* * takes a 21 byte array and treats it as 3 56-bit DES keys. The @@ -262,7 +366,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) { -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL DES_key_schedule ks; setup_des_key(keys, DESKEY(ks)); @@ -301,7 +405,8 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, setup_des_key(keys + 14, &des); gcry_cipher_encrypt(des, results + 16, 8, plaintext, 8); gcry_cipher_close(des); -#elif defined(USE_NSS) || defined(USE_DARWINSSL) +#elif defined(USE_NSS) || defined(USE_DARWINSSL) || defined(USE_OS400CRYPTO) \ + || defined(USE_WIN32_CRYPTO) encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); encrypt_des(plaintext, results + 16, keys + 14); @@ -311,11 +416,11 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, /* * Set up lanmanager hashed password */ -void Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, - const char *password, - unsigned char *lmbuffer /* 21 bytes */) +CURLcode Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, + const char *password, + unsigned char *lmbuffer /* 21 bytes */) { - CURLcode res; + CURLcode result; unsigned char pw[14]; static const unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ @@ -329,14 +434,14 @@ void Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, * The LanManager hashed password needs to be created using the * password in the network encoding not the host encoding. */ - res = Curl_convert_to_network(data, (char *)pw, 14); - if(res) - return; + result = Curl_convert_to_network(data, (char *)pw, 14); + if(result) + return result; { /* Create LanManager hashed password. */ -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL DES_key_schedule ks; setup_des_key(pw, DESKEY(ks)); @@ -364,13 +469,16 @@ void Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, setup_des_key(pw + 7, &des); gcry_cipher_encrypt(des, lmbuffer + 8, 8, magic, 8); gcry_cipher_close(des); -#elif defined(USE_NSS) || defined(USE_DARWINSSL) +#elif defined(USE_NSS) || defined(USE_DARWINSSL) || defined(USE_OS400CRYPTO) \ + || defined(USE_WIN32_CRYPTO) encrypt_des(magic, lmbuffer, pw); encrypt_des(magic, lmbuffer + 8, pw + 7); #endif memset(lmbuffer + 16, 0, 21 - 16); } + + return CURLE_OK; } #if USE_NTRESPONSES @@ -384,6 +492,8 @@ static void ascii_to_unicode_le(unsigned char *dest, const char *src, } } +#if USE_NTLM_V2 && !defined(USE_WINDOWS_SSPI) + static void ascii_uppercase_to_unicode_le(unsigned char *dest, const char *src, size_t srclen) { @@ -394,26 +504,11 @@ static void ascii_uppercase_to_unicode_le(unsigned char *dest, } } -static void write32_le(const int value, unsigned char *buffer) -{ - buffer[0] = (char)(value & 0x000000FF); - buffer[1] = (char)((value & 0x0000FF00) >> 8); - buffer[2] = (char)((value & 0x00FF0000) >> 16); - buffer[3] = (char)((value & 0xFF000000) >> 24); -} - -#if defined(HAVE_LONGLONG) -static void write64_le(const long long value, unsigned char *buffer) -#else -static void write64_le(const __int64 value, unsigned char *buffer) -#endif -{ - write32_le((int)value, buffer); - write32_le((int)(value >> 32), buffer + 4); -} +#endif /* USE_NTLM_V2 && !USE_WINDOWS_SSPI */ /* * Set up nt hashed passwords + * @unittest: 1600 */ CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, const char *password, @@ -437,7 +532,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, { /* Create NT hashed password. */ -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL MD4_CTX MD4pw; MD4_Init(&MD4pw); MD4_Update(&MD4pw, pw, 2 * len); @@ -453,10 +548,23 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, gcry_md_write(MD4pw, pw, 2 * len); memcpy (ntbuffer, gcry_md_read (MD4pw, 0), MD4_DIGEST_LENGTH); gcry_md_close(MD4pw); -#elif defined(USE_NSS) +#elif defined(USE_NSS) || defined(USE_OS400CRYPTO) Curl_md4it(ntbuffer, pw, 2 * len); #elif defined(USE_DARWINSSL) (void)CC_MD4(pw, (CC_LONG)(2 * len), ntbuffer); +#elif defined(USE_WIN32_CRYPTO) + HCRYPTPROV hprov; + if(CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + HCRYPTHASH hhash; + if(CryptCreateHash(hprov, CALG_MD4, 0, 0, &hhash)) { + DWORD length = 16; + CryptHashData(hhash, pw, (unsigned int)len * 2, 0); + CryptGetHashParam(hhash, HP_HASHVAL, ntbuffer, &length, 0); + CryptDestroyHash(hhash); + } + CryptReleaseContext(hprov, 0); + } #endif memset(ntbuffer + 16, 0, 21 - 16); @@ -467,6 +575,8 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, return CURLE_OK; } +#if USE_NTLM_V2 && !defined(USE_WINDOWS_SSPI) + /* This returns the HMAC MD5 digest */ CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, const unsigned char *data, unsigned int datalen, @@ -497,7 +607,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, /* Unicode representation */ size_t identity_len = (userlen + domlen) * 2; unsigned char *identity = malloc(identity_len); - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; if(!identity) return CURLE_OUT_OF_MEMORY; @@ -505,12 +615,12 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, ascii_uppercase_to_unicode_le(identity, user, userlen); ascii_to_unicode_le(identity + (userlen << 1), domain, domlen); - res = Curl_hmac_md5(ntlmhash, 16, identity, curlx_uztoui(identity_len), - ntlmv2hash); + result = Curl_hmac_md5(ntlmhash, 16, identity, curlx_uztoui(identity_len), + ntlmv2hash); - Curl_safefree(identity); + free(identity); - return res; + return result; } /* @@ -559,7 +669,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, #else __int64 tw; #endif - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; /* Calculate the timestamp */ #ifdef DEBUGBUILD @@ -586,17 +696,17 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, "%c%c%c%c", /* Reserved = 0 */ 0, 0, 0, 0); - write64_le(tw, ptr + 24); + Curl_write64_le(tw, ptr + 24); memcpy(ptr + 32, challenge_client, 8); memcpy(ptr + 44, ntlm->target_info, ntlm->target_info_len); /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */ memcpy(ptr + 8, &ntlm->nonce[0], 8); - res = Curl_hmac_md5(ntlmv2hash, NTLM_HMAC_MD5_LEN, ptr + 8, - NTLMv2_BLOB_LEN + 8, hmac_output); - if(res) { - Curl_safefree(ptr); - return res; + result = Curl_hmac_md5(ntlmv2hash, NTLM_HMAC_MD5_LEN, ptr + 8, + NTLMv2_BLOB_LEN + 8, hmac_output); + if(result) { + free(ptr); + return result; } /* Concatenate the HMAC MD5 output with the BLOB */ @@ -606,7 +716,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, *ntresp = ptr; *ntresp_len = len; - return res; + return result; } /* @@ -630,22 +740,26 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, { unsigned char data[16]; unsigned char hmac_output[16]; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; memcpy(&data[0], challenge_server, 8); memcpy(&data[8], challenge_client, 8); - res = Curl_hmac_md5(ntlmv2hash, 16, &data[0], 16, hmac_output); - if(res) - return res; + result = Curl_hmac_md5(ntlmv2hash, 16, &data[0], 16, hmac_output); + if(result) + return result; /* Concatenate the HMAC MD5 output with the client nonce */ memcpy(lmresp, hmac_output, 16); memcpy(lmresp+16, challenge_client, 8); - return res; + return result; } +#endif /* USE_NTLM_V2 && !USE_WINDOWS_SSPI */ + #endif /* USE_NTRESPONSES */ -#endif /* USE_NTLM && !USE_WINDOWS_SSPI */ +#endif /* !USE_WINDOWS_SSPI || USE_WIN32_CRYPTO */ + +#endif /* USE_NTLM */ diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index fe41cdd..3a76359 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,9 +24,11 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) +#if defined(USE_NTLM) -#ifdef USE_SSLEAY +#if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO) + +#ifdef USE_OPENSSL # if !defined(OPENSSL_VERSION_NUMBER) && \ !defined(HEADER_SSL_H) && !defined(HEADER_MD5_H) # error "curl_ntlm_core.h shall not be included before OpenSSL headers." @@ -34,38 +36,49 @@ # ifdef OPENSSL_NO_MD4 # define USE_NTRESPONSES 0 # define USE_NTLM2SESSION 0 +# define USE_NTLM_V2 0 # endif #endif -/* - * Define USE_NTRESPONSES to 1 in order to make the type-3 message include - * the NT response message. Define USE_NTLM2SESSION to 1 in order to make - * the type-3 message include the NTLM2Session response message, requires - * USE_NTRESPONSES defined to 1. - */ - +/* Define USE_NTRESPONSES to 1 in order to make the type-3 message include + * the NT response message. */ #ifndef USE_NTRESPONSES -# define USE_NTRESPONSES 1 -# define USE_NTLM2SESSION 1 +#define USE_NTRESPONSES 1 +#endif + +/* Define USE_NTLM2SESSION to 1 in order to make the type-3 message include the + NTLM2Session response message, requires USE_NTRESPONSES defined to 1 and a + Crypto engine that we have curl_ssl_md5sum() for. */ +#if !defined(USE_NTLM2SESSION) && USE_NTRESPONSES && !defined(USE_WIN32_CRYPTO) +#define USE_NTLM2SESSION 1 +#endif + +/* Define USE_NTLM_V2 to 1 in order to allow the type-3 message to include the + LMv2 and NTLMv2 response messages, requires USE_NTRESPONSES defined to 1 + and support for 64-bit integers. */ +#if !defined(USE_NTLM_V2) && USE_NTRESPONSES && (CURL_SIZEOF_CURL_OFF_T > 4) +#define USE_NTLM_V2 1 #endif void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results); -void Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, - const char *password, - unsigned char *lmbuffer /* 21 bytes */); +CURLcode Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, + const char *password, + unsigned char *lmbuffer /* 21 bytes */); #if USE_NTRESPONSES -CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, - const unsigned char *data, unsigned int datalen, - unsigned char *output); - CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, const char *password, unsigned char *ntbuffer /* 21 bytes */); +#if USE_NTLM_V2 && !defined(USE_WINDOWS_SSPI) + +CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, + const unsigned char *data, unsigned int datalen, + unsigned char *output); + CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, const char *domain, size_t domlen, unsigned char *ntlmhash, @@ -82,8 +95,12 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, unsigned char *challenge_server, unsigned char *lmresp); -#endif +#endif /* USE_NTLM_V2 && !USE_WINDOWS_SSPI */ + +#endif /* USE_NTRESPONSES */ + +#endif /* !USE_WINDOWS_SSPI || USE_WIN32_CRYPTO */ -#endif /* USE_NTLM && !USE_WINDOWS_SSPI */ +#endif /* USE_NTLM */ #endif /* HEADER_CURL_NTLM_CORE_H */ diff --git a/lib/curl_ntlm_msgs.c b/lib/curl_ntlm_msgs.c index b807926..7f07dec 100644 --- a/lib/curl_ntlm_msgs.c +++ b/lib/curl_ntlm_msgs.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@ #include "curl_setup.h" -#ifdef USE_NTLM +#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) /* * NTLM details: @@ -41,21 +41,21 @@ #include "curl_gethostname.h" #include "curl_multibyte.h" #include "warnless.h" -#include "curl_memory.h" - -#ifdef USE_WINDOWS_SSPI -# include "curl_sspi.h" -#endif #include "vtls/vtls.h" +#ifdef USE_NSS +#include "vtls/nssg.h" /* for Curl_nss_force_init() */ +#endif + #define BUILDING_CURL_NTLM_MSGS_C #include "curl_ntlm_msgs.h" +#include "curl_sasl.h" +#include "curl_endian.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* "NTLMSSP" signature is always in ASCII regardless of the platform */ @@ -147,63 +147,38 @@ static void ntlm_print_hex(FILE *handle, const char *buf, size_t len) # define DEBUG_OUT(x) Curl_nop_stmt #endif -#ifndef USE_WINDOWS_SSPI -/* - * This function converts from the little endian format used in the - * incoming package to whatever endian format we're using natively. - * Argument is a pointer to a 4 byte buffer. - */ -static unsigned int readint_le(unsigned char *buf) -{ - return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) | - ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24); -} - -/* - * This function converts from the little endian format used in the incoming - * package to whatever endian format we're using natively. Argument is a - * pointer to a 2 byte buffer. - */ -static unsigned int readshort_le(unsigned char *buf) -{ - return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8); -} - /* - * Curl_ntlm_decode_type2_target() + * ntlm_decode_type2_target() * * This is used to decode the "target info" in the ntlm type-2 message * received. * * Parameters: * - * data [in] - Pointer to the session handle - * buffer [in] - The decoded base64 ntlm header of Type 2 - * size [in] - The input buffer size, atleast 32 bytes - * ntlm [in] - Pointer to ntlm data struct being used and modified. + * data [in] - The session handle. + * buffer [in] - The decoded type-2 message. + * size [in] - The input buffer size, at least 32 bytes. + * ntlm [in/out] - The ntlm data struct being used and modified. * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_decode_type2_target(struct SessionHandle *data, - unsigned char *buffer, - size_t size, - struct ntlmdata *ntlm) +static CURLcode ntlm_decode_type2_target(struct SessionHandle *data, + unsigned char *buffer, + size_t size, + struct ntlmdata *ntlm) { - unsigned int target_info_len = 0; + unsigned short target_info_len = 0; unsigned int target_info_offset = 0; - Curl_safefree(ntlm->target_info); - ntlm->target_info_len = 0; - if(size >= 48) { - target_info_len = readshort_le(&buffer[40]); - target_info_offset = readint_le(&buffer[44]); + target_info_len = Curl_read16_le(&buffer[40]); + target_info_offset = Curl_read32_le(&buffer[44]); if(target_info_len > 0) { if(((target_info_offset + target_info_len) > size) || (target_info_offset < 48)) { infof(data, "NTLM handshake failure (bad type-2 message). " "Target Info Offset Len is set incorrect by the peer\n"); - return CURLE_REMOTE_ACCESS_DENIED; + return CURLE_BAD_CONTENT_ENCODING; } ntlm->target_info = malloc(target_info_len); @@ -211,17 +186,14 @@ CURLcode Curl_ntlm_decode_type2_target(struct SessionHandle *data, return CURLE_OUT_OF_MEMORY; memcpy(ntlm->target_info, &buffer[target_info_offset], target_info_len); - ntlm->target_info_len = target_info_len; - } - } + ntlm->target_info_len = target_info_len; + return CURLE_OK; } -#endif - /* NTLM message structure notes: @@ -239,29 +211,26 @@ CURLcode Curl_ntlm_decode_type2_target(struct SessionHandle *data, */ /* - * Curl_ntlm_decode_type2_message() + * Curl_sasl_decode_ntlm_type2_message() * - * This is used to decode a ntlm type-2 message received from a HTTP or SASL - * based (such as SMTP, POP3 or IMAP) server. The message is first decoded - * from a base64 string into a raw ntlm message and checked for validity - * before the appropriate data for creating a type-3 message is written to - * the given ntlm data structure. + * This is used to decode an already encoded NTLM type-2 message. The message + * is first decoded from a base64 string into a raw NTLM message and checked + * for validity before the appropriate data for creating a type-3 message is + * written to the given NTLM data structure. * * Parameters: * - * data [in] - Pointer to session handle. - * header [in] - Pointer to the input buffer. - * ntlm [in] - Pointer to ntlm data struct being used and modified. + * data [in] - The session handle. + * type2msg [in] - The base64 encoded type-2 message. + * ntlm [in/out] - The ntlm data struct being used and modified. * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, - const char *header, - struct ntlmdata *ntlm) +CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data, + const char *type2msg, + struct ntlmdata *ntlm) { -#ifndef USE_WINDOWS_SSPI static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; -#endif /* NTLM type-2 message structure: @@ -279,52 +248,52 @@ CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, (*) -> Optional */ - size_t size = 0; - unsigned char *buffer = NULL; - CURLcode error; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_WINDOWS_SSPI) + CURLcode result = CURLE_OK; + unsigned char *type2 = NULL; + size_t type2_len = 0; + +#if defined(USE_NSS) + /* Make sure the crypto backend is initialized */ + result = Curl_nss_force_init(data); + if(result) + return result; +#elif defined(CURL_DISABLE_VERBOSE_STRINGS) (void)data; #endif - error = Curl_base64_decode(header, &buffer, &size); - if(error) - return error; - - if(!buffer) { - infof(data, "NTLM handshake failure (unhandled condition)\n"); - return CURLE_REMOTE_ACCESS_DENIED; + /* Decode the base-64 encoded type-2 message */ + if(strlen(type2msg) && *type2msg != '=') { + result = Curl_base64_decode(type2msg, &type2, &type2_len); + if(result) + return result; } -#ifdef USE_WINDOWS_SSPI - ntlm->type_2 = malloc(size + 1); - if(ntlm->type_2 == NULL) { - free(buffer); - return CURLE_OUT_OF_MEMORY; + /* Ensure we have a valid type-2 message */ + if(!type2) { + infof(data, "NTLM handshake failure (empty type-2 message)\n"); + return CURLE_BAD_CONTENT_ENCODING; } - ntlm->n_type_2 = curlx_uztoul(size); - memcpy(ntlm->type_2, buffer, size); -#else + ntlm->flags = 0; - if((size < 32) || - (memcmp(buffer, NTLMSSP_SIGNATURE, 8) != 0) || - (memcmp(buffer + 8, type2_marker, sizeof(type2_marker)) != 0)) { + if((type2_len < 32) || + (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || + (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { /* This was not a good enough type-2 message */ - free(buffer); + free(type2); infof(data, "NTLM handshake failure (bad type-2 message)\n"); - return CURLE_REMOTE_ACCESS_DENIED; + return CURLE_BAD_CONTENT_ENCODING; } - ntlm->flags = readint_le(&buffer[20]); - memcpy(ntlm->nonce, &buffer[24], 8); + ntlm->flags = Curl_read32_le(&type2[20]); + memcpy(ntlm->nonce, &type2[24], 8); if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) { - error = Curl_ntlm_decode_type2_target(data, buffer, size, ntlm); - if(error) { - free(buffer); + result = ntlm_decode_type2_target(data, type2, type2_len, ntlm); + if(result) { + free(type2); infof(data, "NTLM handshake failure (bad type-2 message)\n"); - return error; + return result; } } @@ -336,32 +305,12 @@ CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, fprintf(stderr, "\n****\n"); fprintf(stderr, "**** Header %s\n ", header); }); -#endif - free(buffer); - - return CURLE_OK; -} -#ifdef USE_WINDOWS_SSPI -void Curl_ntlm_sspi_cleanup(struct ntlmdata *ntlm) -{ - Curl_safefree(ntlm->type_2); + free(type2); - if(ntlm->has_handles) { - s_pSecFn->DeleteSecurityContext(&ntlm->c_handle); - s_pSecFn->FreeCredentialsHandle(&ntlm->handle); - ntlm->has_handles = 0; - } - - ntlm->max_token_length = 0; - Curl_safefree(ntlm->output_token); - - Curl_sspi_free_identity(ntlm->p_identity); - ntlm->p_identity = NULL; + return result; } -#endif -#ifndef USE_WINDOWS_SSPI /* copy the source to the destination and fill in zeroes in every other destination byte! */ static void unicodecpy(unsigned char *dest, const char *src, size_t length) @@ -372,14 +321,12 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) dest[2 * i + 1] = '\0'; } } -#endif /* - * Curl_ntlm_create_type1_message() + * Curl_sasl_create_ntlm_type1_message() * * This is used to generate an already encoded NTLM type-1 message ready for - * sending to the recipient, be it a HTTP or SASL based (such as SMTP, POP3 - * or IMAP) server, using the appropriate compile time crypo API. + * sending to the recipient using the appropriate compile time crypto API. * * Parameters: * @@ -392,11 +339,10 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_create_type1_message(const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - char **outptr, - size_t *outlen) +CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + char **outptr, size_t *outlen) { /* NTLM type-1 message structure: @@ -414,89 +360,6 @@ CURLcode Curl_ntlm_create_type1_message(const char *userp, size_t size; -#ifdef USE_WINDOWS_SSPI - - PSecPkgInfo SecurityPackage; - SecBuffer type_1_buf; - SecBufferDesc type_1_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp tsDummy; /* For Windows 9x compatibility of SSPI calls */ - - Curl_ntlm_sspi_cleanup(ntlm); - - /* Query the security package for NTLM */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("NTLM"), - &SecurityPackage); - if(status != SEC_E_OK) - return CURLE_NOT_BUILT_IN; - - ntlm->max_token_length = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate our output buffer */ - ntlm->output_token = malloc(ntlm->max_token_length); - if(!ntlm->output_token) - return CURLE_OUT_OF_MEMORY; - - if(userp && *userp) { - CURLcode result; - - /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); - if(result) - return result; - - /* Allow proper cleanup of the identity structure */ - ntlm->p_identity = &ntlm->identity; - } - else - /* Use the current Windows user */ - ntlm->p_identity = NULL; - - /* Acquire our credientials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT("NTLM"), - SECPKG_CRED_OUTBOUND, NULL, - ntlm->p_identity, NULL, NULL, - &ntlm->handle, &tsDummy); - if(status != SEC_E_OK) - return CURLE_OUT_OF_MEMORY; - - /* Setup the type-1 "output" security buffer */ - type_1_desc.ulVersion = SECBUFFER_VERSION; - type_1_desc.cBuffers = 1; - type_1_desc.pBuffers = &type_1_buf; - type_1_buf.BufferType = SECBUFFER_TOKEN; - type_1_buf.pvBuffer = ntlm->output_token; - type_1_buf.cbBuffer = curlx_uztoul(ntlm->max_token_length); - - /* Generate our type-1 message */ - status = s_pSecFn->InitializeSecurityContext(&ntlm->handle, NULL, - (TCHAR *) TEXT(""), - ISC_REQ_CONFIDENTIALITY | - ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONNECTION, - 0, SECURITY_NETWORK_DREP, - NULL, 0, - &ntlm->c_handle, &type_1_desc, - &attrs, &tsDummy); - - if(status == SEC_I_COMPLETE_AND_CONTINUE || - status == SEC_I_CONTINUE_NEEDED) - s_pSecFn->CompleteAuthToken(&ntlm->c_handle, &type_1_desc); - else if(status != SEC_E_OK) { - s_pSecFn->FreeCredentialsHandle(&ntlm->handle); - return CURLE_RECV_ERROR; - } - - ntlm->has_handles = 1; - size = type_1_buf.cbBuffer; - -#else - unsigned char ntlmbuf[NTLM_BUFSIZE]; const char *host = ""; /* empty */ const char *domain = ""; /* empty */ @@ -507,9 +370,11 @@ CURLcode Curl_ntlm_create_type1_message(const char *userp, domain are empty */ (void)userp; (void)passwdp; - (void)ntlm; -#if USE_NTLM2SESSION + /* Clean up any former leftovers and initialise to defaults */ + Curl_sasl_ntlm_cleanup(ntlm); + +#if USE_NTRESPONSES && USE_NTLM2SESSION #define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY #else #define NTLM2FLAG 0 @@ -550,8 +415,6 @@ CURLcode Curl_ntlm_create_type1_message(const char *userp, /* Initial packet length */ size = 32 + hostlen + domlen; -#endif - DEBUG_OUT({ fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x " "0x%08.8x ", @@ -575,20 +438,14 @@ CURLcode Curl_ntlm_create_type1_message(const char *userp, }); /* Return with binary blob encoded into base64 */ -#ifdef USE_WINDOWS_SSPI - return Curl_base64_encode(NULL, (char *)ntlm->output_token, size, - outptr, outlen); -#else return Curl_base64_encode(NULL, (char *)ntlmbuf, size, outptr, outlen); -#endif } /* - * Curl_ntlm_create_type3_message() + * Curl_sasl_create_ntlm_type3_message() * * This is used to generate an already encoded NTLM type-3 message ready for - * sending to the recipient, be it a HTTP or SASL based (such as SMTP, POP3 - * or IMAP) server, using the appropriate compile time crypo API. + * sending to the recipient using the appropriate compile time crypto API. * * Parameters: * @@ -602,12 +459,12 @@ CURLcode Curl_ntlm_create_type1_message(const char *userp, * * Returns CURLE_OK on success. */ -CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - char **outptr, - size_t *outlen) +CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + char **outptr, size_t *outlen) + { /* NTLM type-3 message structure: @@ -627,65 +484,8 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, (*) -> Optional */ - size_t size; - -#ifdef USE_WINDOWS_SSPI CURLcode result = CURLE_OK; - SecBuffer type_2_buf; - SecBuffer type_3_buf; - SecBufferDesc type_2_desc; - SecBufferDesc type_3_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp tsDummy; /* For Windows 9x compatibility of SSPI calls */ - - (void)passwdp; - (void)userp; - (void)data; - - /* Setup the type-2 "input" security buffer */ - type_2_desc.ulVersion = SECBUFFER_VERSION; - type_2_desc.cBuffers = 1; - type_2_desc.pBuffers = &type_2_buf; - type_2_buf.BufferType = SECBUFFER_TOKEN; - type_2_buf.pvBuffer = ntlm->type_2; - type_2_buf.cbBuffer = ntlm->n_type_2; - - /* Setup the type-3 "output" security buffer */ - type_3_desc.ulVersion = SECBUFFER_VERSION; - type_3_desc.cBuffers = 1; - type_3_desc.pBuffers = &type_3_buf; - type_3_buf.BufferType = SECBUFFER_TOKEN; - type_3_buf.pvBuffer = ntlm->output_token; - type_3_buf.cbBuffer = curlx_uztoul(ntlm->max_token_length); - - /* Generate our type-3 message */ - status = s_pSecFn->InitializeSecurityContext(&ntlm->handle, - &ntlm->c_handle, - (TCHAR *) TEXT(""), - ISC_REQ_CONFIDENTIALITY | - ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONNECTION, - 0, SECURITY_NETWORK_DREP, - &type_2_desc, - 0, &ntlm->c_handle, - &type_3_desc, - &attrs, &tsDummy); - if(status != SEC_E_OK) - return CURLE_RECV_ERROR; - - size = type_3_buf.cbBuffer; - - /* Return with binary blob encoded into base64 */ - result = Curl_base64_encode(NULL, (char *)ntlm->output_token, size, - outptr, outlen); - - Curl_ntlm_sspi_cleanup(ntlm); - - return result; - -#else - + size_t size; unsigned char ntlmbuf[NTLM_BUFSIZE]; int lmrespoff; unsigned char lmresp[24]; /* fixed-size */ @@ -706,7 +506,6 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, size_t hostlen = 0; size_t userlen = 0; size_t domlen = 0; - CURLcode res = CURLE_OK; user = strchr(userp, '\\'); if(!user) @@ -733,7 +532,7 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, hostlen = strlen(host); } -#if USE_NTRESPONSES +#if USE_NTRESPONSES && USE_NTLM_V2 if(ntlm->target_info_len) { unsigned char ntbuffer[0x18]; unsigned int entropy[2]; @@ -742,35 +541,35 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, entropy[0] = Curl_rand(data); entropy[1] = Curl_rand(data); - res = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); - if(res) - return res; + result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); + if(result) + return result; - res = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen, - ntbuffer, ntlmv2hash); - if(res) - return res; + result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen, + ntbuffer, ntlmv2hash); + if(result) + return result; /* LMv2 response */ - res = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, - (unsigned char *)&entropy[0], - &ntlm->nonce[0], lmresp); - if(res) - return res; + result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, + (unsigned char *)&entropy[0], + &ntlm->nonce[0], lmresp); + if(result) + return result; /* NTLMv2 response */ - res = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, - (unsigned char *)&entropy[0], - ntlm, &ntlmv2resp, &ntresplen); - if(res) - return res; + result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, + (unsigned char *)&entropy[0], + ntlm, &ntlmv2resp, &ntresplen); + if(result) + return result; ptr_ntresp = ntlmv2resp; } else #endif -#if USE_NTLM2SESSION +#if USE_NTRESPONSES && USE_NTLM2SESSION /* We don't support NTLM2 if we don't have USE_NTRESPONSES */ if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { unsigned char ntbuffer[0x18]; @@ -792,13 +591,13 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, memcpy(tmp, &ntlm->nonce[0], 8); memcpy(tmp + 8, entropy, 8); - Curl_ssl_md5sum(tmp, 16, md5sum, MD5_DIGEST_LENGTH); - - /* 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 */ - if(CURLE_OUT_OF_MEMORY == - Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer)) - return CURLE_OUT_OF_MEMORY; + result = Curl_ssl_md5sum(tmp, 16, md5sum, MD5_DIGEST_LENGTH); + if(!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); @@ -815,14 +614,19 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, unsigned char lmbuffer[0x18]; #if USE_NTRESPONSES - if(CURLE_OUT_OF_MEMORY == - Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer)) - return CURLE_OUT_OF_MEMORY; + result = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); + if(result) + return result; + Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp); #endif - Curl_ntlm_core_mk_lm_hash(data, passwdp, lmbuffer); + result = Curl_ntlm_core_mk_lm_hash(data, passwdp, lmbuffer); + if(result) + return result; + Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp); + /* A safer but less compatible alternative is: * Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp); * See http://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */ @@ -954,7 +758,7 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen); }); - Curl_safefree(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ + free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ #endif @@ -997,14 +801,17 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, size += hostlen; /* Convert domain, user, and host to ASCII but leave the rest as-is */ - res = Curl_convert_to_network(data, (char *)&ntlmbuf[domoff], - size - domoff); - if(res) + result = Curl_convert_to_network(data, (char *)&ntlmbuf[domoff], + size - domoff); + if(result) return CURLE_CONV_FAILED; /* Return with binary blob encoded into base64 */ - return Curl_base64_encode(NULL, (char *)ntlmbuf, size, outptr, outlen); -#endif + result = Curl_base64_encode(NULL, (char *)ntlmbuf, size, outptr, outlen); + + Curl_sasl_ntlm_cleanup(ntlm); + + return result; } -#endif /* USE_NTLM */ +#endif /* USE_NTLM && !USE_WINDOWS_SSPI */ diff --git a/lib/curl_ntlm_msgs.h b/lib/curl_ntlm_msgs.h index 80413c8..2a71431 100644 --- a/lib/curl_ntlm_msgs.h +++ b/lib/curl_ntlm_msgs.h @@ -26,40 +26,6 @@ #ifdef USE_NTLM -/* This is to generate a base64 encoded NTLM type-1 message */ -CURLcode Curl_ntlm_create_type1_message(const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - char **outptr, - size_t *outlen); - -/* This is to generate a base64 encoded NTLM type-3 message */ -CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - char **outptr, - size_t *outlen); - -/* This is to decode a NTLM type-2 message */ -CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, - const char* header, - struct ntlmdata* ntlm); - -/* This is to decode target info received in NTLM type-2 message */ -CURLcode Curl_ntlm_decode_type2_target(struct SessionHandle *data, - unsigned char* buffer, - size_t size, - struct ntlmdata* ntlm); - - -/* This is to clean up the ntlm data structure */ -#ifdef USE_WINDOWS_SSPI -void Curl_ntlm_sspi_cleanup(struct ntlmdata *ntlm); -#else -#define Curl_ntlm_sspi_cleanup(x) -#endif - /* NTLM buffer fixed size, large enough for long user + host + domain */ #define NTLM_BUFSIZE 1024 diff --git a/lib/curl_ntlm_wb.c b/lib/curl_ntlm_wb.c index 23ee726..b2a5fb3 100644 --- a/lib/curl_ntlm_wb.c +++ b/lib/curl_ntlm_wb.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,8 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) /* * NTLM details: @@ -50,12 +51,10 @@ #include "curl_ntlm_wb.h" #include "url.h" #include "strerror.h" -#include "curl_memory.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #if DEBUG_ME @@ -106,9 +105,9 @@ void Curl_ntlm_wb_cleanup(struct connectdata *conn) conn->ntlm_auth_hlpr_pid = 0; } - Curl_safefree(conn->challenge_header); + free(conn->challenge_header); conn->challenge_header = NULL; - Curl_safefree(conn->response_header); + free(conn->response_header); conn->response_header = NULL; } @@ -245,13 +244,13 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp) sclose(sockfds[1]); conn->ntlm_auth_hlpr_socket = sockfds[0]; conn->ntlm_auth_hlpr_pid = child_pid; - Curl_safefree(domain); - Curl_safefree(ntlm_auth_alloc); + free(domain); + free(ntlm_auth_alloc); return CURLE_OK; done: - Curl_safefree(domain); - Curl_safefree(ntlm_auth_alloc); + free(domain); + free(ntlm_auth_alloc); return CURLE_REMOTE_ACCESS_DENIED; } @@ -293,7 +292,7 @@ static CURLcode ntlm_wb_response(struct connectdata *conn, len_out += size; if(buf[len_out - 1] == '\n') { buf[len_out - 1] = '\0'; - goto wrfinish; + break; } newbuf = realloc(buf, len_out + NTLM_BUFSIZE); if(!newbuf) { @@ -302,13 +301,12 @@ static CURLcode ntlm_wb_response(struct connectdata *conn, } buf = newbuf; } - goto done; -wrfinish: + /* Samba/winbind installed but not configured */ if(state == NTLMSTATE_TYPE1 && len_out == 3 && buf[0] == 'P' && buf[1] == 'W') - return CURLE_REMOTE_ACCESS_DENIED; + goto done; /* invalid response */ if(len_out < 4) goto done; @@ -391,12 +389,12 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, if(res) return res; - Curl_safefree(*allocuserpwd); + free(*allocuserpwd); *allocuserpwd = aprintf("%sAuthorization: %s\r\n", proxy ? "Proxy-" : "", conn->response_header); DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); - Curl_safefree(conn->response_header); + free(conn->response_header); conn->response_header = NULL; break; case NTLMSTATE_TYPE2: @@ -409,7 +407,7 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, if(res) return res; - Curl_safefree(*allocuserpwd); + free(*allocuserpwd); *allocuserpwd = aprintf("%sAuthorization: %s\r\n", proxy ? "Proxy-" : "", conn->response_header); @@ -421,10 +419,8 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, case NTLMSTATE_TYPE3: /* connection is already authenticated, * don't send a header in future requests */ - if(*allocuserpwd) { - free(*allocuserpwd); - *allocuserpwd=NULL; - } + free(*allocuserpwd); + *allocuserpwd=NULL; authp->done = TRUE; break; } @@ -432,4 +428,4 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, return CURLE_OK; } -#endif /* USE_NTLM && NTLM_WB_ENABLED */ +#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ diff --git a/lib/curl_ntlm_wb.h b/lib/curl_ntlm_wb.h index db6bc16..828bb57 100644 --- a/lib/curl_ntlm_wb.h +++ b/lib/curl_ntlm_wb.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,7 +24,8 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) /* this is for creating ntlm header output by delegating challenge/response to Samba's winbind daemon helper ntlm_auth */ @@ -32,6 +33,6 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn, bool proxy); void Curl_ntlm_wb_cleanup(struct connectdata *conn); -#endif /* USE_NTLM && NTLM_WB_ENABLED */ +#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ #endif /* HEADER_CURL_NTLM_WB_H */ diff --git a/lib/curl_printf.h b/lib/curl_printf.h new file mode 100644 index 0000000..086923f --- /dev/null +++ b/lib/curl_printf.h @@ -0,0 +1,56 @@ +#ifndef HEADER_CURL_PRINTF_H +#define HEADER_CURL_PRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.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. + * + ***************************************************************************/ + +/* + * This header should be included by ALL code in libcurl that uses any + * *rintf() functions. + */ + +#include <curl/mprintf.h> + +# undef printf +# undef fprintf +# undef snprintf +# undef vprintf +# undef vfprintf +# undef vsnprintf +# undef aprintf +# undef vaprintf +# define printf curl_mprintf +# define fprintf curl_mfprintf +# define snprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define vsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf + +/* We define away the sprintf functions unconditonally since we don't want + internal code to be using them, intentionally or by mistake!*/ +# undef sprintf +# undef vsprintf +# define sprintf sprintf_was_used +# define vsprintf vsprintf_was_used + +#endif /* HEADER_CURL_PRINTF_H */ diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c index e0c24b0..2938972 100644 --- a/lib/curl_rtmp.c +++ b/lib/curl_rtmp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com> * * This software is licensed as described in the file COPYING, which @@ -32,10 +32,6 @@ #include "warnless.h" #include <curl/curl.h> #include <librtmp/rtmp.h> - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -49,7 +45,7 @@ #define DEF_BUFTIME (2*60*60*1000) /* 2 hours */ -static CURLcode rtmp_setup(struct connectdata *conn); +static CURLcode rtmp_setup_connection(struct connectdata *conn); static CURLcode rtmp_do(struct connectdata *conn, bool *done); static CURLcode rtmp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode rtmp_connect(struct connectdata *conn, bool *done); @@ -64,7 +60,7 @@ static Curl_send rtmp_send; const struct Curl_handler Curl_handler_rtmp = { "RTMP", /* scheme */ - rtmp_setup, /* setup_connection */ + rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ ZERO_NULL, /* do_more */ @@ -84,7 +80,7 @@ const struct Curl_handler Curl_handler_rtmp = { const struct Curl_handler Curl_handler_rtmpt = { "RTMPT", /* scheme */ - rtmp_setup, /* setup_connection */ + rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ ZERO_NULL, /* do_more */ @@ -104,7 +100,7 @@ const struct Curl_handler Curl_handler_rtmpt = { const struct Curl_handler Curl_handler_rtmpe = { "RTMPE", /* scheme */ - rtmp_setup, /* setup_connection */ + rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ ZERO_NULL, /* do_more */ @@ -124,7 +120,7 @@ const struct Curl_handler Curl_handler_rtmpe = { const struct Curl_handler Curl_handler_rtmpte = { "RTMPTE", /* scheme */ - rtmp_setup, /* setup_connection */ + rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ ZERO_NULL, /* do_more */ @@ -144,7 +140,7 @@ const struct Curl_handler Curl_handler_rtmpte = { const struct Curl_handler Curl_handler_rtmps = { "RTMPS", /* scheme */ - rtmp_setup, /* setup_connection */ + rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ ZERO_NULL, /* do_more */ @@ -164,7 +160,7 @@ const struct Curl_handler Curl_handler_rtmps = { const struct Curl_handler Curl_handler_rtmpts = { "RTMPTS", /* scheme */ - rtmp_setup, /* setup_connection */ + rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ ZERO_NULL, /* do_more */ @@ -182,10 +178,9 @@ const struct Curl_handler Curl_handler_rtmpts = { PROTOPT_NONE /* flags*/ }; -static CURLcode rtmp_setup(struct connectdata *conn) +static CURLcode rtmp_setup_connection(struct connectdata *conn) { RTMP *r = RTMP_Alloc(); - if(!r) return CURLE_OUT_OF_MEMORY; @@ -202,7 +197,7 @@ static CURLcode rtmp_setup(struct connectdata *conn) static CURLcode rtmp_connect(struct connectdata *conn, bool *done) { RTMP *r = conn->proto.generic; - SET_RCVTIMEO(tv,10); + SET_RCVTIMEO(tv, 10); r->m_sb.sb_socket = conn->sock[FIRSTSOCKET]; @@ -217,7 +212,7 @@ static CURLcode rtmp_connect(struct connectdata *conn, bool *done) !(r->Link.protocol & RTMP_FEATURE_HTTP)) r->Link.lFlags |= RTMP_LF_BUFX; - curlx_nonblock(r->m_sb.sb_socket, FALSE); + (void)curlx_nonblock(r->m_sb.sb_socket, FALSE); setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 7e2b8af..68646bc 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -19,6 +19,7 @@ * KIND, either express or implied. * * RFC2195 CRAM-MD5 authentication + * RFC2617 Basic and Digest Access Authentication * RFC2831 DIGEST-MD5 authentication * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4616 PLAIN authentication @@ -36,26 +37,35 @@ #include "curl_md5.h" #include "vtls/vtls.h" #include "curl_hmac.h" -#include "curl_ntlm_msgs.h" #include "curl_sasl.h" #include "warnless.h" -#include "curl_memory.h" #include "strtok.h" +#include "strequal.h" #include "rawstr.h" +#include "sendf.h" +#include "non-ascii.h" /* included for Curl_convert_... prototypes */ +#include "curl_printf.h" -#ifdef USE_NSS -#include "vtls/nssg.h" /* for Curl_nss_force_init() */ -#endif - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -#if defined(USE_WINDOWS_SSPI) -extern void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); -#endif +/* Supported mechanisms */ +const struct { + const char *name; /* Name */ + size_t len; /* Name length */ + unsigned int bit; /* Flag bit */ +} mechtable[] = { + { "LOGIN", 5, SASL_MECH_LOGIN }, + { "PLAIN", 5, SASL_MECH_PLAIN }, + { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 }, + { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 }, + { "GSSAPI", 6, SASL_MECH_GSSAPI }, + { "EXTERNAL", 8, SASL_MECH_EXTERNAL }, + { "NTLM", 4, SASL_MECH_NTLM }, + { "XOAUTH2", 7, SASL_MECH_XOAUTH2 }, + { ZERO_NULL, 0, 0 } +}; #if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI) #define DIGEST_QOP_VALUE_AUTH (1 << 0) @@ -66,6 +76,131 @@ extern void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); #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) \ + result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \ + if(result) { \ + free(b); \ + return result; \ + } + +#endif + +#if !defined(CURL_DISABLE_CRYPTO_AUTH) +/* + * Returns 0 on success and then the buffers are filled in fine. + * + * Non-zero means failure to parse. + */ +int Curl_sasl_digest_get_pair(const char *str, char *value, char *content, + const char **endptr) +{ + int c; + bool starts_with_quote = FALSE; + bool escape = FALSE; + + for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--); ) + *value++ = *str++; + *value = 0; + + if('=' != *str++) + /* eek, no match */ + return 1; + + if('\"' == *str) { + /* this starts with a quote so it must end with one as well! */ + str++; + starts_with_quote = TRUE; + } + + for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) { + switch(*str) { + case '\\': + if(!escape) { + /* possibly the start of an escaped quote */ + escape = TRUE; + *content++ = '\\'; /* even though this is an escape character, we still + store it as-is in the target buffer */ + continue; + } + break; + case ',': + if(!starts_with_quote) { + /* this signals the end of the content if we didn't get a starting + quote and then we do "sloppy" parsing */ + c = 0; /* the end */ + continue; + } + break; + case '\r': + case '\n': + /* end of string */ + c = 0; + continue; + case '\"': + if(!escape && starts_with_quote) { + /* end of string */ + c = 0; + continue; + } + break; + } + escape = FALSE; + *content++ = *str; + } + *content = 0; + + *endptr = str; + + return 0; /* all is fine! */ +} +#endif + +#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(USE_WINDOWS_SSPI) +/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ +static void sasl_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ + unsigned char *dest) /* 33 bytes */ +{ + int i; + for(i = 0; i < 16; i++) + snprintf((char *)&dest[i*2], 3, "%02x", source[i]); +} + +/* Perform quoted-string escaping as described in RFC2616 and its errata */ +static char *sasl_digest_string_quoted(const char *source) +{ + char *dest, *d; + const char *s = source; + size_t n = 1; /* null terminator */ + + /* Calculate size needed */ + while(*s) { + ++n; + if(*s == '"' || *s == '\\') { + ++n; + } + ++s; + } + + dest = malloc(n); + if(dest) { + s = source; + d = dest; + while(*s) { + if(*s == '"' || *s == '\\') { + *d++ = '\\'; + } + *d++ = *s++; + } + *d = 0; + } + + return dest; +} + /* Retrieves the value for a corresponding key from the challenge string * returns TRUE if the key could be found, FALSE if it does not exists */ @@ -118,11 +253,11 @@ static CURLcode sasl_digest_get_qop_values(const char *options, int *value) token = strtok_r(NULL, ",", &tok_buf); } - Curl_safefree(tmp); + free(tmp); return CURLE_OK; } -#endif +#endif /* !CURL_DISABLE_CRYPTO_AUTH && !USE_WINDOWS_SSPI */ #if !defined(USE_WINDOWS_SSPI) /* @@ -132,8 +267,8 @@ static CURLcode sasl_digest_get_qop_values(const char *options, int *value) * * Parameters: * - * serivce [in] - The service type such as www, smtp, pop or imap. - * instance [in] - The instance name such as the host nme or realm. + * service [in] - The service type such as www, smtp, pop or imap. + * host [in] - The host name or realm. * * Returns a pointer to the newly allocated SPN. */ @@ -145,7 +280,7 @@ char *Curl_sasl_build_spn(const char *service, const char *host) #endif /* - * Curl_sasl_create_plain_message() + * sasl_create_plain_message() * * This is used to generate an already encoded PLAIN message ready * for sending to the recipient. @@ -161,10 +296,10 @@ char *Curl_sasl_build_spn(const char *service, const char *host) * * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - char **outptr, size_t *outlen) +static CURLcode sasl_create_plain_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + char **outptr, size_t *outlen) { CURLcode result; char *plainauth; @@ -191,12 +326,12 @@ CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, /* Base64 encode the reply */ result = Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr, outlen); - Curl_safefree(plainauth); + free(plainauth); return result; } /* - * Curl_sasl_create_login_message() + * sasl_create_login_message() * * This is used to generate an already encoded LOGIN message containing the * user name or password ready for sending to the recipient. @@ -211,9 +346,9 @@ CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, * * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, - const char *valuep, char **outptr, - size_t *outlen) +static CURLcode sasl_create_login_message(struct SessionHandle *data, + const char *valuep, char **outptr, + size_t *outlen) { size_t vlen = strlen(valuep); @@ -233,23 +368,47 @@ CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, return Curl_base64_encode(data, valuep, vlen, outptr, outlen); } +/* + * sasl_create_external_message() + * + * This is used to generate an already encoded EXTERNAL message containing + * the user name ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * user [in] - The user name. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +static CURLcode sasl_create_external_message(struct SessionHandle *data, + const char *user, char **outptr, + size_t *outlen) +{ + /* This is the same formatting as the login message. */ + return sasl_create_login_message(data, user, outptr, outlen); +} + #ifndef CURL_DISABLE_CRYPTO_AUTH /* - * Curl_sasl_decode_cram_md5_message() + * sasl_decode_cram_md5_message() * * This is used to decode an already encoded CRAM-MD5 challenge message. * * Parameters: * - * chlg64 [in] - Pointer to the base64 encoded challenge message. + * chlg64 [in] - The base64 encoded challenge message. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr, - size_t *outlen) +static CURLcode sasl_decode_cram_md5_message(const char *chlg64, char **outptr, + size_t *outlen) { CURLcode result = CURLE_OK; size_t chlg64len = strlen(chlg64); @@ -265,7 +424,7 @@ CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr, } /* - * Curl_sasl_create_cram_md5_message() + * sasl_create_cram_md5_message() * * This is used to generate an already encoded CRAM-MD5 response message ready * for sending to the recipient. @@ -282,11 +441,11 @@ CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr, * * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, - const char *chlg, - const char *userp, - const char *passwdp, - char **outptr, size_t *outlen) +static CURLcode sasl_create_cram_md5_message(struct SessionHandle *data, + const char *chlg, + const char *userp, + const char *passwdp, + char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlglen = 0; @@ -324,7 +483,7 @@ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, /* Base64 encode the response */ result = Curl_base64_encode(data, response, 0, outptr, outlen); - Curl_safefree(response); + free(response); return result; } @@ -338,7 +497,7 @@ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, * * Parameters: * - * chlg64 [in] - Pointer to the base64 encoded challenge message. + * chlg64 [in] - The base64 encoded challenge message. * nonce [in/out] - The buffer where the nonce will be stored. * nlen [in] - The length of the nonce buffer. * realm [in/out] - The buffer where the realm will be stored. @@ -374,7 +533,7 @@ static CURLcode sasl_decode_digest_md5_message(const char *chlg64, /* Retrieve nonce string from the challenge */ if(!sasl_digest_get_key_value((char *)chlg, "nonce=\"", nonce, nlen, '\"')) { - Curl_safefree(chlg); + free(chlg); return CURLE_BAD_CONTENT_ENCODING; } @@ -386,17 +545,17 @@ static CURLcode sasl_decode_digest_md5_message(const char *chlg64, /* Retrieve algorithm string from the challenge */ if(!sasl_digest_get_key_value((char *)chlg, "algorithm=", alg, alen, ',')) { - Curl_safefree(chlg); + free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Retrieve qop-options string from the challenge */ if(!sasl_digest_get_key_value((char *)chlg, "qop=\"", qop, qlen, '\"')) { - Curl_safefree(chlg); + free(chlg); return CURLE_BAD_CONTENT_ENCODING; } - Curl_safefree(chlg); + free(chlg); return CURLE_OK; } @@ -410,7 +569,7 @@ static CURLcode sasl_decode_digest_md5_message(const char *chlg64, * Parameters: * * data [in] - The session handle. - * chlg64 [in] - Pointer to the base64 encoded challenge message. + * chlg64 [in] - The base64 encoded challenge message. * userp [in] - The user name. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. @@ -518,7 +677,7 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, /* Calculate H(A2) */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); if(!ctxt) { - Curl_safefree(spn); + free(spn); return CURLE_OUT_OF_MEMORY; } @@ -536,7 +695,7 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, /* Now calculate the response hash */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); if(!ctxt) { - Curl_safefree(spn); + free(spn); return CURLE_OUT_OF_MEMORY; } @@ -569,110 +728,432 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, "qop=%s", userp, realm, nonce, cnonce, nonceCount, spn, resp_hash_hex, qop); - Curl_safefree(spn); + free(spn); if(!response) return CURLE_OUT_OF_MEMORY; /* Base64 encode the response */ result = Curl_base64_encode(data, response, 0, outptr, outlen); - Curl_safefree(response); + free(response); return result; } -#endif /* !USE_WINDOWS_SSPI */ -#endif /* CURL_DISABLE_CRYPTO_AUTH */ - -#ifdef USE_NTLM /* - * Curl_sasl_create_ntlm_type1_message() + * Curl_sasl_decode_digest_http_message() * - * This is used to generate an already encoded NTLM type-1 message ready for - * sending to the recipient. + * This is used to decode a HTTP DIGEST challenge message into the seperate + * attributes. * - * Note: This is a simple wrapper of the NTLM function which means that any - * SASL based protocols don't have to include the NTLM functions directly. + * Parameters: + * + * chlg [in] - The challenge message. + * digest [in/out] - The digest data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, + struct digestdata *digest) +{ + bool before = FALSE; /* got a nonce before */ + bool foundAuth = FALSE; + bool foundAuthInt = FALSE; + char *token = NULL; + char *tmp = NULL; + + /* If we already have received a nonce, keep that in mind */ + if(digest->nonce) + before = TRUE; + + /* Clean up any former leftovers and initialise to defaults */ + Curl_sasl_digest_cleanup(digest); + + for(;;) { + char value[DIGEST_MAX_VALUE_LENGTH]; + char content[DIGEST_MAX_CONTENT_LENGTH]; + + /* Pass all additional spaces here */ + while(*chlg && ISSPACE(*chlg)) + chlg++; + + /* Extract a value=content pair */ + if(!Curl_sasl_digest_get_pair(chlg, value, content, &chlg)) { + if(Curl_raw_equal(value, "nonce")) { + digest->nonce = strdup(content); + if(!digest->nonce) + return CURLE_OUT_OF_MEMORY; + } + else if(Curl_raw_equal(value, "stale")) { + if(Curl_raw_equal(content, "true")) { + digest->stale = TRUE; + digest->nc = 1; /* we make a new nonce now */ + } + } + else if(Curl_raw_equal(value, "realm")) { + digest->realm = strdup(content); + if(!digest->realm) + return CURLE_OUT_OF_MEMORY; + } + else if(Curl_raw_equal(value, "opaque")) { + digest->opaque = strdup(content); + if(!digest->opaque) + return CURLE_OUT_OF_MEMORY; + } + else if(Curl_raw_equal(value, "qop")) { + char *tok_buf; + /* Tokenize the list and choose auth if possible, use a temporary + clone of the buffer since strtok_r() ruins it */ + tmp = strdup(content); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ",", &tok_buf); + while(token != NULL) { + if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH)) { + foundAuth = TRUE; + } + else if(Curl_raw_equal(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) { + foundAuthInt = TRUE; + } + token = strtok_r(NULL, ",", &tok_buf); + } + + free(tmp); + + /* Select only auth or auth-int. Otherwise, ignore */ + if(foundAuth) { + digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH); + if(!digest->qop) + return CURLE_OUT_OF_MEMORY; + } + else if(foundAuthInt) { + digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT); + if(!digest->qop) + return CURLE_OUT_OF_MEMORY; + } + } + else if(Curl_raw_equal(value, "algorithm")) { + digest->algorithm = strdup(content); + if(!digest->algorithm) + return CURLE_OUT_OF_MEMORY; + + if(Curl_raw_equal(content, "MD5-sess")) + digest->algo = CURLDIGESTALGO_MD5SESS; + else if(Curl_raw_equal(content, "MD5")) + digest->algo = CURLDIGESTALGO_MD5; + else + return CURLE_BAD_CONTENT_ENCODING; + } + else { + /* unknown specifier, ignore it! */ + } + } + else + break; /* we're done here */ + + /* Pass all additional spaces here */ + while(*chlg && ISSPACE(*chlg)) + chlg++; + + /* Allow the list to be comma-separated */ + if(',' == *chlg) + chlg++; + } + + /* We had a nonce since before, and we got another one now without + 'stale=true'. This means we provided bad credentials in the previous + request */ + if(before && !digest->stale) + return CURLE_BAD_CONTENT_ENCODING; + + /* We got this header without a nonce, that's a bad Digest line! */ + if(!digest->nonce) + return CURLE_BAD_CONTENT_ENCODING; + + return CURLE_OK; +} + +/* + * Curl_sasl_create_digest_http_message() + * + * This is used to generate a HTTP DIGEST response message ready for sending + * to the recipient. * * Parameters: * - * userp [in] - The user name in the format User or Domain\User. + * data [in] - The session handle. + * userp [in] - The user name. * passdwp [in] - The user's password. - * ntlm [in/out] - The ntlm data struct being used and modified. + * request [in] - The HTTP request. + * uripath [in] - The path of the HTTP uri. + * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - char **outptr, size_t *outlen) +CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen) { - return Curl_ntlm_create_type1_message(userp, passwdp, ntlm, outptr, outlen); + CURLcode result; + unsigned char md5buf[16]; /* 16 bytes/128 bits */ + unsigned char request_digest[33]; + unsigned char *md5this; + unsigned char ha1[33];/* 32 digits and 1 zero byte */ + unsigned char ha2[33];/* 32 digits and 1 zero byte */ + char cnoncebuf[33]; + char *cnonce = NULL; + size_t cnonce_sz = 0; + char *userp_quoted; + char *response = NULL; + char *tmp = NULL; + + if(!digest->nc) + digest->nc = 1; + + if(!digest->cnonce) { + snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", + Curl_rand(data), Curl_rand(data), + Curl_rand(data), Curl_rand(data)); + + result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), + &cnonce, &cnonce_sz); + if(result) + return result; + + digest->cnonce = cnonce; + } + + /* + if the algorithm is "MD5" or unspecified (which then defaults to MD5): + + A1 = unq(username-value) ":" unq(realm-value) ":" passwd + + if the algorithm is "MD5-sess" then: + + A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) + ":" unq(nonce-value) ":" unq(cnonce-value) + */ + + md5this = (unsigned char *) + aprintf("%s:%s:%s", userp, digest->realm, passwdp); + if(!md5this) + return CURLE_OUT_OF_MEMORY; + + CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ + Curl_md5it(md5buf, md5this); + free(md5this); + sasl_digest_md5_to_ascii(md5buf, ha1); + + if(digest->algo == CURLDIGESTALGO_MD5SESS) { + /* nonce and cnonce are OUTSIDE the hash */ + tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ + Curl_md5it(md5buf, (unsigned char *)tmp); + free(tmp); + sasl_digest_md5_to_ascii(md5buf, ha1); + } + + /* + If the "qop" directive's value is "auth" or is unspecified, then A2 is: + + A2 = Method ":" digest-uri-value + + If the "qop" value is "auth-int", then A2 is: + + A2 = Method ":" digest-uri-value ":" H(entity-body) + + (The "Method" value is the HTTP request method as specified in section + 5.1.1 of RFC 2616) + */ + + md5this = (unsigned char *)aprintf("%s:%s", request, uripath); + + if(digest->qop && Curl_raw_equal(digest->qop, "auth-int")) { + /* We don't support auth-int for PUT or POST at the moment. + TODO: replace md5 of empty string with entity-body for PUT/POST */ + unsigned char *md5this2 = (unsigned char *) + aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); + free(md5this); + md5this = md5this2; + } + + if(!md5this) + return CURLE_OUT_OF_MEMORY; + + CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ + Curl_md5it(md5buf, md5this); + free(md5this); + sasl_digest_md5_to_ascii(md5buf, ha2); + + if(digest->qop) { + md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s", + ha1, + digest->nonce, + digest->nc, + digest->cnonce, + digest->qop, + ha2); + } + else { + md5this = (unsigned char *)aprintf("%s:%s:%s", + ha1, + digest->nonce, + ha2); + } + + if(!md5this) + return CURLE_OUT_OF_MEMORY; + + CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ + Curl_md5it(md5buf, md5this); + free(md5this); + sasl_digest_md5_to_ascii(md5buf, request_digest); + + /* for test case 64 (snooped from a Mozilla 1.3a request) + + Authorization: Digest username="testuser", realm="testrealm", \ + nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" + + Digest parameters are all quoted strings. Username which is provided by + the user will need double quotes and backslashes within it escaped. For + the other fields, this shouldn't be an issue. realm, nonce, and opaque + are copied as is from the server, escapes and all. cnonce is generated + with web-safe characters. uri is already percent encoded. nc is 8 hex + characters. algorithm and qop with standard values only contain web-safe + chracters. + */ + userp_quoted = sasl_digest_string_quoted(userp); + if(!userp_quoted) + return CURLE_OUT_OF_MEMORY; + + if(digest->qop) { + response = aprintf("username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "cnonce=\"%s\", " + "nc=%08x, " + "qop=%s, " + "response=\"%s\"", + userp_quoted, + digest->realm, + digest->nonce, + uripath, + digest->cnonce, + digest->nc, + digest->qop, + request_digest); + + if(Curl_raw_equal(digest->qop, "auth")) + digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 + padded which tells to the server how many times you are + using the same nonce in the qop=auth mode */ + } + else { + response = aprintf("username=\"%s\", " + "realm=\"%s\", " + "nonce=\"%s\", " + "uri=\"%s\", " + "response=\"%s\"", + userp_quoted, + digest->realm, + digest->nonce, + uripath, + request_digest); + } + free(userp_quoted); + if(!response) + return CURLE_OUT_OF_MEMORY; + + /* Add the optional fields */ + if(digest->opaque) { + /* Append the opaque */ + tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque); + free(response); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + response = tmp; + } + + if(digest->algorithm) { + /* Append the algorithm */ + tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm); + free(response); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + response = tmp; + } + + /* Return the output */ + *outptr = response; + *outlen = strlen(response); + + return CURLE_OK; } /* - * Curl_sasl_decode_ntlm_type2_message() + * Curl_sasl_digest_cleanup() * - * This is used to decode an already encoded NTLM type-2 message. + * This is used to clean up the digest specific data. * * Parameters: * - * data [in] - Pointer to session handle. - * type2msg [in] - Pointer to the base64 encoded type-2 message. - * ntlm [in/out] - The ntlm data struct being used and modified. + * digest [in/out] - The digest data struct being cleaned up. * - * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data, - const char *type2msg, - struct ntlmdata *ntlm) +void Curl_sasl_digest_cleanup(struct digestdata *digest) { -#ifdef USE_NSS - CURLcode result; - - /* make sure the crypto backend is initialized */ - result = Curl_nss_force_init(data); - if(result) - return result; -#endif - - return Curl_ntlm_decode_type2_message(data, type2msg, ntlm); + Curl_safefree(digest->nonce); + Curl_safefree(digest->cnonce); + Curl_safefree(digest->realm); + Curl_safefree(digest->opaque); + Curl_safefree(digest->qop); + Curl_safefree(digest->algorithm); + + digest->nc = 0; + digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */ + digest->stale = FALSE; /* default means normal, not stale */ } +#endif /* !USE_WINDOWS_SSPI */ + +#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) /* - * Curl_sasl_create_ntlm_type3_message() + * Curl_sasl_ntlm_cleanup() * - * This is used to generate an already encoded NTLM type-3 message ready for - * sending to the recipient. + * This is used to clean up the ntlm specific data. * * Parameters: * - * data [in] - Pointer to session handle. - * userp [in] - The user name in the format User or Domain\User. - * passdwp [in] - The user's password. - * ntlm [in/out] - The ntlm data struct being used and modified. - * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. - * outlen [out] - The length of the output message. + * ntlm [in/out] - The ntlm data struct being cleaned up. * - * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - char **outptr, size_t *outlen) +void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm) { - return Curl_ntlm_create_type3_message(data, userp, passwdp, ntlm, outptr, - outlen); + /* Free the target info */ + Curl_safefree(ntlm->target_info); + + /* Reset any variables */ + ntlm->target_info_len = 0; } -#endif /* USE_NTLM */ +#endif /* USE_NTLM && !USE_WINDOWS_SSPI*/ /* - * Curl_sasl_create_xoauth2_message() + * sasl_create_xoauth2_message() * * This is used to generate an already encoded OAuth 2.0 message ready for * sending to the recipient. @@ -688,10 +1169,10 @@ CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, * * Returns CURLE_OK on success. */ -CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, - const char *user, - const char *bearer, - char **outptr, size_t *outlen) +static CURLcode sasl_create_xoauth2_message(struct SessionHandle *data, + const char *user, + const char *bearer, + char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; char *xoauth = NULL; @@ -704,7 +1185,7 @@ CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, /* Base64 encode the reply */ result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen); - Curl_safefree(xoauth); + free(xoauth); return result; } @@ -717,25 +1198,472 @@ CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, * * Parameters: * - * conn [in] - Pointer to the connection data. + * conn [in] - The connection data. * authused [in] - The authentication mechanism used. */ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) { -#if defined(USE_WINDOWS_SSPI) +#if defined(USE_KERBEROS5) /* Cleanup the gssapi structure */ if(authused == SASL_MECH_GSSAPI) { Curl_sasl_gssapi_cleanup(&conn->krb5); } -#ifdef USE_NTLM +#endif + +#if defined(USE_NTLM) /* Cleanup the ntlm structure */ - else if(authused == SASL_MECH_NTLM) { - Curl_ntlm_sspi_cleanup(&conn->ntlm); + if(authused == SASL_MECH_NTLM) { + Curl_sasl_ntlm_cleanup(&conn->ntlm); } #endif -#else + +#if !defined(USE_KERBEROS5) && !defined(USE_NTLM) /* Reserved for future use */ (void)conn; (void)authused; #endif } + +/* + * Curl_sasl_decode_mech() + * + * Convert a SASL mechanism name into a token. + * + * Parameters: + * + * ptr [in] - The mechanism string. + * maxlen [in] - Maximum mechanism string length. + * len [out] - If not NULL, effective name length. + * + * Returns the SASL mechanism token or 0 if no match. + */ +unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len) +{ + unsigned int i; + char c; + + for(i = 0; mechtable[i].name; i++) { + if(maxlen >= mechtable[i].len && + !memcmp(ptr, mechtable[i].name, mechtable[i].len)) { + if(len) + *len = mechtable[i].len; + + if(maxlen == mechtable[i].len) + return mechtable[i].bit; + + c = ptr[mechtable[i].len]; + if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_') + return mechtable[i].bit; + } + } + + return 0; +} + +/* + * Curl_sasl_parse_url_auth_option() + * + * Parse the URL login options. + */ +CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, + const char *value, size_t len) +{ + CURLcode result = CURLE_OK; + unsigned int mechbit; + size_t mechlen; + + if(!len) + return CURLE_URL_MALFORMAT; + + if(sasl->resetprefs) { + sasl->resetprefs = FALSE; + sasl->prefmech = SASL_AUTH_NONE; + } + + if(strnequal(value, "*", len)) + sasl->prefmech = SASL_AUTH_DEFAULT; + else if((mechbit = Curl_sasl_decode_mech(value, len, &mechlen)) && + mechlen == len) + sasl->prefmech |= mechbit; + else + result = CURLE_URL_MALFORMAT; + + return result; +} + +/* + * Curl_sasl_init() + * + * Initializes the SASL structure. + */ +void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params) +{ + sasl->params = params; /* Set protocol dependent parameters */ + sasl->state = SASL_STOP; /* Not yet running */ + sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ + sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */ + sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */ + sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ + sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ + sasl->force_ir = FALSE; /* Respect external option */ +} + +/* + * state() + * + * This is the ONLY way to change SASL state! + */ +static void state(struct SASL *sasl, struct connectdata *conn, + saslstate newstate) +{ +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[]={ + "STOP", + "PLAIN", + "LOGIN", + "LOGIN_PASSWD", + "EXTERNAL", + "CRAMMD5", + "DIGESTMD5", + "DIGESTMD5_RESP", + "NTLM", + "NTLM_TYPE2MSG", + "GSSAPI", + "GSSAPI_TOKEN", + "GSSAPI_NO_DATA", + "XOAUTH2", + "CANCEL", + "FINAL", + /* LAST */ + }; + + if(sasl->state != newstate) + infof(conn->data, "SASL %p state change from %s to %s\n", + (void *)sasl, names[sasl->state], names[newstate]); +#else + (void) conn; +#endif + + sasl->state = newstate; +} + +/* + * Curl_sasl_can_authenticate() + * + * Check if we have enough auth data and capabilities to authenticate. + */ +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn) +{ + /* Have credentials been provided? */ + if(conn->bits.user_passwd) + return TRUE; + + /* EXTERNAL can authenticate without a user name and/or password */ + if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) + return TRUE; + + return FALSE; +} + +/* + * Curl_sasl_start() + * + * Calculate the required login details for SASL authentication. + */ +CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, + bool force_ir, saslprogress *progress) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + unsigned int enabledmechs; + const char *mech = NULL; + char *resp = NULL; + size_t len = 0; + saslstate state1 = SASL_STOP; + saslstate state2 = SASL_FINAL; + + sasl->force_ir = force_ir; /* Latch for future use */ + sasl->authused = 0; /* No mechanism used yet */ + enabledmechs = sasl->authmechs & sasl->prefmech; + *progress = SASL_IDLE; + + /* Calculate the supported authentication mechanism, by decreasing order of + security, as well as the initial response where appropriate */ + if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { + mech = SASL_MECH_STRING_EXTERNAL; + state1 = SASL_EXTERNAL; + sasl->authused = SASL_MECH_EXTERNAL; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_external_message(data, conn->user, &resp, &len); + } + else if(conn->bits.user_passwd) { +#if defined(USE_KERBEROS5) + if(enabledmechs & SASL_MECH_GSSAPI) { + sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ + mech = SASL_MECH_STRING_GSSAPI; + state1 = SASL_GSSAPI; + state2 = SASL_GSSAPI_TOKEN; + sasl->authused = SASL_MECH_GSSAPI; + + if(force_ir || data->set.sasl_ir) + result = Curl_sasl_create_gssapi_user_message(data, conn->user, + conn->passwd, + sasl->params->service, + sasl->mutual_auth, + NULL, &conn->krb5, + &resp, &len); + } + else +#endif +#ifndef CURL_DISABLE_CRYPTO_AUTH + if(enabledmechs & SASL_MECH_DIGEST_MD5) { + mech = SASL_MECH_STRING_DIGEST_MD5; + state1 = SASL_DIGESTMD5; + sasl->authused = SASL_MECH_DIGEST_MD5; + } + else if(enabledmechs & SASL_MECH_CRAM_MD5) { + mech = SASL_MECH_STRING_CRAM_MD5; + state1 = SASL_CRAMMD5; + sasl->authused = SASL_MECH_CRAM_MD5; + } + else +#endif +#ifdef USE_NTLM + if(enabledmechs & SASL_MECH_NTLM) { + mech = SASL_MECH_STRING_NTLM; + state1 = SASL_NTLM; + state2 = SASL_NTLM_TYPE2MSG; + sasl->authused = SASL_MECH_NTLM; + + if(force_ir || data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, &resp, &len); + } + else +#endif + if((enabledmechs & SASL_MECH_XOAUTH2) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = SASL_XOAUTH2; + sasl->authused = SASL_MECH_XOAUTH2; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_xoauth2_message(data, conn->user, + conn->xoauth2_bearer, + &resp, &len); + } + else if(enabledmechs & SASL_MECH_LOGIN) { + mech = SASL_MECH_STRING_LOGIN; + state1 = SASL_LOGIN; + state2 = SASL_LOGIN_PASSWD; + sasl->authused = SASL_MECH_LOGIN; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_login_message(data, conn->user, &resp, &len); + } + else if(enabledmechs & SASL_MECH_PLAIN) { + mech = SASL_MECH_STRING_PLAIN; + state1 = SASL_PLAIN; + sasl->authused = SASL_MECH_PLAIN; + + if(force_ir || data->set.sasl_ir) + result = sasl_create_plain_message(data, conn->user, conn->passwd, + &resp, &len); + } + } + + if(!result) { + if(resp && sasl->params->maxirlen && + strlen(mech) + len > sasl->params->maxirlen) { + free(resp); + resp = NULL; + } + + if(mech) { + result = sasl->params->sendauth(conn, mech, resp); + if(!result) { + *progress = SASL_INPROGRESS; + state(sasl, conn, resp? state2: state1); + } + } + } + + free(resp); + + return result; +} + +/* + * Curl_sasl_continue() + * + * Continue the authentication. + */ +CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, + int code, saslprogress *progress) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + saslstate newstate = SASL_FINAL; + char *resp = NULL; +#if !defined(CURL_DISABLE_CRYPTO_AUTH) + char *serverdata; + char *chlg = NULL; + size_t chlglen = 0; +#endif + size_t len = 0; + + *progress = SASL_INPROGRESS; + + if(sasl->state == SASL_FINAL) { + if(code != sasl->params->finalcode) + result = CURLE_LOGIN_DENIED; + *progress = SASL_DONE; + state(sasl, conn, SASL_STOP); + return result; + } + + if(sasl->state != SASL_CANCEL && code != sasl->params->contcode) { + *progress = SASL_DONE; + state(sasl, conn, SASL_STOP); + return CURLE_LOGIN_DENIED; + } + + switch(sasl->state) { + case SASL_STOP: + *progress = SASL_DONE; + return result; + case SASL_PLAIN: + result = sasl_create_plain_message(data, conn->user, conn->passwd, &resp, + &len); + break; + case SASL_LOGIN: + result = sasl_create_login_message(data, conn->user, &resp, &len); + newstate = SASL_LOGIN_PASSWD; + break; + case SASL_LOGIN_PASSWD: + result = sasl_create_login_message(data, conn->passwd, &resp, &len); + break; + case SASL_EXTERNAL: + result = sasl_create_external_message(data, conn->user, &resp, &len); + break; + +#ifndef CURL_DISABLE_CRYPTO_AUTH + case SASL_CRAMMD5: + sasl->params->getmessage(data->state.buffer, &serverdata); + result = sasl_decode_cram_md5_message(serverdata, &chlg, &chlglen); + if(!result) + result = sasl_create_cram_md5_message(data, chlg, conn->user, + conn->passwd, &resp, &len); + free(chlg); + break; + case SASL_DIGESTMD5: + sasl->params->getmessage(data->state.buffer, &serverdata); + result = Curl_sasl_create_digest_md5_message(data, serverdata, + conn->user, conn->passwd, + sasl->params->service, + &resp, &len); + newstate = SASL_DIGESTMD5_RESP; + break; + case SASL_DIGESTMD5_RESP: + if(!(resp = strdup(""))) + result = CURLE_OUT_OF_MEMORY; + break; +#endif + +#ifdef USE_NTLM + case SASL_NTLM: + /* Create the type-1 message */ + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, &resp, &len); + newstate = SASL_NTLM_TYPE2MSG; + break; + case SASL_NTLM_TYPE2MSG: + /* Decode the type-2 message */ + sasl->params->getmessage(data->state.buffer, &serverdata); + result = Curl_sasl_decode_ntlm_type2_message(data, serverdata, + &conn->ntlm); + if(!result) + result = Curl_sasl_create_ntlm_type3_message(data, conn->user, + conn->passwd, &conn->ntlm, + &resp, &len); + break; +#endif + +#if defined(USE_KERBEROS5) + case SASL_GSSAPI: + result = Curl_sasl_create_gssapi_user_message(data, conn->user, + conn->passwd, + sasl->params->service, + sasl->mutual_auth, NULL, + &conn->krb5, + &resp, &len); + newstate = SASL_GSSAPI_TOKEN; + break; + case SASL_GSSAPI_TOKEN: + sasl->params->getmessage(data->state.buffer, &serverdata); + if(sasl->mutual_auth) { + /* Decode the user token challenge and create the optional response + message */ + result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL, + sasl->mutual_auth, + serverdata, &conn->krb5, + &resp, &len); + newstate = SASL_GSSAPI_NO_DATA; + } + else + /* Decode the security challenge and create the response message */ + result = Curl_sasl_create_gssapi_security_message(data, serverdata, + &conn->krb5, + &resp, &len); + break; + case SASL_GSSAPI_NO_DATA: + sasl->params->getmessage(data->state.buffer, &serverdata); + /* Decode the security challenge and create the response message */ + result = Curl_sasl_create_gssapi_security_message(data, serverdata, + &conn->krb5, + &resp, &len); + break; +#endif + + case SASL_XOAUTH2: + /* Create the authorisation message */ + result = sasl_create_xoauth2_message(data, conn->user, + conn->xoauth2_bearer, &resp, &len); + break; + case SASL_CANCEL: + /* Remove the offending mechanism from the supported list */ + sasl->authmechs ^= sasl->authused; + + /* Start an alternative SASL authentication */ + result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress); + newstate = sasl->state; /* Use state from Curl_sasl_start() */ + break; + default: + failf(data, "Unsupported SASL authentication mechanism"); + result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ + break; + } + + switch(result) { + case CURLE_BAD_CONTENT_ENCODING: + /* Cancel dialog */ + result = sasl->params->sendcont(conn, "*"); + newstate = SASL_CANCEL; + break; + case CURLE_OK: + if(resp) + result = sasl->params->sendcont(conn, resp); + break; + default: + newstate = SASL_STOP; /* Stop on error */ + *progress = SASL_DONE; + break; + } + + free(resp); + + state(sasl, conn, newstate); + + return result; +} diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index e56fa1a..117d60e 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,16 +26,19 @@ struct SessionHandle; struct connectdata; + +#if !defined(CURL_DISABLE_CRYPTO_AUTH) +struct digestdata; +#endif + +#if defined(USE_NTLM) struct ntlmdata; +#endif -#if defined(USE_WINDOWS_SSPI) +#if defined(USE_KERBEROS5) struct kerberos5data; #endif -/* Authentication mechanism values */ -#define SASL_AUTH_NONE 0 -#define SASL_AUTH_ANY ~0U - /* Authentication mechanism flags */ #define SASL_MECH_LOGIN (1 << 0) #define SASL_MECH_PLAIN (1 << 1) @@ -46,6 +49,12 @@ struct kerberos5data; #define SASL_MECH_NTLM (1 << 6) #define SASL_MECH_XOAUTH2 (1 << 7) +/* Authentication mechanism values */ +#define SASL_AUTH_NONE 0 +#define SASL_AUTH_ANY ~0U +#define SASL_AUTH_DEFAULT (SASL_AUTH_ANY & \ + ~(SASL_MECH_EXTERNAL | SASL_MECH_XOAUTH2)) + /* Authentication mechanism strings */ #define SASL_MECH_STRING_LOGIN "LOGIN" #define SASL_MECH_STRING_PLAIN "PLAIN" @@ -56,6 +65,70 @@ struct kerberos5data; #define SASL_MECH_STRING_NTLM "NTLM" #define SASL_MECH_STRING_XOAUTH2 "XOAUTH2" +#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#define DIGEST_MAX_VALUE_LENGTH 256 +#define DIGEST_MAX_CONTENT_LENGTH 1024 +#endif + +enum { + CURLDIGESTALGO_MD5, + CURLDIGESTALGO_MD5SESS +}; + +/* SASL machine states */ +typedef enum { + SASL_STOP, + SASL_PLAIN, + SASL_LOGIN, + SASL_LOGIN_PASSWD, + SASL_EXTERNAL, + SASL_CRAMMD5, + SASL_DIGESTMD5, + SASL_DIGESTMD5_RESP, + SASL_NTLM, + SASL_NTLM_TYPE2MSG, + SASL_GSSAPI, + SASL_GSSAPI_TOKEN, + SASL_GSSAPI_NO_DATA, + SASL_XOAUTH2, + SASL_CANCEL, + SASL_FINAL +} saslstate; + +/* Progress indicator */ +typedef enum { + SASL_IDLE, + SASL_INPROGRESS, + SASL_DONE +} saslprogress; + +/* Protocol dependent SASL parameters */ +struct SASLproto { + const char *service; /* The service name */ + int contcode; /* Code to receive when continuation is expected */ + int finalcode; /* Code to receive upon authentication success */ + size_t maxirlen; /* Maximum initial response length */ + CURLcode (*sendauth)(struct connectdata *conn, + const char *mech, const char *ir); + /* Send authentication command */ + CURLcode (*sendcont)(struct connectdata *conn, const char *contauth); + /* Send authentication continuation */ + void (*getmessage)(char *buffer, char **outptr); + /* Get SASL response message */ +}; + +/* Per-connection parameters */ +struct SASL { + const struct SASLproto *params; /* Protocol dependent parameters */ + saslstate state; /* Current machine state */ + unsigned int authmechs; /* Accepted authentication mechanisms */ + unsigned int prefmech; /* Preferred authentication mechanism */ + unsigned int authused; /* Auth mechanism used for the connection */ + bool resetprefs; /* For URL auth option parsing. */ + bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ + bool force_ir; /* Protocol always supports initial response */ +}; + /* This is used to test whether the line starts with the given mechanism */ #define sasl_mech_equal(line, wordlen, mech) \ (wordlen == (sizeof(mech) - 1) / sizeof(char) && \ @@ -68,29 +141,15 @@ char *Curl_sasl_build_spn(const char *service, const char *instance); TCHAR *Curl_sasl_build_spn(const char *service, const char *instance); #endif -/* This is used to generate a base64 encoded PLAIN authentication message */ -CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - char **outptr, size_t *outlen); +/* This is used to extract the realm from a challenge message */ +int Curl_sasl_digest_get_pair(const char *str, char *value, char *content, + const char **endptr); -/* This is used to generate a base64 encoded LOGIN authentication message - containing either the user name or password details */ -CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, - const char *valuep, char **outptr, - size_t *outlen); +#if defined(HAVE_GSSAPI) +char *Curl_sasl_build_gssapi_spn(const char *service, const char *host); +#endif #ifndef CURL_DISABLE_CRYPTO_AUTH -/* This is used to decode a base64 encoded CRAM-MD5 challange message */ -CURLcode Curl_sasl_decode_cram_md5_message(const char *chlg64, char **outptr, - size_t *outlen); - -/* This is used to generate a base64 encoded CRAM-MD5 response message */ -CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, - const char *chlg, - const char *user, - const char *passwdp, - char **outptr, size_t *outlen); /* This is used to generate a base64 encoded DIGEST-MD5 response message */ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, @@ -99,6 +158,22 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, const char *passwdp, const char *service, char **outptr, size_t *outlen); + +/* This is used to decode a HTTP DIGEST challenge message */ +CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, + struct digestdata *digest); + +/* This is used to generate a HTTP DIGEST response message */ +CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uri, + struct digestdata *digest, + char **outptr, size_t *outlen); + +/* This is used to clean up the digest specific data */ +void Curl_sasl_digest_cleanup(struct digestdata *digest); #endif #ifdef USE_NTLM @@ -121,9 +196,12 @@ CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, struct ntlmdata *ntlm, char **outptr, size_t *outlen); +/* This is used to clean up the ntlm specific data */ +void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm); + #endif /* USE_NTLM */ -#if defined(USE_WINDOWS_SSPI) +#if defined(USE_KERBEROS5) /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token message */ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, @@ -142,17 +220,35 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, struct kerberos5data *krb5, char **outptr, size_t *outlen); -#endif -/* This is used to generate a base64 encoded XOAUTH2 authentication message - containing the user name and bearer token */ -CURLcode Curl_sasl_create_xoauth2_message(struct SessionHandle *data, - const char *user, - const char *bearer, - char **outptr, size_t *outlen); +/* This is used to clean up the gssapi specific data */ +void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); +#endif /* USE_KERBEROS5 */ /* This is used to cleanup any libraries or curl modules used by the sasl functions */ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused); +/* Convert a mechanism name to a token */ +unsigned int Curl_sasl_decode_mech(const char *ptr, + size_t maxlen, size_t *len); + +/* Parse the URL login options */ +CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, + const char *value, size_t len); + +/* Initializes an SASL structure */ +void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params); + +/* Check if we have enough auth data and capabilities to authenticate */ +bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn); + +/* Calculate the required login details for SASL authentication */ +CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, + bool force_ir, saslprogress *progress); + +/* Continue an SASL authentication */ +CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, + int code, saslprogress *progress); + #endif /* HEADER_CURL_SASL_H */ diff --git a/lib/curl_sasl_gssapi.c b/lib/curl_sasl_gssapi.c new file mode 100644 index 0000000..3c6f3ce --- /dev/null +++ b/lib/curl_sasl_gssapi.c @@ -0,0 +1,392 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2014 - 2015, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.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. + * + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) + +#include <curl/curl.h> + +#include "curl_sasl.h" +#include "urldata.h" +#include "curl_base64.h" +#include "curl_gssapi.h" +#include "sendf.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* +* Curl_sasl_build_gssapi_spn() +* +* This is used to build a SPN string in the format service@host. +* +* Parameters: +* +* serivce [in] - The service type such as www, smtp, pop or imap. +* host [in] - The host name or realm. +* +* Returns a pointer to the newly allocated SPN. +*/ +char *Curl_sasl_build_gssapi_spn(const char *service, const char *host) +{ + /* Generate and return our SPN */ + return aprintf("%s@%s", service, host); +} + +/* + * Curl_sasl_create_gssapi_user_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) user token + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The user name. + * passdwp [in] - The user's password. + * service [in] - The service type such as www, smtp, pop or imap. + * mutual_auth [in] - Flag specifing whether or not mutual authentication + * is enabled. + * chlg64 [in] - Pointer to the optional base64 encoded challenge + * message. + * krb5 [in/out] - The gssapi data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const char *service, + const bool mutual_auth, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, size_t *outlen) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + unsigned char *chlg = NULL; + OM_uint32 gss_status; + OM_uint32 gss_major_status; + OM_uint32 gss_minor_status; + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + + (void) userp; + (void) passwdp; + + if(krb5->context == GSS_C_NO_CONTEXT) { + /* Generate our SPN */ + char *spn = Curl_sasl_build_gssapi_spn(service, + data->easy_conn->host.name); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Populate the SPN structure */ + spn_token.value = spn; + spn_token.length = strlen(spn); + + /* Import the SPN */ + gss_major_status = gss_import_name(&gss_minor_status, &spn_token, + GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_import_name() failed: "); + + free(spn); + + return CURLE_OUT_OF_MEMORY; + } + + free(spn); + } + else { + /* Decode the base-64 encoded challenge message */ + if(strlen(chlg64) && *chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty challenge message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + input_token.value = chlg; + input_token.length = chlglen; + } + + gss_major_status = Curl_gss_init_sec_context(data, + &gss_minor_status, + &krb5->context, + krb5->spn, + &Curl_krb5_mech_oid, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + &output_token, + mutual_auth, + NULL); + + free(input_token.value); + + if(GSS_ERROR(gss_major_status)) { + if(output_token.value) + gss_release_buffer(&gss_status, &output_token); + + Curl_gss_log_error(data, gss_minor_status, + "gss_init_sec_context() failed: "); + + return CURLE_RECV_ERROR; + } + + if(output_token.value && output_token.length) { + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) output_token.value, + output_token.length, outptr, outlen); + + gss_release_buffer(&gss_status, &output_token); + } + + return result; +} + +/* + * Curl_sasl_create_gssapi_security_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) security + * token message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * chlg64 [in] - Pointer to the optional base64 encoded challenge message. + * krb5 [in/out] - The gssapi data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, + size_t *outlen) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + size_t messagelen = 0; + unsigned char *chlg = NULL; + unsigned char *message = NULL; + OM_uint32 gss_status; + OM_uint32 gss_major_status; + OM_uint32 gss_minor_status; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + unsigned int indata = 0; + unsigned int outdata = 0; + gss_qop_t qop = GSS_C_QOP_DEFAULT; + unsigned int sec_layer = 0; + unsigned int max_size = 0; + gss_name_t username = GSS_C_NO_NAME; + gss_buffer_desc username_token; + + /* Decode the base-64 encoded input message */ + if(strlen(chlg64) && *chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty security message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Get the fully qualified username back from the context */ + gss_major_status = gss_inquire_context(&gss_minor_status, krb5->context, + &username, NULL, NULL, NULL, NULL, + NULL, NULL); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, + "gss_inquire_context() failed: "); + + free(chlg); + + return CURLE_OUT_OF_MEMORY; + } + + /* Convert the username from internal format to a displayable token */ + gss_major_status = gss_display_name(&gss_minor_status, username, + &username_token, NULL); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_display_name() failed: "); + + free(chlg); + + return CURLE_OUT_OF_MEMORY; + } + + /* Setup the challenge "input" security buffer */ + input_token.value = chlg; + input_token.length = chlglen; + + /* Decrypt the inbound challenge and obtain the qop */ + gss_major_status = gss_unwrap(&gss_minor_status, krb5->context, &input_token, + &output_token, NULL, &qop); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_unwrap() failed: "); + + gss_release_buffer(&gss_status, &username_token); + free(chlg); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ + if(output_token.length != 4) { + infof(data, "GSSAPI handshake failure (invalid security data)\n"); + + gss_release_buffer(&gss_status, &username_token); + free(chlg); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Copy the data out and free the challenge as it is not required anymore */ + memcpy(&indata, output_token.value, 4); + gss_release_buffer(&gss_status, &output_token); + free(chlg); + + /* Extract the security layer */ + sec_layer = indata & 0x000000FF; + if(!(sec_layer & GSSAUTH_P_NONE)) { + infof(data, "GSSAPI handshake failure (invalid security layer)\n"); + + gss_release_buffer(&gss_status, &username_token); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Extract the maximum message size the server can receive */ + max_size = ntohl(indata & 0xFFFFFF00); + if(max_size > 0) { + /* The server has told us it supports a maximum receive buffer, however, as + we don't require one unless we are encrypting data, we tell the server + our receive buffer is zero. */ + max_size = 0; + } + + /* Allocate our message */ + messagelen = sizeof(outdata) + username_token.length + 1; + message = malloc(messagelen); + if(!message) { + gss_release_buffer(&gss_status, &username_token); + + return CURLE_OUT_OF_MEMORY; + } + + /* Populate the message with the security layer, client supported receive + message size and authorization identity including the 0x00 based + terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization + identity is not terminated with the zero-valued (%x00) octet." it seems + necessary to include it. */ + outdata = htonl(max_size) | sec_layer; + memcpy(message, &outdata, sizeof(outdata)); + memcpy(message + sizeof(outdata), username_token.value, + username_token.length); + message[messagelen - 1] = '\0'; + + /* Free the username token as it is not required anymore */ + gss_release_buffer(&gss_status, &username_token); + + /* Setup the "authentication data" security buffer */ + input_token.value = message; + input_token.length = messagelen; + + /* Encrypt the data */ + gss_major_status = gss_wrap(&gss_minor_status, krb5->context, 0, + GSS_C_QOP_DEFAULT, &input_token, NULL, + &output_token); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_wrap() failed: "); + + free(message); + + return CURLE_OUT_OF_MEMORY; + } + + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) output_token.value, + output_token.length, outptr, outlen); + + /* Free the output buffer */ + gss_release_buffer(&gss_status, &output_token); + + /* Free the message buffer */ + free(message); + + return result; +} + +/* + * Curl_sasl_gssapi_cleanup() + * + * This is used to clean up the gssapi specific data. + * + * Parameters: + * + * krb5 [in/out] - The kerberos 5 data struct being cleaned up. + * + */ +void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) +{ + OM_uint32 minor_status; + + /* Free our security context */ + if(krb5->context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER); + krb5->context = GSS_C_NO_CONTEXT; + } + + /* Free the SPN */ + if(krb5->spn != GSS_C_NO_NAME) { + gss_release_name(&minor_status, &krb5->spn); + krb5->spn = GSS_C_NO_NAME; + } +} + +#endif /* HAVE_GSSAPI && USE_KERBEROS5 */ diff --git a/lib/curl_sasl_sspi.c b/lib/curl_sasl_sspi.c index df4da96..b149530 100644 --- a/lib/curl_sasl_sspi.c +++ b/lib/curl_sasl_sspi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * + * Copyright (C) 2014 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2014, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -19,6 +19,7 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * + * RFC2617 Basic and Digest Access Authentication * RFC2831 DIGEST-MD5 authentication * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism @@ -35,17 +36,16 @@ #include "urldata.h" #include "curl_base64.h" #include "warnless.h" -#include "curl_memory.h" #include "curl_multibyte.h" +#include "sendf.h" +#include "strdup.h" +#include "curl_printf.h" +#include "rawstr.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); - /* * Curl_sasl_build_spn() * @@ -54,7 +54,7 @@ void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); * Parameters: * * serivce [in] - The service type such as www, smtp, pop or imap. - * instance [in] - The instance name such as the host nme or realm. + * host [in] - The host name or realm. * * Returns a pointer to the newly allocated SPN. */ @@ -79,14 +79,13 @@ TCHAR *Curl_sasl_build_spn(const char *service, const char *host) /* Allocate our TCHAR based SPN */ tchar_spn = Curl_convert_UTF8_to_tchar(utf8_spn); if(!tchar_spn) { - Curl_safefree(utf8_spn); + free(utf8_spn); return NULL; } /* Release the UTF8 variant when operating with Unicode */ - if(utf8_spn != tchar_spn) - Curl_safefree(utf8_spn); + Curl_unicodefree(utf8_spn); /* Return our newly allocated SPN */ return tchar_spn; @@ -102,8 +101,8 @@ TCHAR *Curl_sasl_build_spn(const char *service, const char *host) * Parameters: * * data [in] - The session handle. - * chlg64 [in] - Pointer to the base64 encoded challenge message. - * userp [in] - The user name. + * chlg64 [in] - The base64 encoded challenge message. + * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * outptr [in/out] - The address where a pointer to newly allocated memory @@ -122,57 +121,54 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, CURLcode result = CURLE_OK; TCHAR *spn = NULL; size_t chlglen = 0; - size_t resp_max = 0; - unsigned char *chlg = NULL; - unsigned char *resp = NULL; - CredHandle handle; - CtxtHandle ctx; + size_t token_max = 0; + unsigned char *input_token = NULL; + unsigned char *output_token = NULL; + CredHandle credentials; + CtxtHandle context; PSecPkgInfo SecurityPackage; SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; SecBuffer chlg_buf; SecBuffer resp_buf; SecBufferDesc chlg_desc; SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; - TimeStamp tsDummy; /* For Windows 9x compatibility of SSPI calls */ + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ /* Decode the base-64 encoded challenge message */ if(strlen(chlg64) && *chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); + result = Curl_base64_decode(chlg64, &input_token, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ - if(!chlg) - return CURLE_BAD_CONTENT_ENCODING; + if(!input_token) { + infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n"); - /* Ensure we have some login credientials as DigestSSP cannot use the current - Windows user like NTLMSSP can */ - if(!userp || !*userp) { - Curl_safefree(chlg); - return CURLE_LOGIN_DENIED; + return CURLE_BAD_CONTENT_ENCODING; } /* Query the security package for DigestSSP */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("WDigest"), + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), &SecurityPackage); if(status != SEC_E_OK) { - Curl_safefree(chlg); + free(input_token); return CURLE_NOT_BUILT_IN; } - resp_max = SecurityPackage->cbMaxToken; + token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ s_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ - resp = malloc(resp_max); - if(!resp) { - Curl_safefree(chlg); + output_token = malloc(token_max); + if(!output_token) { + free(input_token); return CURLE_OUT_OF_MEMORY; } @@ -180,36 +176,44 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, /* Generate our SPN */ spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); if(!spn) { - Curl_safefree(resp); - Curl_safefree(chlg); + free(output_token); + free(input_token); return CURLE_OUT_OF_MEMORY; } - /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &identity); - if(result) { - Curl_safefree(spn); - Curl_safefree(resp); - Curl_safefree(chlg); + if(userp && *userp) { + /* Populate our identity structure */ + result = Curl_create_sspi_identity(userp, passwdp, &identity); + if(result) { + free(spn); + free(output_token); + free(input_token); + + return result; + } - return result; + /* Allow proper cleanup of the identity structure */ + p_identity = &identity; } + else + /* Use the current Windows user */ + p_identity = NULL; - /* Acquire our credientials handle */ + /* Acquire our credentials handle */ status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT("WDigest"), + (TCHAR *) TEXT(SP_NAME_DIGEST), SECPKG_CRED_OUTBOUND, NULL, - &identity, NULL, NULL, - &handle, &tsDummy); + p_identity, NULL, NULL, + &credentials, &expiry); if(status != SEC_E_OK) { - Curl_sspi_free_identity(&identity); - Curl_safefree(spn); - Curl_safefree(resp); - Curl_safefree(chlg); + Curl_sspi_free_identity(p_identity); + free(spn); + free(output_token); + free(input_token); - return CURLE_OUT_OF_MEMORY; + return CURLE_LOGIN_DENIED; } /* Setup the challenge "input" security buffer */ @@ -217,7 +221,7 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, chlg_desc.cBuffers = 1; chlg_desc.pBuffers = &chlg_buf; chlg_buf.BufferType = SECBUFFER_TOKEN; - chlg_buf.pvBuffer = chlg; + chlg_buf.pvBuffer = input_token; chlg_buf.cbBuffer = curlx_uztoul(chlglen); /* Setup the response "output" security buffer */ @@ -225,52 +229,611 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, resp_desc.cBuffers = 1; resp_desc.pBuffers = &resp_buf; resp_buf.BufferType = SECBUFFER_TOKEN; - resp_buf.pvBuffer = resp; - resp_buf.cbBuffer = curlx_uztoul(resp_max); - - /* Generate our challenge-response message */ - status = s_pSecFn->InitializeSecurityContext(&handle, NULL, spn, 0, 0, 0, - &chlg_desc, 0, &ctx, - &resp_desc, &attrs, &tsDummy); - - if(status == SEC_I_COMPLETE_AND_CONTINUE || - status == SEC_I_CONTINUE_NEEDED) - s_pSecFn->CompleteAuthToken(&handle, &resp_desc); - else if(status != SEC_E_OK) { - s_pSecFn->FreeCredentialsHandle(&handle); - Curl_sspi_free_identity(&identity); - Curl_safefree(spn); - Curl_safefree(resp); - Curl_safefree(chlg); + resp_buf.pvBuffer = output_token; + resp_buf.cbBuffer = curlx_uztoul(token_max); + + /* Generate our response message */ + status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, + 0, 0, 0, &chlg_desc, 0, + &context, &resp_desc, &attrs, + &expiry); + + if(status == SEC_I_COMPLETE_NEEDED || + status == SEC_I_COMPLETE_AND_CONTINUE) + s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); + else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { + s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_sspi_free_identity(p_identity); + free(spn); + free(output_token); + free(input_token); return CURLE_RECV_ERROR; } /* Base64 encode the response */ - result = Curl_base64_encode(data, (char *)resp, resp_buf.cbBuffer, outptr, - outlen); + result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer, + outptr, outlen); /* Free our handles */ - s_pSecFn->DeleteSecurityContext(&ctx); - s_pSecFn->FreeCredentialsHandle(&handle); + s_pSecFn->DeleteSecurityContext(&context); + s_pSecFn->FreeCredentialsHandle(&credentials); /* Free the identity structure */ - Curl_sspi_free_identity(&identity); + Curl_sspi_free_identity(p_identity); /* Free the SPN */ - Curl_safefree(spn); + free(spn); /* Free the response buffer */ - Curl_safefree(resp); + free(output_token); - /* Free the decoeded challenge message */ - Curl_safefree(chlg); + /* Free the decoded challenge message */ + free(input_token); return result; } +/* +* Curl_override_sspi_http_realm() +* +* This is used to populate the domain in a SSPI identity structure +* The realm is extracted from the challenge message and used as the +* domain if it is not already explicitly set. +* +* Parameters: +* +* chlg [in] - The challenge message. +* identity [in/out] - The identity structure. +* +* Returns CURLE_OK on success. +*/ +CURLcode Curl_override_sspi_http_realm(const char *chlg, + SEC_WINNT_AUTH_IDENTITY *identity) +{ + xcharp_u domain, dup_domain; + + /* If domain is blank or unset, check challenge message for realm */ + if(!identity->Domain || !identity->DomainLength) { + for(;;) { + char value[DIGEST_MAX_VALUE_LENGTH]; + char content[DIGEST_MAX_CONTENT_LENGTH]; + + /* Pass all additional spaces here */ + while(*chlg && ISSPACE(*chlg)) + chlg++; + + /* Extract a value=content pair */ + if(!Curl_sasl_digest_get_pair(chlg, value, content, &chlg)) { + if(Curl_raw_equal(value, "realm")) { + + /* Setup identity's domain and length */ + domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *)content); + if(!domain.tchar_ptr) + return CURLE_OUT_OF_MEMORY; + dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr); + if(!dup_domain.tchar_ptr) { + Curl_unicodefree(domain.tchar_ptr); + return CURLE_OUT_OF_MEMORY; + } + identity->Domain = dup_domain.tbyte_ptr; + identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr)); + dup_domain.tchar_ptr = NULL; + + Curl_unicodefree(domain.tchar_ptr); + } + else { + /* unknown specifier, ignore it! */ + } + } + else + break; /* we're done here */ + + /* Pass all additional spaces here */ + while(*chlg && ISSPACE(*chlg)) + chlg++; + + /* Allow the list to be comma-separated */ + if(',' == *chlg) + chlg++; + } + } + + return CURLE_OK; +} + +/* + * Curl_sasl_decode_digest_http_message() + * + * This is used to decode a HTTP DIGEST challenge message into the seperate + * attributes. + * + * Parameters: + * + * chlg [in] - The challenge message. + * digest [in/out] - The digest data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, + struct digestdata *digest) +{ + size_t chlglen = strlen(chlg); + + /* We had an input token before and we got another one now. This means we + provided bad credentials in the previous request. */ + if(digest->input_token) + return CURLE_BAD_CONTENT_ENCODING; + + /* Simply store the challenge for use later */ + digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen); + if(!digest->input_token) + return CURLE_OUT_OF_MEMORY; + + digest->input_token_len = chlglen; + + return CURLE_OK; +} + +/* + * Curl_sasl_create_digest_http_message() + * + * This is used to generate a HTTP DIGEST response message ready for sending + * to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The user name in the format User or Domain\User. + * passdwp [in] - The user's password. + * request [in] - The HTTP request. + * uripath [in] - The path of the HTTP uri. + * digest [in/out] - The digest data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen) +{ + size_t token_max; + CredHandle credentials; + CtxtHandle context; + char *resp; + BYTE *output_token; + PSecPkgInfo SecurityPackage; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + SecBuffer chlg_buf[3]; + SecBuffer resp_buf; + SecBufferDesc chlg_desc; + SecBufferDesc resp_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + (void) data; + + /* Query the security package for DigestSSP */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); + if(status != SEC_E_OK) + return CURLE_NOT_BUILT_IN; + + token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate the output buffer according to the max token size as indicated + by the security package */ + output_token = malloc(token_max); + if(!output_token) + return CURLE_OUT_OF_MEMORY; + + if(userp && *userp) { + /* Populate our identity structure */ + if(Curl_create_sspi_identity(userp, passwdp, &identity)) + return CURLE_OUT_OF_MEMORY; + + /* Populate our identity domain */ + if(Curl_override_sspi_http_realm((const char*)digest->input_token, + &identity)) + return CURLE_OUT_OF_MEMORY; + + /* Allow proper cleanup of the identity structure */ + p_identity = &identity; + } + else + /* Use the current Windows user */ + p_identity = NULL; + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) TEXT(SP_NAME_DIGEST), + SECPKG_CRED_OUTBOUND, NULL, + p_identity, NULL, NULL, + &credentials, &expiry); + if(status != SEC_E_OK) { + free(output_token); + + return CURLE_LOGIN_DENIED; + } + + /* Setup the challenge "input" security buffer if present */ + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 3; + chlg_desc.pBuffers = chlg_buf; + chlg_buf[0].BufferType = SECBUFFER_TOKEN; + chlg_buf[0].pvBuffer = digest->input_token; + chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len); + chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[1].pvBuffer = (void *)request; + chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); + chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; + chlg_buf[2].pvBuffer = NULL; + chlg_buf[2].cbBuffer = 0; + + /* Setup the response "output" security buffer */ + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = output_token; + resp_buf.cbBuffer = curlx_uztoul(token_max); + + /* Generate our reponse message */ + status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, + (TCHAR *) uripath, + ISC_REQ_USE_HTTP_STYLE, 0, 0, + &chlg_desc, 0, &context, + &resp_desc, &attrs, &expiry); + + if(status == SEC_I_COMPLETE_NEEDED || + status == SEC_I_COMPLETE_AND_CONTINUE) + s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); + else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { + s_pSecFn->FreeCredentialsHandle(&credentials); + + free(output_token); + + return CURLE_OUT_OF_MEMORY; + } + + resp = malloc(resp_buf.cbBuffer + 1); + if(!resp) { + s_pSecFn->DeleteSecurityContext(&context); + s_pSecFn->FreeCredentialsHandle(&credentials); + + free(output_token); + + return CURLE_OUT_OF_MEMORY; + } + + /* Copy the generated reponse */ + memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer); + resp[resp_buf.cbBuffer] = 0x00; + + /* Return the response */ + *outptr = resp; + *outlen = resp_buf.cbBuffer; + + /* Free our handles */ + s_pSecFn->DeleteSecurityContext(&context); + s_pSecFn->FreeCredentialsHandle(&credentials); + + /* Free the identity structure */ + Curl_sspi_free_identity(p_identity); + + /* Free the response buffer */ + free(output_token); + + return CURLE_OK; +} + +/* + * Curl_sasl_digest_cleanup() + * + * This is used to clean up the digest specific data. + * + * Parameters: + * + * digest [in/out] - The digest data struct being cleaned up. + * + */ +void Curl_sasl_digest_cleanup(struct digestdata *digest) +{ + /* Free the input token */ + Curl_safefree(digest->input_token); + + /* Reset any variables */ + digest->input_token_len = 0; +} #endif /* !CURL_DISABLE_CRYPTO_AUTH */ +#if defined USE_NTLM +/* +* Curl_sasl_create_ntlm_type1_message() +* +* This is used to generate an already encoded NTLM type-1 message ready for +* sending to the recipient. +* +* Parameters: +* +* userp [in] - The user name in the format User or Domain\User. +* passdwp [in] - The user's password. +* ntlm [in/out] - The ntlm data struct being used and modified. +* outptr [in/out] - The address where a pointer to newly allocated memory +* holding the result will be stored upon completion. +* outlen [out] - The length of the output message. +* +* Returns CURLE_OK on success. +*/ +CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + char **outptr, size_t *outlen) +{ + PSecPkgInfo SecurityPackage; + SecBuffer type_1_buf; + SecBufferDesc type_1_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + /* Clean up any former leftovers and initialise to defaults */ + Curl_sasl_ntlm_cleanup(ntlm); + + /* Query the security package for NTLM */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), + &SecurityPackage); + if(status != SEC_E_OK) + return CURLE_NOT_BUILT_IN; + + ntlm->token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate our output buffer */ + ntlm->output_token = malloc(ntlm->token_max); + if(!ntlm->output_token) + return CURLE_OUT_OF_MEMORY; + + if(userp && *userp) { + CURLcode result; + + /* Populate our identity structure */ + result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); + if(result) + return result; + + /* Allow proper cleanup of the identity structure */ + ntlm->p_identity = &ntlm->identity; + } + else + /* Use the current Windows user */ + ntlm->p_identity = NULL; + + /* Allocate our credentials handle */ + ntlm->credentials = malloc(sizeof(CredHandle)); + if(!ntlm->credentials) + return CURLE_OUT_OF_MEMORY; + + memset(ntlm->credentials, 0, sizeof(CredHandle)); + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) TEXT(SP_NAME_NTLM), + SECPKG_CRED_OUTBOUND, NULL, + ntlm->p_identity, NULL, NULL, + ntlm->credentials, &expiry); + if(status != SEC_E_OK) + return CURLE_LOGIN_DENIED; + + /* Allocate our new context handle */ + ntlm->context = malloc(sizeof(CtxtHandle)); + if(!ntlm->context) + return CURLE_OUT_OF_MEMORY; + + memset(ntlm->context, 0, sizeof(CtxtHandle)); + + /* Setup the type-1 "output" security buffer */ + type_1_desc.ulVersion = SECBUFFER_VERSION; + type_1_desc.cBuffers = 1; + type_1_desc.pBuffers = &type_1_buf; + type_1_buf.BufferType = SECBUFFER_TOKEN; + type_1_buf.pvBuffer = ntlm->output_token; + type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max); + + /* Generate our type-1 message */ + status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, + (TCHAR *) TEXT(""), + 0, 0, SECURITY_NETWORK_DREP, + NULL, 0, + ntlm->context, &type_1_desc, + &attrs, &expiry); + if(status == SEC_I_COMPLETE_NEEDED || + status == SEC_I_COMPLETE_AND_CONTINUE) + s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); + else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) + return CURLE_RECV_ERROR; + + /* Base64 encode the response */ + return Curl_base64_encode(NULL, (char *) ntlm->output_token, + type_1_buf.cbBuffer, outptr, outlen); +} + +/* +* Curl_sasl_decode_ntlm_type2_message() +* +* This is used to decode an already encoded NTLM type-2 message. +* +* Parameters: +* +* data [in] - The session handle. +* type2msg [in] - The base64 encoded type-2 message. +* ntlm [in/out] - The ntlm data struct being used and modified. +* +* Returns CURLE_OK on success. +*/ +CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data, + const char *type2msg, + struct ntlmdata *ntlm) +{ + CURLcode result = CURLE_OK; + unsigned char *type2 = NULL; + size_t type2_len = 0; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + /* Decode the base-64 encoded type-2 message */ + if(strlen(type2msg) && *type2msg != '=') { + result = Curl_base64_decode(type2msg, &type2, &type2_len); + if(result) + return result; + } + + /* Ensure we have a valid type-2 message */ + if(!type2) { + infof(data, "NTLM handshake failure (empty type-2 message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Simply store the challenge for use later */ + ntlm->input_token = type2; + ntlm->input_token_len = type2_len; + + return result; +} + +/* +* Curl_sasl_create_ntlm_type3_message() +* +* This is used to generate an already encoded NTLM type-3 message ready for +* sending to the recipient. +* +* Parameters: +* +* data [in] - The session handle. +* userp [in] - The user name in the format User or Domain\User. +* passdwp [in] - The user's password. +* ntlm [in/out] - The ntlm data struct being used and modified. +* outptr [in/out] - The address where a pointer to newly allocated memory +* holding the result will be stored upon completion. +* outlen [out] - The length of the output message. +* +* Returns CURLE_OK on success. +*/ +CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + struct ntlmdata *ntlm, + char **outptr, size_t *outlen) +{ + CURLcode result = CURLE_OK; + SecBuffer type_2_buf; + SecBuffer type_3_buf; + SecBufferDesc type_2_desc; + SecBufferDesc type_3_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + (void) passwdp; + (void) userp; + + /* Setup the type-2 "input" security buffer */ + type_2_desc.ulVersion = SECBUFFER_VERSION; + type_2_desc.cBuffers = 1; + type_2_desc.pBuffers = &type_2_buf; + type_2_buf.BufferType = SECBUFFER_TOKEN; + type_2_buf.pvBuffer = ntlm->input_token; + type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len); + + /* Setup the type-3 "output" security buffer */ + type_3_desc.ulVersion = SECBUFFER_VERSION; + type_3_desc.cBuffers = 1; + type_3_desc.pBuffers = &type_3_buf; + type_3_buf.BufferType = SECBUFFER_TOKEN; + type_3_buf.pvBuffer = ntlm->output_token; + type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max); + + /* Generate our type-3 message */ + status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, + ntlm->context, + (TCHAR *) TEXT(""), + 0, 0, SECURITY_NETWORK_DREP, + &type_2_desc, + 0, ntlm->context, + &type_3_desc, + &attrs, &expiry); + if(status != SEC_E_OK) { + infof(data, "NTLM handshake failure (type-3 message): Status=%x\n", + status); + + return CURLE_RECV_ERROR; + } + + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) ntlm->output_token, + type_3_buf.cbBuffer, outptr, outlen); + + Curl_sasl_ntlm_cleanup(ntlm); + + return result; +} + +/* + * Curl_sasl_ntlm_cleanup() + * + * This is used to clean up the ntlm specific data. + * + * Parameters: + * + * ntlm [in/out] - The ntlm data struct being cleaned up. + * + */ +void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm) +{ + /* Free our security context */ + if(ntlm->context) { + s_pSecFn->DeleteSecurityContext(ntlm->context); + free(ntlm->context); + ntlm->context = NULL; + } + + /* Free our credentials handle */ + if(ntlm->credentials) { + s_pSecFn->FreeCredentialsHandle(ntlm->credentials); + free(ntlm->credentials); + ntlm->credentials = NULL; + } + + /* Free our identity */ + Curl_sspi_free_identity(ntlm->p_identity); + ntlm->p_identity = NULL; + + /* Free the input and output tokens */ + Curl_safefree(ntlm->input_token); + Curl_safefree(ntlm->output_token); + + /* Reset any variables */ + ntlm->token_max = 0; +} +#endif /* USE_NTLM */ + +#if defined(USE_KERBEROS5) /* * Curl_sasl_create_gssapi_user_message() * @@ -280,13 +843,12 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, * Parameters: * * data [in] - The session handle. - * userp [in] - The user name. + * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * mutual_auth [in] - Flag specifing whether or not mutual authentication * is enabled. - * chlg64 [in] - Pointer to the optional base64 encoded challenge - * message. + * chlg64 [in] - The optional base64 encoded challenge message. * krb5 [in/out] - The gssapi data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. @@ -314,11 +876,12 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; - TimeStamp tsDummy; /* For Windows 9x compatibility of SSPI calls */ + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ if(!krb5->credentials) { /* Query the security package for Kerberos */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Kerberos"), + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_KERBEROS), &SecurityPackage); if(status != SEC_E_OK) { return CURLE_NOT_BUILT_IN; @@ -329,6 +892,11 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, /* Release the package buffer as it is not required anymore */ s_pSecFn->FreeContextBuffer(SecurityPackage); + /* Allocate our response buffer */ + krb5->output_token = malloc(krb5->token_max); + if(!krb5->output_token) + return CURLE_OUT_OF_MEMORY; + /* Generate our SPN */ krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); if(!krb5->spn) @@ -342,11 +910,6 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, /* Allow proper cleanup of the identity structure */ krb5->p_identity = &krb5->identity; - - /* Allocate our response buffer */ - krb5->output_token = malloc(krb5->token_max); - if(!krb5->output_token) - return CURLE_OUT_OF_MEMORY; } else /* Use the current Windows user */ @@ -359,14 +922,15 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, memset(krb5->credentials, 0, sizeof(CredHandle)); - /* Acquire our credientials handle */ + /* Acquire our credentials handle */ status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT("Kerberos"), + (TCHAR *) + TEXT(SP_NAME_KERBEROS), SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, - krb5->credentials, &tsDummy); + krb5->credentials, &expiry); if(status != SEC_E_OK) - return CURLE_OUT_OF_MEMORY; + return CURLE_LOGIN_DENIED; /* Allocate our new context handle */ krb5->context = malloc(sizeof(CtxtHandle)); @@ -384,8 +948,11 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, } /* Ensure we have a valid challenge message */ - if(!chlg) + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty challenge message)\n"); + return CURLE_BAD_CONTENT_ENCODING; + } /* Setup the challenge "input" security buffer */ chlg_desc.ulVersion = SECBUFFER_VERSION; @@ -414,10 +981,10 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, chlg ? &chlg_desc : NULL, 0, &context, &resp_desc, &attrs, - &tsDummy); + &expiry); if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { - Curl_safefree(chlg); + free(chlg); return CURLE_RECV_ERROR; } @@ -435,7 +1002,7 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, } /* Free the decoded challenge */ - Curl_safefree(chlg); + free(chlg); return result; } @@ -449,7 +1016,7 @@ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, * Parameters: * * data [in] - The session handle. - * chlg64 [in] - Pointer to the optional base64 encoded challenge message. + * chlg64 [in] - The optional base64 encoded challenge message. * krb5 [in/out] - The gssapi data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. @@ -485,8 +1052,7 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, SecPkgContext_Sizes sizes; SecPkgCredentials_Names names; SECURITY_STATUS status; - - /* TODO: Verify the unicodeness of this function */ + char *user_name; /* Decode the base-64 encoded input message */ if(strlen(chlg64) && *chlg64 != '=') { @@ -496,15 +1062,18 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, } /* Ensure we have a valid challenge message */ - if(!chlg) + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty security message)\n"); + return CURLE_BAD_CONTENT_ENCODING; + } /* Get our response size information */ status = s_pSecFn->QueryContextAttributes(krb5->context, SECPKG_ATTR_SIZES, &sizes); if(status != SEC_E_OK) { - Curl_safefree(chlg); + free(chlg); return CURLE_OUT_OF_MEMORY; } @@ -514,7 +1083,7 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, SECPKG_CRED_ATTR_NAMES, &names); if(status != SEC_E_OK) { - Curl_safefree(chlg); + free(chlg); return CURLE_RECV_ERROR; } @@ -530,30 +1099,34 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, input_buf[1].pvBuffer = NULL; input_buf[1].cbBuffer = 0; - /* Decrypt in the inbound challenge obtaining the qop */ + /* Decrypt the inbound challenge and obtain the qop */ status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); if(status != SEC_E_OK) { - Curl_safefree(chlg); + infof(data, "GSSAPI handshake failure (empty security message)\n"); + + free(chlg); return CURLE_BAD_CONTENT_ENCODING; } - /* Not 4 octets long to fail as per RFC4752 Section 3.1 */ + /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ if(input_buf[1].cbBuffer != 4) { - Curl_safefree(chlg); + infof(data, "GSSAPI handshake failure (invalid security data)\n"); + + free(chlg); return CURLE_BAD_CONTENT_ENCODING; } - /* Copy the data out into a coinput_bufnvenient variable and free the SSPI - allocated buffer as it is not required anymore */ + /* Copy the data out and free the challenge as it is not required anymore */ memcpy(&indata, input_buf[1].pvBuffer, 4); s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); + free(chlg); /* Extract the security layer */ sec_layer = indata & 0x000000FF; if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { - Curl_safefree(chlg); + infof(data, "GSSAPI handshake failure (invalid security layer)\n"); return CURLE_BAD_CONTENT_ENCODING; } @@ -562,27 +1135,30 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, max_size = ntohl(indata & 0xFFFFFF00); if(max_size > 0) { /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data we, tell the server + we don't require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } - outdata = htonl(max_size) | sec_layer; - /* Allocate the trailer */ trailer = malloc(sizes.cbSecurityTrailer); - if(!trailer) { - Curl_safefree(chlg); + if(!trailer) + return CURLE_OUT_OF_MEMORY; + + /* Convert the user name to UTF8 when operating with Unicode */ + user_name = Curl_convert_tchar_to_UTF8(names.sUserName); + if(!user_name) { + free(trailer); return CURLE_OUT_OF_MEMORY; } /* Allocate our message */ - messagelen = 4 + strlen(names.sUserName) + 1; + messagelen = sizeof(outdata) + strlen(user_name) + 1; message = malloc(messagelen); if(!message) { - Curl_safefree(trailer); - Curl_safefree(chlg); + free(trailer); + Curl_unicodefree(user_name); return CURLE_OUT_OF_MEMORY; } @@ -592,15 +1168,16 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization identity is not terminated with the zero-valued (%x00) octet." it seems necessary to include it. */ - memcpy(message, &outdata, 4); - strcpy((char *)message + 4, names.sUserName); + outdata = htonl(max_size) | sec_layer; + memcpy(message, &outdata, sizeof(outdata)); + strcpy((char *) message + sizeof(outdata), user_name); + Curl_unicodefree(user_name); /* Allocate the padding */ padding = malloc(sizes.cbBlockSize); if(!padding) { - Curl_safefree(message); - Curl_safefree(trailer); - Curl_safefree(chlg); + free(message); + free(trailer); return CURLE_OUT_OF_MEMORY; } @@ -623,10 +1200,9 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, &wrap_desc, 0); if(status != SEC_E_OK) { - Curl_safefree(padding); - Curl_safefree(message); - Curl_safefree(trailer); - Curl_safefree(chlg); + free(padding); + free(message); + free(trailer); return CURLE_OUT_OF_MEMORY; } @@ -636,10 +1212,9 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, wrap_buf[2].cbBuffer; appdata = malloc(appdatalen); if(!appdata) { - Curl_safefree(padding); - Curl_safefree(message); - Curl_safefree(trailer); - Curl_safefree(chlg); + free(padding); + free(message); + free(trailer); return CURLE_OUT_OF_MEMORY; } @@ -656,25 +1231,34 @@ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, outlen); /* Free all of our local buffers */ - Curl_safefree(appdata); - Curl_safefree(padding); - Curl_safefree(message); - Curl_safefree(trailer); - Curl_safefree(chlg); + free(appdata); + free(padding); + free(message); + free(trailer); return result; } +/* + * Curl_sasl_gssapi_cleanup() + * + * This is used to clean up the gssapi specific data. + * + * Parameters: + * + * krb5 [in/out] - The kerberos 5 data struct being cleaned up. + * + */ void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) { - /* Free the context */ + /* Free our security context */ if(krb5->context) { s_pSecFn->DeleteSecurityContext(krb5->context); free(krb5->context); krb5->context = NULL; } - /* Free the credientials handle */ + /* Free our credentials handle */ if(krb5->credentials) { s_pSecFn->FreeCredentialsHandle(krb5->credentials); free(krb5->credentials); @@ -692,5 +1276,6 @@ void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) /* Reset any variables */ krb5->token_max = 0; } +#endif /* USE_KERBEROS5 */ #endif /* USE_WINDOWS_SSPI */ diff --git a/lib/curl_sec.h b/lib/curl_sec.h index 82151e9..6c48da2 100644 --- a/lib/curl_sec.h +++ b/lib/curl_sec.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,7 +30,7 @@ struct Curl_sec_client_mech { void (*end)(void *); int (*check_prot)(void *, int); int (*overhead)(void *, int, int); - int (*encode)(void *, const void*, int, int, void**, struct connectdata *); + int (*encode)(void *, const void*, int, int, void**); int (*decode)(void *, void*, int, int, struct connectdata *); }; diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 173731c..ab0c139 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -190,6 +190,9 @@ # ifndef CURL_DISABLE_GOPHER # define CURL_DISABLE_GOPHER # endif +# ifndef CURL_DISABLE_SMB +# define CURL_DISABLE_SMB +# endif #endif /* @@ -601,26 +604,38 @@ int netware_init(void); #define LIBIDN_REQUIRED_VERSION "0.4.1" -#if defined(USE_GNUTLS) || defined(USE_SSLEAY) || defined(USE_NSS) || \ - defined(USE_QSOSSL) || defined(USE_POLARSSL) || defined(USE_AXTLS) || \ +#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ + defined(USE_POLARSSL) || defined(USE_AXTLS) || \ defined(USE_CYASSL) || defined(USE_SCHANNEL) || \ defined(USE_DARWINSSL) || defined(USE_GSKIT) #define USE_SSL /* SSL support has been enabled */ #endif +/* Single point where USE_SPNEGO definition might be defined */ #if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_SPNEGO #endif -/* Single point where USE_NTLM definition might be done */ -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM) && \ - !defined(CURL_DISABLE_CRYPTO_AUTH) -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || \ - defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_DARWINSSL) +/* Single point where USE_KERBEROS5 definition might be defined */ +#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#define USE_KERBEROS5 +#endif + +/* Single point where USE_NTLM definition might be defined */ +#if !defined(CURL_DISABLE_NTLM) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if defined(USE_OPENSSL) || defined(USE_WINDOWS_SSPI) || \ + defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_DARWINSSL) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) + +#ifdef HAVE_BORINGSSL /* BoringSSL is not NTLM capable */ +#undef USE_NTLM +#else #define USE_NTLM #endif #endif +#endif /* non-configure builds may define CURL_WANTS_CA_BUNDLE_ENV */ #if defined(CURL_WANTS_CA_BUNDLE_ENV) && !defined(CURL_CA_BUNDLE) @@ -636,8 +651,10 @@ int netware_init(void); #if defined(__GNUC__) && ((__GNUC__ >= 3) || \ ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7))) # define UNUSED_PARAM __attribute__((__unused__)) +# define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else # define UNUSED_PARAM /*NOTHING*/ +# define WARN_UNUSED_RESULT #endif /* @@ -690,4 +707,24 @@ int netware_init(void); #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif +/* In Windows the default file mode is text but an application can override it. +Therefore we specify it explicitly. https://github.com/bagder/curl/pull/258 +*/ +#if defined(WIN32) || defined(MSDOS) +#define FOPEN_READTEXT "rt" +#define FOPEN_WRITETEXT "wt" +#elif defined(__CYGWIN__) +/* Cygwin has specific behavior we need to address when WIN32 is not defined. +https://cygwin.com/cygwin-ug-net/using-textbinary.html +For write we want our output to have line endings of LF and be compatible with +other Cygwin utilities. For read we want to handle input that may have line +endings either CRLF or LF so 't' is appropriate. +*/ +#define FOPEN_READTEXT "rt" +#define FOPEN_WRITETEXT "w" +#else +#define FOPEN_READTEXT "r" +#define FOPEN_WRITETEXT "w" +#endif + #endif /* HEADER_CURL_SETUP_H */ diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index f09d288..070424d 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,17 +25,12 @@ #ifdef USE_WINDOWS_SSPI #include <curl/curl.h> - #include "curl_sspi.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -#include "curl_memory.h" #include "curl_multibyte.h" #include "warnless.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* We use our own typedef here since some headers might lack these */ @@ -98,20 +93,25 @@ CURLcode Curl_sspi_global_init(void) osver.dwPlatformId == platformId) securityDll = TRUE; #else - ULONGLONG majorVersionMask; - ULONGLONG platformIdMask; + ULONGLONG cm; OSVERSIONINFOEX osver; memset(&osver, 0, sizeof(osver)); osver.dwOSVersionInfoSize = sizeof(osver); osver.dwMajorVersion = majorVersion; osver.dwPlatformId = platformId; - majorVersionMask = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); - platformIdMask = VerSetConditionMask(0, VER_PLATFORMID, VER_EQUAL); + + cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL); /* Verify the major version number == 4 and platform id == WIN_NT */ - if(VerifyVersionInfo(&osver, VER_MAJORVERSION, majorVersionMask) && - VerifyVersionInfo(&osver, VER_PLATFORMID, platformIdMask)) + if(VerifyVersionInfo(&osver, (VER_MAJORVERSION | VER_MINORVERSION | + VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR | + VER_PLATFORMID), + cm)) securityDll = TRUE; #endif @@ -224,7 +224,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, Curl_unicodefree(useranddomain.tchar_ptr); - /* Setup ntlm identity's password and length */ + /* Setup the identity's password and length */ passwd.tchar_ptr = Curl_convert_UTF8_to_tchar((char *)passwdp); if(!passwd.tchar_ptr) return CURLE_OUT_OF_MEMORY; diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 5ab17d5..8655715 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -43,6 +43,10 @@ CURLcode Curl_sspi_global_init(void); void Curl_sspi_global_cleanup(void); +/* This is used to populate the domain in a SSPI identity structure */ +CURLcode Curl_override_sspi_http_realm(const char *chlg, + SEC_WINNT_AUTH_IDENTITY *identity); + /* This is used to generate an SSPI identity structure */ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, SEC_WINNT_AUTH_IDENTITY *identity); @@ -51,249 +55,276 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity); /* Forward-declaration of global variables defined in curl_sspi.c */ - extern HMODULE s_hSecDll; extern PSecurityFunctionTable s_pSecFn; /* Provide some definitions missing in old headers */ +#define SP_NAME_DIGEST "WDigest" +#define SP_NAME_NTLM "NTLM" +#define SP_NAME_NEGOTIATE "Negotiate" +#define SP_NAME_KERBEROS "Kerberos" + +#ifndef ISC_REQ_USE_HTTP_STYLE +#define ISC_REQ_USE_HTTP_STYLE 0x01000000 +#endif + +#ifndef ISC_RET_REPLAY_DETECT +#define ISC_RET_REPLAY_DETECT 0x00000004 +#endif + +#ifndef ISC_RET_SEQUENCE_DETECT +#define ISC_RET_SEQUENCE_DETECT 0x00000008 +#endif + +#ifndef ISC_RET_CONFIDENTIALITY +#define ISC_RET_CONFIDENTIALITY 0x00000010 +#endif + +#ifndef ISC_RET_ALLOCATED_MEMORY +#define ISC_RET_ALLOCATED_MEMORY 0x00000100 +#endif + +#ifndef ISC_RET_STREAM +#define ISC_RET_STREAM 0x00008000 +#endif #ifndef SEC_E_INSUFFICIENT_MEMORY -# define SEC_E_INSUFFICIENT_MEMORY ((HRESULT)0x80090300L) +# define SEC_E_INSUFFICIENT_MEMORY ((HRESULT)0x80090300L) #endif #ifndef SEC_E_INVALID_HANDLE -# define SEC_E_INVALID_HANDLE ((HRESULT)0x80090301L) +# define SEC_E_INVALID_HANDLE ((HRESULT)0x80090301L) #endif #ifndef SEC_E_UNSUPPORTED_FUNCTION -# define SEC_E_UNSUPPORTED_FUNCTION ((HRESULT)0x80090302L) +# define SEC_E_UNSUPPORTED_FUNCTION ((HRESULT)0x80090302L) #endif #ifndef SEC_E_TARGET_UNKNOWN -# define SEC_E_TARGET_UNKNOWN ((HRESULT)0x80090303L) +# define SEC_E_TARGET_UNKNOWN ((HRESULT)0x80090303L) #endif #ifndef SEC_E_INTERNAL_ERROR -# define SEC_E_INTERNAL_ERROR ((HRESULT)0x80090304L) +# define SEC_E_INTERNAL_ERROR ((HRESULT)0x80090304L) #endif #ifndef SEC_E_SECPKG_NOT_FOUND -# define SEC_E_SECPKG_NOT_FOUND ((HRESULT)0x80090305L) +# define SEC_E_SECPKG_NOT_FOUND ((HRESULT)0x80090305L) #endif #ifndef SEC_E_NOT_OWNER -# define SEC_E_NOT_OWNER ((HRESULT)0x80090306L) +# define SEC_E_NOT_OWNER ((HRESULT)0x80090306L) #endif #ifndef SEC_E_CANNOT_INSTALL -# define SEC_E_CANNOT_INSTALL ((HRESULT)0x80090307L) +# define SEC_E_CANNOT_INSTALL ((HRESULT)0x80090307L) #endif #ifndef SEC_E_INVALID_TOKEN -# define SEC_E_INVALID_TOKEN ((HRESULT)0x80090308L) +# define SEC_E_INVALID_TOKEN ((HRESULT)0x80090308L) #endif #ifndef SEC_E_CANNOT_PACK -# define SEC_E_CANNOT_PACK ((HRESULT)0x80090309L) +# define SEC_E_CANNOT_PACK ((HRESULT)0x80090309L) #endif #ifndef SEC_E_QOP_NOT_SUPPORTED -# define SEC_E_QOP_NOT_SUPPORTED ((HRESULT)0x8009030AL) +# define SEC_E_QOP_NOT_SUPPORTED ((HRESULT)0x8009030AL) #endif #ifndef SEC_E_NO_IMPERSONATION -# define SEC_E_NO_IMPERSONATION ((HRESULT)0x8009030BL) +# define SEC_E_NO_IMPERSONATION ((HRESULT)0x8009030BL) #endif #ifndef SEC_E_LOGON_DENIED -# define SEC_E_LOGON_DENIED ((HRESULT)0x8009030CL) +# define SEC_E_LOGON_DENIED ((HRESULT)0x8009030CL) #endif #ifndef SEC_E_UNKNOWN_CREDENTIALS -# define SEC_E_UNKNOWN_CREDENTIALS ((HRESULT)0x8009030DL) +# define SEC_E_UNKNOWN_CREDENTIALS ((HRESULT)0x8009030DL) #endif #ifndef SEC_E_NO_CREDENTIALS -# define SEC_E_NO_CREDENTIALS ((HRESULT)0x8009030EL) +# define SEC_E_NO_CREDENTIALS ((HRESULT)0x8009030EL) #endif #ifndef SEC_E_MESSAGE_ALTERED -# define SEC_E_MESSAGE_ALTERED ((HRESULT)0x8009030FL) +# define SEC_E_MESSAGE_ALTERED ((HRESULT)0x8009030FL) #endif #ifndef SEC_E_OUT_OF_SEQUENCE -# define SEC_E_OUT_OF_SEQUENCE ((HRESULT)0x80090310L) +# define SEC_E_OUT_OF_SEQUENCE ((HRESULT)0x80090310L) #endif #ifndef SEC_E_NO_AUTHENTICATING_AUTHORITY -# define SEC_E_NO_AUTHENTICATING_AUTHORITY ((HRESULT)0x80090311L) +# define SEC_E_NO_AUTHENTICATING_AUTHORITY ((HRESULT)0x80090311L) #endif #ifndef SEC_E_BAD_PKGID -# define SEC_E_BAD_PKGID ((HRESULT)0x80090316L) +# define SEC_E_BAD_PKGID ((HRESULT)0x80090316L) #endif #ifndef SEC_E_CONTEXT_EXPIRED -# define SEC_E_CONTEXT_EXPIRED ((HRESULT)0x80090317L) +# define SEC_E_CONTEXT_EXPIRED ((HRESULT)0x80090317L) #endif #ifndef SEC_E_INCOMPLETE_MESSAGE -# define SEC_E_INCOMPLETE_MESSAGE ((HRESULT)0x80090318L) +# define SEC_E_INCOMPLETE_MESSAGE ((HRESULT)0x80090318L) #endif #ifndef SEC_E_INCOMPLETE_CREDENTIALS -# define SEC_E_INCOMPLETE_CREDENTIALS ((HRESULT)0x80090320L) +# define SEC_E_INCOMPLETE_CREDENTIALS ((HRESULT)0x80090320L) #endif #ifndef SEC_E_BUFFER_TOO_SMALL -# define SEC_E_BUFFER_TOO_SMALL ((HRESULT)0x80090321L) +# define SEC_E_BUFFER_TOO_SMALL ((HRESULT)0x80090321L) #endif #ifndef SEC_E_WRONG_PRINCIPAL -# define SEC_E_WRONG_PRINCIPAL ((HRESULT)0x80090322L) +# define SEC_E_WRONG_PRINCIPAL ((HRESULT)0x80090322L) #endif #ifndef SEC_E_TIME_SKEW -# define SEC_E_TIME_SKEW ((HRESULT)0x80090324L) +# define SEC_E_TIME_SKEW ((HRESULT)0x80090324L) #endif #ifndef SEC_E_UNTRUSTED_ROOT -# define SEC_E_UNTRUSTED_ROOT ((HRESULT)0x80090325L) +# define SEC_E_UNTRUSTED_ROOT ((HRESULT)0x80090325L) #endif #ifndef SEC_E_ILLEGAL_MESSAGE -# define SEC_E_ILLEGAL_MESSAGE ((HRESULT)0x80090326L) +# define SEC_E_ILLEGAL_MESSAGE ((HRESULT)0x80090326L) #endif #ifndef SEC_E_CERT_UNKNOWN -# define SEC_E_CERT_UNKNOWN ((HRESULT)0x80090327L) +# define SEC_E_CERT_UNKNOWN ((HRESULT)0x80090327L) #endif #ifndef SEC_E_CERT_EXPIRED -# define SEC_E_CERT_EXPIRED ((HRESULT)0x80090328L) +# define SEC_E_CERT_EXPIRED ((HRESULT)0x80090328L) #endif #ifndef SEC_E_ENCRYPT_FAILURE -# define SEC_E_ENCRYPT_FAILURE ((HRESULT)0x80090329L) +# define SEC_E_ENCRYPT_FAILURE ((HRESULT)0x80090329L) #endif #ifndef SEC_E_DECRYPT_FAILURE -# define SEC_E_DECRYPT_FAILURE ((HRESULT)0x80090330L) +# define SEC_E_DECRYPT_FAILURE ((HRESULT)0x80090330L) #endif #ifndef SEC_E_ALGORITHM_MISMATCH -# define SEC_E_ALGORITHM_MISMATCH ((HRESULT)0x80090331L) +# define SEC_E_ALGORITHM_MISMATCH ((HRESULT)0x80090331L) #endif #ifndef SEC_E_SECURITY_QOS_FAILED -# define SEC_E_SECURITY_QOS_FAILED ((HRESULT)0x80090332L) +# define SEC_E_SECURITY_QOS_FAILED ((HRESULT)0x80090332L) #endif #ifndef SEC_E_UNFINISHED_CONTEXT_DELETED -# define SEC_E_UNFINISHED_CONTEXT_DELETED ((HRESULT)0x80090333L) +# define SEC_E_UNFINISHED_CONTEXT_DELETED ((HRESULT)0x80090333L) #endif #ifndef SEC_E_NO_TGT_REPLY -# define SEC_E_NO_TGT_REPLY ((HRESULT)0x80090334L) +# define SEC_E_NO_TGT_REPLY ((HRESULT)0x80090334L) #endif #ifndef SEC_E_NO_IP_ADDRESSES -# define SEC_E_NO_IP_ADDRESSES ((HRESULT)0x80090335L) +# define SEC_E_NO_IP_ADDRESSES ((HRESULT)0x80090335L) #endif #ifndef SEC_E_WRONG_CREDENTIAL_HANDLE -# define SEC_E_WRONG_CREDENTIAL_HANDLE ((HRESULT)0x80090336L) +# define SEC_E_WRONG_CREDENTIAL_HANDLE ((HRESULT)0x80090336L) #endif #ifndef SEC_E_CRYPTO_SYSTEM_INVALID -# define SEC_E_CRYPTO_SYSTEM_INVALID ((HRESULT)0x80090337L) +# define SEC_E_CRYPTO_SYSTEM_INVALID ((HRESULT)0x80090337L) #endif #ifndef SEC_E_MAX_REFERRALS_EXCEEDED -# define SEC_E_MAX_REFERRALS_EXCEEDED ((HRESULT)0x80090338L) +# define SEC_E_MAX_REFERRALS_EXCEEDED ((HRESULT)0x80090338L) #endif #ifndef SEC_E_MUST_BE_KDC -# define SEC_E_MUST_BE_KDC ((HRESULT)0x80090339L) +# define SEC_E_MUST_BE_KDC ((HRESULT)0x80090339L) #endif #ifndef SEC_E_STRONG_CRYPTO_NOT_SUPPORTED -# define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED ((HRESULT)0x8009033AL) +# define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED ((HRESULT)0x8009033AL) #endif #ifndef SEC_E_TOO_MANY_PRINCIPALS -# define SEC_E_TOO_MANY_PRINCIPALS ((HRESULT)0x8009033BL) +# define SEC_E_TOO_MANY_PRINCIPALS ((HRESULT)0x8009033BL) #endif #ifndef SEC_E_NO_PA_DATA -# define SEC_E_NO_PA_DATA ((HRESULT)0x8009033CL) +# define SEC_E_NO_PA_DATA ((HRESULT)0x8009033CL) #endif #ifndef SEC_E_PKINIT_NAME_MISMATCH -# define SEC_E_PKINIT_NAME_MISMATCH ((HRESULT)0x8009033DL) +# define SEC_E_PKINIT_NAME_MISMATCH ((HRESULT)0x8009033DL) #endif #ifndef SEC_E_SMARTCARD_LOGON_REQUIRED -# define SEC_E_SMARTCARD_LOGON_REQUIRED ((HRESULT)0x8009033EL) +# define SEC_E_SMARTCARD_LOGON_REQUIRED ((HRESULT)0x8009033EL) #endif #ifndef SEC_E_SHUTDOWN_IN_PROGRESS -# define SEC_E_SHUTDOWN_IN_PROGRESS ((HRESULT)0x8009033FL) +# define SEC_E_SHUTDOWN_IN_PROGRESS ((HRESULT)0x8009033FL) #endif #ifndef SEC_E_KDC_INVALID_REQUEST -# define SEC_E_KDC_INVALID_REQUEST ((HRESULT)0x80090340L) +# define SEC_E_KDC_INVALID_REQUEST ((HRESULT)0x80090340L) #endif #ifndef SEC_E_KDC_UNABLE_TO_REFER -# define SEC_E_KDC_UNABLE_TO_REFER ((HRESULT)0x80090341L) +# define SEC_E_KDC_UNABLE_TO_REFER ((HRESULT)0x80090341L) #endif #ifndef SEC_E_KDC_UNKNOWN_ETYPE -# define SEC_E_KDC_UNKNOWN_ETYPE ((HRESULT)0x80090342L) +# define SEC_E_KDC_UNKNOWN_ETYPE ((HRESULT)0x80090342L) #endif #ifndef SEC_E_UNSUPPORTED_PREAUTH -# define SEC_E_UNSUPPORTED_PREAUTH ((HRESULT)0x80090343L) +# define SEC_E_UNSUPPORTED_PREAUTH ((HRESULT)0x80090343L) #endif #ifndef SEC_E_DELEGATION_REQUIRED -# define SEC_E_DELEGATION_REQUIRED ((HRESULT)0x80090345L) +# define SEC_E_DELEGATION_REQUIRED ((HRESULT)0x80090345L) #endif #ifndef SEC_E_BAD_BINDINGS -# define SEC_E_BAD_BINDINGS ((HRESULT)0x80090346L) +# define SEC_E_BAD_BINDINGS ((HRESULT)0x80090346L) #endif #ifndef SEC_E_MULTIPLE_ACCOUNTS -# define SEC_E_MULTIPLE_ACCOUNTS ((HRESULT)0x80090347L) +# define SEC_E_MULTIPLE_ACCOUNTS ((HRESULT)0x80090347L) #endif #ifndef SEC_E_NO_KERB_KEY -# define SEC_E_NO_KERB_KEY ((HRESULT)0x80090348L) +# define SEC_E_NO_KERB_KEY ((HRESULT)0x80090348L) #endif #ifndef SEC_E_CERT_WRONG_USAGE -# define SEC_E_CERT_WRONG_USAGE ((HRESULT)0x80090349L) +# define SEC_E_CERT_WRONG_USAGE ((HRESULT)0x80090349L) #endif #ifndef SEC_E_DOWNGRADE_DETECTED -# define SEC_E_DOWNGRADE_DETECTED ((HRESULT)0x80090350L) +# define SEC_E_DOWNGRADE_DETECTED ((HRESULT)0x80090350L) #endif #ifndef SEC_E_SMARTCARD_CERT_REVOKED -# define SEC_E_SMARTCARD_CERT_REVOKED ((HRESULT)0x80090351L) +# define SEC_E_SMARTCARD_CERT_REVOKED ((HRESULT)0x80090351L) #endif #ifndef SEC_E_ISSUING_CA_UNTRUSTED -# define SEC_E_ISSUING_CA_UNTRUSTED ((HRESULT)0x80090352L) +# define SEC_E_ISSUING_CA_UNTRUSTED ((HRESULT)0x80090352L) #endif #ifndef SEC_E_REVOCATION_OFFLINE_C -# define SEC_E_REVOCATION_OFFLINE_C ((HRESULT)0x80090353L) +# define SEC_E_REVOCATION_OFFLINE_C ((HRESULT)0x80090353L) #endif #ifndef SEC_E_PKINIT_CLIENT_FAILURE -# define SEC_E_PKINIT_CLIENT_FAILURE ((HRESULT)0x80090354L) +# define SEC_E_PKINIT_CLIENT_FAILURE ((HRESULT)0x80090354L) #endif #ifndef SEC_E_SMARTCARD_CERT_EXPIRED -# define SEC_E_SMARTCARD_CERT_EXPIRED ((HRESULT)0x80090355L) +# define SEC_E_SMARTCARD_CERT_EXPIRED ((HRESULT)0x80090355L) #endif #ifndef SEC_E_NO_S4U_PROT_SUPPORT -# define SEC_E_NO_S4U_PROT_SUPPORT ((HRESULT)0x80090356L) +# define SEC_E_NO_S4U_PROT_SUPPORT ((HRESULT)0x80090356L) #endif #ifndef SEC_E_CROSSREALM_DELEGATION_FAILURE -# define SEC_E_CROSSREALM_DELEGATION_FAILURE ((HRESULT)0x80090357L) +# define SEC_E_CROSSREALM_DELEGATION_FAILURE ((HRESULT)0x80090357L) #endif #ifndef SEC_E_REVOCATION_OFFLINE_KDC -# define SEC_E_REVOCATION_OFFLINE_KDC ((HRESULT)0x80090358L) +# define SEC_E_REVOCATION_OFFLINE_KDC ((HRESULT)0x80090358L) #endif #ifndef SEC_E_ISSUING_CA_UNTRUSTED_KDC -# define SEC_E_ISSUING_CA_UNTRUSTED_KDC ((HRESULT)0x80090359L) +# define SEC_E_ISSUING_CA_UNTRUSTED_KDC ((HRESULT)0x80090359L) #endif #ifndef SEC_E_KDC_CERT_EXPIRED -# define SEC_E_KDC_CERT_EXPIRED ((HRESULT)0x8009035AL) +# define SEC_E_KDC_CERT_EXPIRED ((HRESULT)0x8009035AL) #endif #ifndef SEC_E_KDC_CERT_REVOKED -# define SEC_E_KDC_CERT_REVOKED ((HRESULT)0x8009035BL) +# define SEC_E_KDC_CERT_REVOKED ((HRESULT)0x8009035BL) #endif #ifndef SEC_E_INVALID_PARAMETER -# define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) +# define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) #endif #ifndef SEC_E_DELEGATION_POLICY -# define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) +# define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) #endif #ifndef SEC_E_POLICY_NLTM_ONLY -# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) +# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) #endif #ifndef SEC_I_CONTINUE_NEEDED -# define SEC_I_CONTINUE_NEEDED ((HRESULT)0x00090312L) +# define SEC_I_CONTINUE_NEEDED ((HRESULT)0x00090312L) #endif #ifndef SEC_I_COMPLETE_NEEDED -# define SEC_I_COMPLETE_NEEDED ((HRESULT)0x00090313L) +# define SEC_I_COMPLETE_NEEDED ((HRESULT)0x00090313L) #endif #ifndef SEC_I_COMPLETE_AND_CONTINUE -# define SEC_I_COMPLETE_AND_CONTINUE ((HRESULT)0x00090314L) +# define SEC_I_COMPLETE_AND_CONTINUE ((HRESULT)0x00090314L) #endif #ifndef SEC_I_LOCAL_LOGON -# define SEC_I_LOCAL_LOGON ((HRESULT)0x00090315L) +# define SEC_I_LOCAL_LOGON ((HRESULT)0x00090315L) #endif #ifndef SEC_I_CONTEXT_EXPIRED -# define SEC_I_CONTEXT_EXPIRED ((HRESULT)0x00090317L) +# define SEC_I_CONTEXT_EXPIRED ((HRESULT)0x00090317L) #endif #ifndef SEC_I_INCOMPLETE_CREDENTIALS -# define SEC_I_INCOMPLETE_CREDENTIALS ((HRESULT)0x00090320L) +# define SEC_I_INCOMPLETE_CREDENTIALS ((HRESULT)0x00090320L) #endif #ifndef SEC_I_RENEGOTIATE -# define SEC_I_RENEGOTIATE ((HRESULT)0x00090321L) +# define SEC_I_RENEGOTIATE ((HRESULT)0x00090321L) #endif #ifndef SEC_I_NO_LSA_CONTEXT -# define SEC_I_NO_LSA_CONTEXT ((HRESULT)0x00090323L) +# define SEC_I_NO_LSA_CONTEXT ((HRESULT)0x00090323L) #endif #ifndef SEC_I_SIGNATURE_NEEDED -# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) +# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) #endif #ifdef UNICODE diff --git a/lib/curl_threads.c b/lib/curl_threads.c index d40e024..f9b812e 100644 --- a/lib/curl_threads.c +++ b/lib/curl_threads.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,10 +33,6 @@ #endif #include "curl_threads.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -77,8 +73,8 @@ curl_thread_t Curl_thread_create(unsigned int (*func) (void*), void *arg) return t; err: - Curl_safefree(t); - Curl_safefree(ac); + free(t); + free(ac); return curl_thread_t_null; } @@ -123,7 +119,12 @@ void Curl_thread_destroy(curl_thread_t hnd) int Curl_thread_join(curl_thread_t *hnd) { +#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ + (_WIN32_WINNT < _WIN32_WINNT_VISTA) int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0); +#else + int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0); +#endif Curl_thread_destroy(*hnd); diff --git a/lib/curl_threads.h b/lib/curl_threads.h index 6457cbb..0f3191a 100644 --- a/lib/curl_threads.h +++ b/lib/curl_threads.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -37,7 +37,12 @@ # define curl_mutex_t CRITICAL_SECTION # define curl_thread_t HANDLE # define curl_thread_t_null (HANDLE)0 -# define Curl_mutex_init(m) InitializeCriticalSection(m) +# if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ + (_WIN32_WINNT < _WIN32_WINNT_VISTA) +# define Curl_mutex_init(m) InitializeCriticalSection(m) +# else +# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1) +# endif # define Curl_mutex_acquire(m) EnterCriticalSection(m) # define Curl_mutex_release(m) LeaveCriticalSection(m) # define Curl_mutex_destroy(m) DeleteCriticalSection(m) diff --git a/lib/curlx.h b/lib/curlx.h index 9dc90a0..979e7d7 100644 --- a/lib/curlx.h +++ b/lib/curlx.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -90,8 +90,7 @@ #ifdef ENABLE_CURLX_PRINTF /* If this define is set, we define all "standard" printf() functions to use the curlx_* version instead. It makes the source code transparent and - easier to understand/patch. Undefine them first in case _MPRINTF_REPLACE - is set. */ + easier to understand/patch. Undefine them first. */ # undef printf # undef fprintf # undef sprintf @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -57,10 +57,6 @@ #include "strequal.h" #include "dict.h" #include "rawstr.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -101,7 +97,7 @@ static char *unescape_word(struct SessionHandle *data, const char *inputbuff) char *dictp; char *ptr; int len; - char byte; + char ch; int olen=0; newp = curl_easy_unescape(data, inputbuff, 0, &len); @@ -113,13 +109,13 @@ static char *unescape_word(struct SessionHandle *data, const char *inputbuff) /* According to RFC2229 section 2.2, these letters need to be escaped with \[letter] */ for(ptr = newp; - (byte = *ptr) != 0; + (ch = *ptr) != 0; ptr++) { - if((byte <= 32) || (byte == 127) || - (byte == '\'') || (byte == '\"') || (byte == '\\')) { + if((ch <= 32) || (ch == 127) || + (ch == '\'') || (ch == '\"') || (ch == '\\')) { dictp[olen++] = '\\'; } - dictp[olen++] = byte; + dictp[olen++] = ch; } dictp[olen]=0; } @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,7 +60,6 @@ #include "hostip.h" #include "share.h" #include "strdup.h" -#include "curl_memory.h" #include "progress.h" #include "easyif.h" #include "select.h" @@ -74,11 +73,11 @@ #include "conncache.h" #include "multiif.h" #include "sigpipe.h" +#include "ssh.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* win32_cleanup() is for win32 socket cleanup functionality, the opposite @@ -136,9 +135,9 @@ static CURLcode win32_init(void) #ifdef USE_WINDOWS_SSPI { - CURLcode err = Curl_sspi_global_init(); - if(err != CURLE_OK) - return err; + CURLcode result = Curl_sspi_global_init(); + if(result) + return result; } #endif @@ -243,7 +242,7 @@ CURLcode curl_global_init(long flags) } if(flags & CURL_GLOBAL_WIN32) - if(win32_init() != CURLE_OK) { + if(win32_init()) { DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); return CURLE_FAILED_INIT; } @@ -265,7 +264,7 @@ CURLcode curl_global_init(long flags) idna_init(); #endif - if(Curl_resolver_global_init() != CURLE_OK) { + if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); return CURLE_FAILED_INIT; } @@ -293,7 +292,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, curl_free_callback f, curl_realloc_callback r, curl_strdup_callback s, curl_calloc_callback c) { - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; /* Invalid input, return immediately */ if(!m || !f || !r || !s || !c) @@ -308,8 +307,8 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, } /* Call the actual init function first */ - code = curl_global_init(flags); - if(code == CURLE_OK) { + result = curl_global_init(flags); + if(!result) { Curl_cmalloc = m; Curl_cfree = f; Curl_cstrdup = s; @@ -317,7 +316,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, Curl_ccalloc = c; } - return code; + return result; } /** @@ -357,13 +356,13 @@ void curl_global_cleanup(void) */ CURL *curl_easy_init(void) { - CURLcode res; + CURLcode result; struct SessionHandle *data; /* Make sure we inited the global SSL stuff */ if(!initialized) { - res = curl_global_init(CURL_GLOBAL_DEFAULT); - if(res) { + result = curl_global_init(CURL_GLOBAL_DEFAULT); + if(result) { /* something in the global init failed, return nothing */ DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); return NULL; @@ -371,8 +370,8 @@ CURL *curl_easy_init(void) } /* We use curl_open() with undefined URL so far */ - res = Curl_open(&data); - if(res != CURLE_OK) { + result = Curl_open(&data); + if(result) { DEBUGF(fprintf(stderr, "Error: Curl_open failed\n")); return NULL; } @@ -390,17 +389,17 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption tag, ...) { va_list arg; struct SessionHandle *data = curl; - CURLcode ret; + CURLcode result; if(!curl) return CURLE_BAD_FUNCTION_ARGUMENT; va_start(arg, tag); - ret = Curl_setopt(data, tag, arg); + result = Curl_setopt(data, tag, arg); va_end(arg); - return ret; + return result; } #ifdef CURLDEBUG @@ -490,6 +489,10 @@ static int events_socket(CURL *easy, /* easy handle */ struct events *ev = userp; struct socketmonitor *m; struct socketmonitor *prev=NULL; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) easy; +#endif (void)socketp; m = ev->list; @@ -570,7 +573,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) { bool done = FALSE; CURLMcode mcode; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; while(!done) { CURLMsg *msg; @@ -630,6 +633,9 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) ev->ms += curlx_tvdiff(after, before); } + else + return CURLE_RECV_ERROR; + if(mcode) return CURLE_URL_MALFORMAT; /* TODO: return a proper error! */ @@ -637,12 +643,12 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) second argument */ msg = curl_multi_info_read(multi, &pollrc); if(msg) { - rc = msg->data.result; + result = msg->data.result; done = TRUE; } } - return rc; + return result; } @@ -668,7 +674,7 @@ static CURLcode easy_transfer(CURLM *multi) { bool done = FALSE; CURLMcode mcode = CURLM_OK; - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; struct timeval before; int without_fds = 0; /* count number of consecutive returns from curl_multi_wait() without any filedescriptors */ @@ -683,7 +689,7 @@ static CURLcode easy_transfer(CURLM *multi) if(mcode == CURLM_OK) { if(ret == -1) { /* poll() failed not on EINTR, indicate a network problem */ - code = CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; break; } else if(ret == 0) { @@ -714,7 +720,7 @@ static CURLcode easy_transfer(CURLM *multi) int rc; CURLMsg *msg = curl_multi_info_read(multi, &rc); if(msg) { - code = msg->data.result; + result = msg->data.result; done = TRUE; } } @@ -728,7 +734,7 @@ static CURLcode easy_transfer(CURLM *multi) CURLE_BAD_FUNCTION_ARGUMENT; } - return code; + return result; } @@ -753,7 +759,7 @@ static CURLcode easy_perform(struct SessionHandle *data, bool events) { CURLM *multi; CURLMcode mcode; - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; SIGPIPE_VARIABLE(pipe_st); if(!data) @@ -794,7 +800,7 @@ static CURLcode easy_perform(struct SessionHandle *data, bool events) data->multi = multi; /* run the transfer */ - code = events ? easy_events(multi) : easy_transfer(multi); + result = events ? easy_events(multi) : easy_transfer(multi); /* ignoring the return code isn't nice, but atm we can't really handle a failure here, room for future improvement! */ @@ -803,7 +809,7 @@ static CURLcode easy_perform(struct SessionHandle *data, bool events) sigpipe_restore(&pipe_st); /* The multi handle is kept alive, owned by the easy handle */ - return code; + return result; } @@ -854,16 +860,16 @@ CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; - CURLcode ret; + CURLcode result; struct SessionHandle *data = (struct SessionHandle *)curl; va_start(arg, info); paramp = va_arg(arg, void *); - ret = Curl_getinfo(data, info, paramp); + result = Curl_getinfo(data, info, paramp); va_end(arg); - return ret; + return result; } /* @@ -890,7 +896,7 @@ CURL *curl_easy_duphandle(CURL *incurl) outcurl->state.headersize = HEADERSIZE; /* copy all userdefined values */ - if(Curl_dupset(outcurl, data) != CURLE_OK) + if(Curl_dupset(outcurl, data)) goto fail; /* the connection cache is setup on demand */ @@ -936,7 +942,7 @@ CURL *curl_easy_duphandle(CURL *incurl) /* Clone the resolver handle, if present, for the new handle */ if(Curl_resolver_duphandle(&outcurl->state.resolver, - data->state.resolver) != CURLE_OK) + data->state.resolver)) goto fail; Curl_convert_setup(outcurl); @@ -1018,73 +1024,15 @@ CURLcode curl_easy_pause(CURL *curl, int action) /* we have a buffer for sending that we now seem to be able to deliver since the receive pausing is lifted! */ - /* get the pointer, type and length in local copies since the function may - return PAUSE again and then we'll get a new copy allocted and stored in + /* get the pointer in local copy since the function may return PAUSE + again and then we'll get a new copy allocted and stored in the tempwrite variables */ char *tempwrite = data->state.tempwrite; - char *freewrite = tempwrite; /* store this pointer to free it later */ - size_t tempsize = data->state.tempwritesize; - int temptype = data->state.tempwritetype; - size_t chunklen; - - /* clear tempwrite here just to make sure it gets cleared if there's no - further use of it, and make sure we don't clear it after the function - invoke as it may have been set to a new value by then */ - data->state.tempwrite = NULL; - - /* since the write callback API is define to never exceed - CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact - have more data than that in our buffer here, we must loop sending the - data in multiple calls until there's no data left or we get another - pause returned. - - A tricky part is that the function we call will "buffer" the data - itself when it pauses on a particular buffer, so we may need to do some - extra trickery if we get a pause return here. - */ - do { - chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize; - - result = Curl_client_write(data->easy_conn, - temptype, tempwrite, chunklen); - if(result) - /* failures abort the loop at once */ - break; - - if(data->state.tempwrite && (tempsize - chunklen)) { - /* Ouch, the reading is again paused and the block we send is now - "cached". If this is the final chunk we can leave it like this, but - if we have more chunks that are cached after this, we need to free - the newly cached one and put back a version that is truly the entire - contents that is saved for later - */ - char *newptr; - - /* note that tempsize is still the size as before the callback was - used, and thus the whole piece of data to keep */ - newptr = realloc(data->state.tempwrite, tempsize); - - if(!newptr) { - free(data->state.tempwrite); /* free old area */ - data->state.tempwrite = NULL; - result = CURLE_OUT_OF_MEMORY; - /* tempwrite will be freed further down */ - break; - } - data->state.tempwrite = newptr; /* store new pointer */ - memcpy(newptr, tempwrite, tempsize); - data->state.tempwritesize = tempsize; /* store new size */ - /* tempwrite will be freed further down */ - break; /* go back to pausing until further notice */ - } - else { - tempsize -= chunklen; /* left after the call above */ - tempwrite += chunklen; /* advance the pointer */ - } - } while((result == CURLE_OK) && tempsize); - - free(freewrite); /* this is unconditionally no longer used */ + data->state.tempwrite = NULL; + result = Curl_client_chop_write(data->easy_conn, data->state.tempwritetype, + tempwrite, data->state.tempwritesize); + free(tempwrite); } /* if there's no error and we're not pausing both directions, we want @@ -1129,20 +1077,20 @@ static CURLcode easy_connection(struct SessionHandle *data, CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) { curl_socket_t sfd; - CURLcode ret; + CURLcode result; ssize_t n1; struct connectdata *c; struct SessionHandle *data = (struct SessionHandle *)curl; - ret = easy_connection(data, &sfd, &c); - if(ret) - return ret; + result = easy_connection(data, &sfd, &c); + if(result) + return result; *n = 0; - ret = Curl_read(c, sfd, buffer, buflen, &n1); + result = Curl_read(c, sfd, buffer, buflen, &n1); - if(ret != CURLE_OK) - return ret; + if(result) + return result; *n = (size_t)n1; @@ -1157,26 +1105,26 @@ CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n) { curl_socket_t sfd; - CURLcode ret; + CURLcode result; ssize_t n1; struct connectdata *c = NULL; struct SessionHandle *data = (struct SessionHandle *)curl; - ret = easy_connection(data, &sfd, &c); - if(ret) - return ret; + result = easy_connection(data, &sfd, &c); + if(result) + return result; *n = 0; - ret = Curl_write(c, sfd, buffer, buflen, &n1); + result = Curl_write(c, sfd, buffer, buflen, &n1); if(n1 == -1) return CURLE_SEND_ERROR; /* detect EAGAIN */ - if((CURLE_OK == ret) && (0 == n1)) + if(!result && !n1) return CURLE_AGAIN; *n = (size_t)n1; - return ret; + return result; } diff --git a/lib/escape.c b/lib/escape.c index d7f8a8f..24abb93 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,16 +27,14 @@ #include <curl/curl.h> -#include "curl_memory.h" #include "urldata.h" #include "warnless.h" #include "non-ascii.h" #include "escape.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* Portable character check (remember EBCDIC). Do not use isalnum() because @@ -87,7 +85,7 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength) size_t newlen = alloc; size_t strindex=0; size_t length; - CURLcode res; + CURLcode result; ns = malloc(alloc); if(!ns) @@ -115,8 +113,8 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength) } } - res = Curl_convert_to_network(handle, &in, 1); - if(res) { + result = Curl_convert_to_network(handle, &in, 1); + if(result) { /* Curl_convert_to_network calls failf if unsuccessful */ free(ns); return NULL; @@ -152,7 +150,7 @@ CURLcode Curl_urldecode(struct SessionHandle *data, unsigned char in; size_t strindex=0; unsigned long hex; - CURLcode res; + CURLcode result; if(!ns) return CURLE_OUT_OF_MEMORY; @@ -172,16 +170,17 @@ CURLcode Curl_urldecode(struct SessionHandle *data, in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ - res = Curl_convert_from_network(data, &in, 1); - if(res) { + result = Curl_convert_from_network(data, &in, 1); + if(result) { /* Curl_convert_from_network calls failf if unsuccessful */ free(ns); - return res; + return result; } string+=2; alloc-=2; } + if(reject_ctrl && (in < 0x20)) { free(ns); return CURLE_URL_MALFORMAT; @@ -228,6 +227,5 @@ char *curl_easy_unescape(CURL *handle, const char *string, int length, the library's memory system */ void curl_free(void *p) { - if(p) - free(p); + free(p); } @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -59,14 +59,12 @@ #include "getinfo.h" #include "transfer.h" #include "url.h" -#include "curl_memory.h" #include "parsedate.h" /* for the week day and month names */ #include "warnless.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \ @@ -196,8 +194,9 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) int i; char *actual_path; #endif + int real_path_len; - real_path = curl_easy_unescape(data, data->state.path, 0, NULL); + real_path = curl_easy_unescape(data, data->state.path, 0, &real_path_len); if(!real_path) return CURLE_OUT_OF_MEMORY; @@ -222,16 +221,23 @@ static CURLcode file_connect(struct connectdata *conn, bool *done) (actual_path[2] == ':' || actual_path[2] == '|')) { actual_path[2] = ':'; actual_path++; + real_path_len--; } /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */ - for(i=0; actual_path[i] != '\0'; ++i) + for(i=0; i < real_path_len; ++i) if(actual_path[i] == '/') actual_path[i] = '\\'; + else if(!actual_path[i]) /* binary zero */ + return CURLE_URL_MALFORMAT; fd = open_readonly(actual_path, O_RDONLY|O_BINARY); file->path = actual_path; #else + if(memchr(real_path, 0, real_path_len)) + /* binary zeroes indicate foul play */ + return CURLE_URL_MALFORMAT; + fd = open_readonly(real_path, O_RDONLY); file->path = real_path; #endif @@ -295,7 +301,7 @@ static CURLcode file_upload(struct connectdata *conn) const char *dir = strchr(file->path, DIRSEP); int fd; int mode; - CURLcode res=CURLE_OK; + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; char *buf = data->state.buffer; size_t nread; @@ -309,8 +315,6 @@ static CURLcode file_upload(struct connectdata *conn) * Since FILE: doesn't do the full init, we need to provide some extra * assignments here. */ - conn->fread_func = data->set.fread_func; - conn->fread_in = data->set.in; conn->data->req.upload_fromhere = buf; if(!dir) @@ -351,10 +355,10 @@ static CURLcode file_upload(struct connectdata *conn) data->state.resume_from = (curl_off_t)file_stat.st_size; } - while(res == CURLE_OK) { + while(!result) { int readcount; - res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount); - if(res) + result = Curl_fillreadbuffer(conn, BUFSIZE, &readcount); + if(result) break; if(readcount <= 0) /* fix questionable compare error. curlvms */ @@ -381,7 +385,7 @@ static CURLcode file_upload(struct connectdata *conn) /* write the data to the target */ nwrite = write(fd, buf2, nread); if(nwrite != nread) { - res = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; break; } @@ -390,16 +394,16 @@ static CURLcode file_upload(struct connectdata *conn) Curl_pgrsSetUploadCounter(data, bytecount); if(Curl_pgrsUpdate(conn)) - res = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; else - res = Curl_speedcheck(data, now); + result = Curl_speedcheck(data, now); } - if(!res && Curl_pgrsUpdate(conn)) - res = CURLE_ABORTED_BY_CALLBACK; + if(!result && Curl_pgrsUpdate(conn)) + result = CURLE_ABORTED_BY_CALLBACK; close(fd); - return res; + return result; } /* @@ -417,7 +421,7 @@ static CURLcode file_do(struct connectdata *conn, bool *done) are supported. This means that files on remotely mounted directories (via NFS, Samba, NT sharing) can be accessed through a file:// URL */ - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; struct_stat statbuf; /* struct_stat instead of struct stat just to allow the Windows version to have a different struct without having to redefine the simple word 'stat' */ @@ -464,7 +468,6 @@ static CURLcode file_do(struct connectdata *conn, bool *done) information. Which for FILE can't be much more than the file size and date. */ if(data->set.opt_no_body && data->set.include_header && fstated) { - CURLcode result; snprintf(buf, sizeof(data->state.buffer), "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size); result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0); @@ -546,7 +549,7 @@ static CURLcode file_do(struct connectdata *conn, bool *done) Curl_pgrsTime(data, TIMER_STARTTRANSFER); - while(res == CURLE_OK) { + while(!result) { /* Don't fill a whole buffer if we want less than all data */ size_t bytestoread = (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ? @@ -563,21 +566,21 @@ static CURLcode file_do(struct connectdata *conn, bool *done) bytecount += nread; expected_size -= nread; - res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread); - if(res) - return res; + result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread); + if(result) + return result; Curl_pgrsSetDownloadCounter(data, bytecount); if(Curl_pgrsUpdate(conn)) - res = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; else - res = Curl_speedcheck(data, now); + result = Curl_speedcheck(data, now); } if(Curl_pgrsUpdate(conn)) - res = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; - return res; + return result; } #endif diff --git a/lib/fileinfo.c b/lib/fileinfo.c index 8c8ee98..0904937 100644 --- a/lib/fileinfo.c +++ b/lib/fileinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010-2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,10 +24,6 @@ #include "strdup.h" #include "fileinfo.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" diff --git a/lib/formdata.c b/lib/formdata.c index 3260928..9e8ce4e 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,7 +24,7 @@ #include <curl/curl.h> -#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) +#ifndef CURL_DISABLE_HTTP #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) #include <libgen.h> @@ -34,19 +34,14 @@ #include "formdata.h" #include "vtls/vtls.h" #include "strequal.h" -#include "curl_memory.h" #include "sendf.h" +#include "strdup.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */ - -#ifndef CURL_DISABLE_HTTP - #ifndef HAVE_BASENAME static char *Curl_basename(char *path); #define basename(x) Curl_basename((x)) @@ -214,46 +209,6 @@ static const char *ContentTypeForFilename(const char *filename, /*************************************************************************** * - * memdup() - * - * Copies the 'source' data to a newly allocated buffer buffer (that is - * returned). Uses buffer_length if not null, else uses strlen to determine - * the length of the buffer to be copied - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -static char *memdup(const char *src, size_t buffer_length) -{ - size_t length; - bool add = FALSE; - char *buffer; - - if(buffer_length) - length = buffer_length; - else if(src) { - length = strlen(src); - add = TRUE; - } - else - /* no length and a NULL src pointer! */ - return strdup(""); - - buffer = malloc(length+add); - if(!buffer) - return NULL; /* fail */ - - memcpy(buffer, src, length); - - /* if length unknown do null termination */ - if(add) - buffer[length] = '\0'; - - return buffer; -} - -/*************************************************************************** - * * FormAdd() * * Stores a formpost parameter and builds the appropriate linked list. @@ -460,7 +415,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, else { form = AddFormInfo(fname, NULL, current_form); if(!form) { - Curl_safefree(fname); + free(fname); return_value = CURL_FORMADD_MEMORY; } else { @@ -549,7 +504,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, else { form = AddFormInfo(NULL, type, current_form); if(!form) { - Curl_safefree(type); + free(type); return_value = CURL_FORMADD_MEMORY; } else { @@ -682,9 +637,12 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, (form == first_form) ) { /* Note that there's small risk that form->name is NULL here if the app passed in a bad combo, so we better check for that first. */ - if(form->name) + if(form->name) { /* copy name (without strdup; possibly contains null characters) */ - form->name = memdup(form->name, form->namelength); + form->name = Curl_memdup(form->name, form->namelength? + form->namelength: + strlen(form->name)+1); + } if(!form->name) { return_value = CURL_FORMADD_MEMORY; break; @@ -693,9 +651,11 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | - HTTPPOST_CALLBACK)) ) { + HTTPPOST_CALLBACK)) && form->value) { /* copy value (without strdup; possibly contains null characters) */ - form->value = memdup(form->value, form->contentslength); + form->value = Curl_memdup(form->value, form->contentslength? + form->contentslength: + strlen(form->value)+1); if(!form->value) { return_value = CURL_FORMADD_MEMORY; break; @@ -751,7 +711,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, now by the httppost linked list */ while(first_form) { FormInfo *ptr = first_form->more; - Curl_safefree(first_form); + free(first_form); first_form = ptr; } @@ -796,7 +756,7 @@ curl_off_t VmsRealFileSize(const char * name, int ret_stat; FILE * file; - file = fopen(name, "r"); + file = fopen(name, "r"); /* VMS */ if(file == NULL) return 0; @@ -954,13 +914,13 @@ void Curl_formclean(struct FormData **form_ptr) int curl_formget(struct curl_httppost *form, void *arg, curl_formget_callback append) { - CURLcode rc; + CURLcode result; curl_off_t size; struct FormData *data, *ptr; - rc = Curl_getformdata(NULL, &data, form, NULL, &size); - if(rc != CURLE_OK) - return (int)rc; + result = Curl_getformdata(NULL, &data, form, NULL, &size); + if(result) + return (int)result; for(ptr = data; ptr; ptr = ptr->next) { if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) { @@ -1009,19 +969,16 @@ void curl_formfree(struct curl_httppost *form) next=form->next; /* the following form line */ /* recurse to sub-contents */ - if(form->more) - curl_formfree(form->more); + curl_formfree(form->more); - if(!(form->flags & HTTPPOST_PTRNAME) && form->name) + if(!(form->flags & HTTPPOST_PTRNAME)) free(form->name); /* free the name */ if(!(form->flags & - (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) && - form->contents) + (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) + ) free(form->contents); /* free the contents */ - if(form->contenttype) - free(form->contenttype); /* free the content type */ - if(form->showfilename) - free(form->showfilename); /* free the faked file name */ + free(form->contenttype); /* free the content type */ + free(form->showfilename); /* free the faked file name */ free(form); /* free the struct */ } while((form = next) != NULL); /* continue */ @@ -1111,7 +1068,7 @@ static CURLcode formdata_add_filename(const struct curl_httppost *file, /* filename need be escaped */ filename_escaped = malloc(strlen(filename)*2+1); if(!filename_escaped) { - Curl_safefree(filebasename); + free(filebasename); return CURLE_OUT_OF_MEMORY; } p0 = filename_escaped; @@ -1127,8 +1084,8 @@ static CURLcode formdata_add_filename(const struct curl_httppost *file, result = AddFormDataf(form, size, "; filename=\"%s\"", filename); - Curl_safefree(filename_escaped); - Curl_safefree(filebasename); + free(filename_escaped); + free(filebasename); return result; } @@ -1178,7 +1135,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data, boundary); if(result) { - Curl_safefree(boundary); + free(boundary); return result; } /* we DO NOT include that line in the total size of the POST, since it'll be @@ -1221,7 +1178,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data, /* If used, this is a link to more file names, we must then do the magic to include several files with the same field name */ - Curl_safefree(fileboundary); + free(fileboundary); fileboundary = formboundary(data); if(!fileboundary) { result = CURLE_OUT_OF_MEMORY; @@ -1369,22 +1326,20 @@ CURLcode Curl_getformdata(struct SessionHandle *data, } while((post = post->next) != NULL); /* for each field */ /* end-boundary for everything */ - if(CURLE_OK == result) - result = AddFormDataf(&form, &size, - "\r\n--%s--\r\n", - boundary); + if(!result) + result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary); if(result) { Curl_formclean(&firstform); - Curl_safefree(fileboundary); - Curl_safefree(boundary); + free(fileboundary); + free(boundary); return result; } *sizep = size; - Curl_safefree(fileboundary); - Curl_safefree(boundary); + free(fileboundary); + free(boundary); *finalform = firstform; @@ -1430,7 +1385,7 @@ static FILE * vmsfopenread(const char *file, const char *mode) { case FAB$C_VAR: case FAB$C_VFC: case FAB$C_STMCR: - return fopen(file, "r"); + return fopen(file, "r"); /* VMS */ break; default: return fopen(file, "r", "rfm=stmlf", "ctx=stm"); @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -77,9 +77,7 @@ #include "warnless.h" #include "http_proxy.h" #include "non-ascii.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -157,7 +155,7 @@ static CURLcode ftp_dophase_done(struct connectdata *conn, bool connected); /* easy-to-use macro: */ -#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \ +#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z))) \ return result @@ -285,19 +283,17 @@ static void freedirs(struct ftp_conn *ftpc) int i; if(ftpc->dirs) { for(i=0; i < ftpc->dirdepth; i++) { - if(ftpc->dirs[i]) { - free(ftpc->dirs[i]); - ftpc->dirs[i]=NULL; - } + free(ftpc->dirs[i]); + ftpc->dirs[i]=NULL; } free(ftpc->dirs); ftpc->dirs = NULL; ftpc->dirdepth = 0; } - if(ftpc->file) { - free(ftpc->file); - ftpc->file = NULL; - } + Curl_safefree(ftpc->file); + + /* no longer of any use */ + Curl_safefree(ftpc->newhost); } /* Returns non-zero if the given string contains CR (\r) or LF (\n), @@ -346,7 +342,7 @@ static CURLcode AcceptServerConnect(struct connectdata *conn) infof(data, "Connection accepted from server\n"); conn->sock[SECONDARYSOCKET] = s; - curlx_nonblock(s, TRUE); /* enable non-blocking */ + (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ conn->sock_accepted[SECONDARYSOCKET] = TRUE; if(data->set.fsockopt) { @@ -541,7 +537,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) { struct SessionHandle *data = conn->data; long timeout_ms; - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; *connected = FALSE; infof(data, "Preparing for accepting server on data port\n"); @@ -557,22 +553,22 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) } /* see if the connection request is already here */ - ret = ReceivedServerConnect(conn, connected); - if(ret) - return ret; + result = ReceivedServerConnect(conn, connected); + if(result) + return result; if(*connected) { - ret = AcceptServerConnect(conn); - if(ret) - return ret; + result = AcceptServerConnect(conn); + if(result) + return result; - ret = InitiateTransfer(conn); - if(ret) - return ret; + result = InitiateTransfer(conn); + if(result) + return result; } else { /* Add timeout to multi handle and break out of the loop */ - if(ret == CURLE_OK && *connected == FALSE) { + if(!result && *connected == FALSE) { if(data->set.accepttimeout > 0) Curl_expire(data, data->set.accepttimeout); else @@ -580,7 +576,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) } } - return ret; + return result; } /* macro to check for a three-digit ftp status code at the start of the @@ -821,12 +817,19 @@ static void _state(struct connectdata *conn, ) { struct ftp_conn *ftpc = &conn->proto.ftpc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + +#if defined(DEBUGBUILD) + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) lineno; +#else if(ftpc->state != newstate) infof(conn->data, "FTP %p (line %d) state change from %s to %s\n", (void *)ftpc, lineno, ftp_state_names[ftpc->state], ftp_state_names[newstate]); #endif +#endif + ftpc->state = newstate; } @@ -1072,8 +1075,9 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, if(*addr != '\0') { /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(conn->ip_addr->ai_family, conn->scope, addr, - hbuf, sizeof(hbuf))) { + switch(Curl_if2ip(conn->ip_addr->ai_family, + Curl_ipv6_scope(conn->ip_addr->ai_addr), + conn->scope_id, addr, hbuf, sizeof(hbuf))) { case IF2IP_NOT_FOUND: /* not an interface, use the given string as host name instead */ host = addr; @@ -1097,7 +1101,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(conn, SOCKERRNO) ); - Curl_safefree(addr); + free(addr); return CURLE_FTP_PORT_FAILED; } switch(sa->sa_family) { @@ -1129,11 +1133,11 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, if(res == NULL) { failf(data, "failed to resolve the address provided to PORT: %s", host); - Curl_safefree(addr); + free(addr); return CURLE_FTP_PORT_FAILED; } - Curl_safefree(addr); + free(addr); host = NULL; /* step 2, create a socket for the requested address */ @@ -1246,10 +1250,10 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, continue; if((PORT == fcmd) && sa->sa_family != AF_INET) - /* PORT is ipv4 only */ + /* PORT is IPv4 only */ continue; - switch (sa->sa_family) { + switch(sa->sa_family) { case AF_INET: port = ntohs(sa4->sin_port); break; @@ -1487,13 +1491,13 @@ static CURLcode ftp_state_list(struct connectdata *conn) The other ftp_filemethods will CWD into dir/dir/ first and then just do LIST (in that case: nothing to do here) */ - char *cmd,*lstArg,*slashPos; + char *cmd, *lstArg, *slashPos; lstArg = NULL; if((data->set.ftp_filemethod == FTPFILE_NOCWD) && data->state.path && data->state.path[0] && - strchr(data->state.path,'/')) { + strchr(data->state.path, '/')) { lstArg = strdup(data->state.path); if(!lstArg) @@ -1503,7 +1507,7 @@ static CURLcode ftp_state_list(struct connectdata *conn) if(lstArg[strlen(lstArg) - 1] != '/') { /* chop off the file part if format is dir/dir/file */ - slashPos = strrchr(lstArg,'/'); + slashPos = strrchr(lstArg, '/'); if(slashPos) *(slashPos+1) = '\0'; } @@ -1517,19 +1521,16 @@ static CURLcode ftp_state_list(struct connectdata *conn) lstArg? lstArg: "" ); if(!cmd) { - if(lstArg) - free(lstArg); + free(lstArg); return CURLE_OUT_OF_MEMORY; } result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd); - if(lstArg) - free(lstArg); - + free(lstArg); free(cmd); - if(result != CURLE_OK) + if(result) return result; state(conn, FTP_LIST); @@ -1669,8 +1670,8 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn, BUFSIZE : curlx_sotouz(data->state.resume_from - passed); size_t actuallyread = - conn->fread_func(data->state.buffer, 1, readthisamountnow, - conn->fread_in); + data->set.fread_func(data->state.buffer, 1, readthisamountnow, + data->set.in); passed += actuallyread; if((actuallyread == 0) || (actuallyread > readthisamountnow)) { @@ -1807,6 +1808,13 @@ static CURLcode ftp_state_quote(struct connectdata *conn, static CURLcode ftp_epsv_disable(struct connectdata *conn) { CURLcode result = CURLE_OK; + + if(conn->bits.ipv6) { + /* We can't disable EPSV when doing IPv6, so this is instead a fail */ + failf(conn->data, "Failed EPSV attempt, exiting\n"); + return CURLE_FTP_WEIRD_SERVER_REPLY; + } + infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n"); /* disable it for next transfer */ conn->bits.ftp_use_epsv = FALSE; @@ -1828,9 +1836,15 @@ static CURLcode proxy_magic(struct connectdata *conn, bool *magicdone) { CURLcode result = CURLE_OK; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; + +#if defined(CURL_DISABLE_PROXY) + (void) newhost; + (void) newport; +#endif *magicdone = FALSE; + switch(conn->proxytype) { case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: @@ -1873,7 +1887,7 @@ static CURLcode proxy_magic(struct connectdata *conn, memset(&http_proxy, 0, sizeof(http_proxy)); data->req.protop = &http_proxy; - result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); + result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport, TRUE); data->req.protop = ftp_save; @@ -1888,9 +1902,26 @@ static CURLcode proxy_magic(struct connectdata *conn, else *magicdone = TRUE; } + return result; } +static char *control_address(struct connectdata *conn) +{ + /* Returns the control connection IP address. + If a proxy tunnel is used, returns the original host name instead, because + the effective control connection address is the proxy address, + not the ftp host. */ + if(conn->bits.tunnel_proxy || + conn->proxytype == CURLPROXY_SOCKS5 || + conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME || + conn->proxytype == CURLPROXY_SOCKS4 || + conn->proxytype == CURLPROXY_SOCKS4A) + return conn->host.name; + + return conn->ip_addr_str; +} + static CURLcode ftp_state_pasv_resp(struct connectdata *conn, int ftpcode) { @@ -1902,6 +1933,9 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, unsigned short connectport; /* the local port connect() should use! */ char *str=&data->state.buffer[4]; /* start on the first letter */ + /* if we come here again, make sure the former name is cleared */ + Curl_safefree(ftpc->newhost); + if((ftpc->count1 == 0) && (ftpcode == 229)) { /* positive EPSV response */ @@ -1910,12 +1944,12 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, unsigned int num; char separator[4]; ptr++; - if(5 == sscanf(ptr, "%c%c%c%u%c", - &separator[0], - &separator[1], - &separator[2], - &num, - &separator[3])) { + if(5 == sscanf(ptr, "%c%c%c%u%c", + &separator[0], + &separator[1], + &separator[2], + &num, + &separator[3])) { const char sep1 = separator[0]; int i; @@ -1933,19 +1967,9 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, } if(ptr) { ftpc->newport = (unsigned short)(num & 0xffff); - - if(conn->bits.tunnel_proxy || - conn->proxytype == CURLPROXY_SOCKS5 || - conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME || - conn->proxytype == CURLPROXY_SOCKS4 || - conn->proxytype == CURLPROXY_SOCKS4A) - /* proxy tunnel -> use other host info because ip_addr_str is the - proxy address not the ftp host */ - snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", - conn->host.name); - else - /* use the same IP we are already connected to */ - snprintf(ftpc->newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str); + ftpc->newhost = strdup(control_address(conn)); + if(!ftpc->newhost) + return CURLE_OUT_OF_MEMORY; } } else @@ -1973,8 +1997,8 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, */ while(*str) { if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d", - &ip[0], &ip[1], &ip[2], &ip[3], - &port[0], &port[1])) + &ip[0], &ip[1], &ip[2], &ip[3], + &port[0], &port[1])) break; str++; } @@ -1986,26 +2010,19 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, /* we got OK from server */ if(data->set.ftp_skip_ip) { - /* told to ignore the remotely given IP but instead use the one we used + /* told to ignore the remotely given IP but instead use the host we used for the control connection */ - infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n", + infof(data, "Skip %d.%d.%d.%d for data connection, re-use %s instead\n", ip[0], ip[1], ip[2], ip[3], - conn->ip_addr_str); - if(conn->bits.tunnel_proxy || - conn->proxytype == CURLPROXY_SOCKS5 || - conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME || - conn->proxytype == CURLPROXY_SOCKS4 || - conn->proxytype == CURLPROXY_SOCKS4A) - /* proxy tunnel -> use other host info because ip_addr_str is the - proxy address not the ftp host */ - snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", conn->host.name); - else - snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", - conn->ip_addr_str); + conn->host.name); + ftpc->newhost = strdup(control_address(conn)); } else - snprintf(ftpc->newhost, sizeof(ftpc->newhost), - "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + ftpc->newhost = aprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + + if(!ftpc->newhost) + return CURLE_OUT_OF_MEMORY; + ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff); } else if(ftpc->count1 == 0) { @@ -2056,9 +2073,8 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; result = Curl_connecthost(conn, addr); - Curl_resolv_unlock(data, addr); /* we're done using this address */ - if(result) { + Curl_resolv_unlock(data, addr); /* we're done using this address */ if(ftpc->count1 == 0 && ftpcode == 229) return ftp_epsv_disable(conn); @@ -2074,8 +2090,9 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, if(data->set.verbose) /* this just dumps information about this second connection */ - ftp_pasv_verbose(conn, conn->ip_addr, ftpc->newhost, connectport); + ftp_pasv_verbose(conn, addr->addr, ftpc->newhost, connectport); + Curl_resolv_unlock(data, addr); /* we're done using this address */ conn->bits.do_more = TRUE; state(conn, FTP_STOP); /* this phase is completed */ @@ -2090,7 +2107,9 @@ static CURLcode ftp_state_port_resp(struct connectdata *conn, ftpport fcmd = (ftpport)ftpc->count1; CURLcode result = CURLE_OK; - if(ftpcode != 200) { + /* The FTP spec tells a positive response should have code 200. + Be more permissive here to tolerate deviant servers. */ + if(ftpcode / 100 != 2) { /* the command failed */ if(EPRT == fcmd) { @@ -2714,7 +2733,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) set a valid level */ Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]); - if(Curl_sec_login(conn) != CURLE_OK) + if(Curl_sec_login(conn)) infof(data, "Logging in with password in cleartext!\n"); else infof(data, "Authentication successful\n"); @@ -2765,7 +2784,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if((ftpcode == 234) || (ftpcode == 334)) { /* Curl_ssl_connect is BLOCKING */ result = Curl_ssl_connect(conn, FIRSTSOCKET); - if(CURLE_OK == result) { + if(!result) { conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */ result = ftp_state_user(conn); } @@ -2907,7 +2926,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(!ftpc->server_os && dir[0] != '/') { result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST"); - if(result != CURLE_OK) { + if(result) { free(dir); return result; } @@ -2960,7 +2979,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(strequal(os, "OS/400")) { /* Force OS400 name format 1. */ result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1"); - if(result != CURLE_OK) { + if(result) { free(os); return result; } @@ -3254,8 +3273,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, } /* now store a copy of the directory we are in */ - if(ftpc->prevpath) - free(ftpc->prevpath); + free(ftpc->prevpath); if(data->set.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { @@ -3304,7 +3322,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, /* shut down the socket to inform the server we're done */ #ifdef _WIN32_WCE - shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */ + shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */ #endif if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { @@ -3627,7 +3645,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) { /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port aren't used so we blank their arguments. TODO: make this nicer */ - result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0); + result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0, FALSE); return result; } @@ -3736,7 +3754,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) return result; } - if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY)) + if(!result && (ftp->transfer != FTPTRANSFER_BODY)) /* no data to transfer. FIX: it feels like a kludge to have this here too! */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); @@ -3801,7 +3819,7 @@ static void wc_data_dtor(void *ptr) struct ftp_wc_tmpdata *tmp = ptr; if(tmp) Curl_ftp_parselist_data_free(&tmp->parser); - Curl_safefree(tmp); + free(tmp); } static CURLcode init_wc_data(struct connectdata *conn) @@ -3809,7 +3827,7 @@ static CURLcode init_wc_data(struct connectdata *conn) char *last_slash; char *path = conn->data->state.path; struct WildcardData *wildcard = &(conn->data->wildcard); - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; struct ftp_wc_tmpdata *ftp_tmp; last_slash = strrchr(conn->data->state.path, '/'); @@ -3817,8 +3835,8 @@ static CURLcode init_wc_data(struct connectdata *conn) last_slash++; if(last_slash[0] == '\0') { wildcard->state = CURLWC_CLEAN; - ret = ftp_parse_url_path(conn); - return ret; + result = ftp_parse_url_path(conn); + return result; } else { wildcard->pattern = strdup(last_slash); @@ -3836,8 +3854,8 @@ static CURLcode init_wc_data(struct connectdata *conn) } else { /* only list */ wildcard->state = CURLWC_CLEAN; - ret = ftp_parse_url_path(conn); - return ret; + result = ftp_parse_url_path(conn); + return result; } } @@ -3855,7 +3873,7 @@ static CURLcode init_wc_data(struct connectdata *conn) ftp_tmp->parser = Curl_ftp_parselist_data_alloc(); if(!ftp_tmp->parser) { Curl_safefree(wildcard->pattern); - Curl_safefree(ftp_tmp); + free(ftp_tmp); return CURLE_OUT_OF_MEMORY; } @@ -3867,13 +3885,13 @@ static CURLcode init_wc_data(struct connectdata *conn) conn->data->set.ftp_filemethod = FTPFILE_MULTICWD; /* try to parse ftp url */ - ret = ftp_parse_url_path(conn); - if(ret) { + result = ftp_parse_url_path(conn); + if(result) { Curl_safefree(wildcard->pattern); wildcard->tmp_dtor(wildcard->tmp); wildcard->tmp_dtor = ZERO_NULL; wildcard->tmp = NULL; - return ret; + return result; } wildcard->path = strdup(conn->data->state.path); @@ -3902,16 +3920,16 @@ static CURLcode init_wc_data(struct connectdata *conn) static CURLcode wc_statemach(struct connectdata *conn) { struct WildcardData * const wildcard = &(conn->data->wildcard); - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; switch (wildcard->state) { case CURLWC_INIT: - ret = init_wc_data(conn); + result = init_wc_data(conn); if(wildcard->state == CURLWC_CLEAN) /* only listing! */ break; else - wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING; + wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; break; case CURLWC_MATCHING: { @@ -3975,10 +3993,9 @@ static CURLcode wc_statemach(struct connectdata *conn) if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) ftpc->known_filesize = finfo->size; - ret = ftp_parse_url_path(conn); - if(ret) { - return ret; - } + result = ftp_parse_url_path(conn); + if(result) + return result; /* we don't need the Curl_fileinfo of first file anymore */ Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL); @@ -4002,11 +4019,11 @@ static CURLcode wc_statemach(struct connectdata *conn) case CURLWC_CLEAN: { struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp; - ret = CURLE_OK; - if(ftp_tmp) { - ret = Curl_ftp_parselist_geterror(ftp_tmp->parser); - } - wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE; + result = CURLE_OK; + if(ftp_tmp) + result = Curl_ftp_parselist_geterror(ftp_tmp->parser); + + wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; } break; case CURLWC_DONE: @@ -4014,7 +4031,7 @@ static CURLcode wc_statemach(struct connectdata *conn) break; } - return ret; + return result; } /*********************************************************************** @@ -4028,31 +4045,31 @@ static CURLcode wc_statemach(struct connectdata *conn) */ static CURLcode ftp_do(struct connectdata *conn, bool *done) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; struct ftp_conn *ftpc = &conn->proto.ftpc; *done = FALSE; /* default to false */ ftpc->wait_data_conn = FALSE; /* default to no such wait */ if(conn->data->set.wildcardmatch) { - retcode = wc_statemach(conn); + result = wc_statemach(conn); if(conn->data->wildcard.state == CURLWC_SKIP || conn->data->wildcard.state == CURLWC_DONE) { /* do not call ftp_regular_transfer */ return CURLE_OK; } - if(retcode) /* error, loop or skipping the file */ - return retcode; + if(result) /* error, loop or skipping the file */ + return result; } else { /* no wildcard FSM needed */ - retcode = ftp_parse_url_path(conn); - if(retcode) - return retcode; + result = ftp_parse_url_path(conn); + if(result) + return result; } - retcode = ftp_regular_transfer(conn, done); + result = ftp_regular_transfer(conn, done); - return retcode; + return result; } @@ -4064,7 +4081,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, char s[SBUF_SIZE]; size_t write_len; char *sptr=s; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; #ifdef HAVE_GSSAPI enum protection_level data_sec = conn->data_prot; #endif @@ -4079,23 +4096,23 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, bytes_written=0; - res = Curl_convert_to_network(conn->data, s, write_len); + result = Curl_convert_to_network(conn->data, s, write_len); /* Curl_convert_to_network calls failf if unsuccessful */ - if(res) - return(res); + if(result) + return result; for(;;) { #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, - &bytes_written); + result = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, + &bytes_written); #ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif - if(CURLE_OK != res) + if(result) break; if(conn->data->set.verbose) @@ -4110,7 +4127,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, break; } - return res; + return result; } /*********************************************************************** @@ -4181,14 +4198,10 @@ static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection) } freedirs(ftpc); - if(ftpc->prevpath) { - free(ftpc->prevpath); - ftpc->prevpath = NULL; - } - if(ftpc->server_os) { - free(ftpc->server_os); - ftpc->server_os = NULL; - } + free(ftpc->prevpath); + ftpc->prevpath = NULL; + free(ftpc->server_os); + ftpc->server_os = NULL; Curl_pp_disconnect(pp); @@ -4479,7 +4492,7 @@ CURLcode ftp_regular_transfer(struct connectdata *conn, &connected, /* have we connected after PASV/PORT */ dophase_done); /* all commands in the DO-phase done? */ - if(CURLE_OK == result) { + if(!result) { if(!*dophase_done) /* the DO phase has not completed yet */ @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -147,11 +147,10 @@ struct ftp_conn { curl_off_t known_filesize; /* file size is different from -1, if wildcard LIST parsing was done and wc_statemach set it */ - /* newhost must be able to hold a full IP-style address in ASCII, which - in the IPv6 case means 5*8-1 = 39 letters */ -#define NEWHOST_BUFSIZE 48 - char newhost[NEWHOST_BUFSIZE]; /* this is the pair to connect the DATA... */ - unsigned short newport; /* connection to */ + /* newhost is the (allocated) IP addr or host name to connect the data + connection to */ + char *newhost; /* this is the pair to connect the DATA... */ + unsigned short newport; /* connection to */ }; diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 4a46dd1..17e0a66 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,13 +23,13 @@ /** * Now implemented: * - * 1) UNIX version 1 + * 1) Unix version 1 * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog - * 2) UNIX version 2 + * 2) Unix version 2 * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog - * 3) UNIX version 3 + * 3) Unix version 3 * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog - * 4) UNIX symlink + * 4) Unix symlink * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000 * 5) DOS style * 01-29-97 11:32PM <DIR> prog @@ -49,10 +49,6 @@ #include "ftp.h" #include "ftplistparser.h" #include "curl_fnmatch.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -191,8 +187,7 @@ struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data) { - if(*pl_data) - free(*pl_data); + free(*pl_data); *pl_data = NULL; } @@ -365,7 +360,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, struct ftp_parselist_data *parser = tmpdata->parser; struct curl_fileinfo *finfo; unsigned long i = 0; - CURLcode rc; + CURLcode result; if(parser->error) { /* error in previous call */ /* scenario: @@ -758,9 +753,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.filename = parser->item_offset; parser->state.UNIX.main = PL_UNIX_FILETYPE; - rc = ftp_pl_insert_finfo(conn, finfo); - if(rc) { - PL_ERROR(conn, rc); + result = ftp_pl_insert_finfo(conn, finfo); + if(result) { + PL_ERROR(conn, result); return bufflen; } } @@ -770,9 +765,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, finfo->b_data[parser->item_offset + parser->item_length] = 0; parser->offsets.filename = parser->item_offset; parser->state.UNIX.main = PL_UNIX_FILETYPE; - rc = ftp_pl_insert_finfo(conn, finfo); - if(rc) { - PL_ERROR(conn, rc); + result = ftp_pl_insert_finfo(conn, finfo); + if(result) { + PL_ERROR(conn, result); return bufflen; } } @@ -866,9 +861,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, else if(c == '\n') { finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.symlink_target = parser->item_offset; - rc = ftp_pl_insert_finfo(conn, finfo); - if(rc) { - PL_ERROR(conn, rc); + result = ftp_pl_insert_finfo(conn, finfo); + if(result) { + PL_ERROR(conn, result); return bufflen; } parser->state.UNIX.main = PL_UNIX_FILETYPE; @@ -878,9 +873,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, if(c == '\n') { finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.symlink_target = parser->item_offset; - rc = ftp_pl_insert_finfo(conn, finfo); - if(rc) { - PL_ERROR(conn, rc); + result = ftp_pl_insert_finfo(conn, finfo); + if(result) { + PL_ERROR(conn, result); return bufflen; } parser->state.UNIX.main = PL_UNIX_FILETYPE; @@ -1011,9 +1006,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->offsets.filename = parser->item_offset; finfo->b_data[finfo->b_used - 1] = 0; parser->offsets.filename = parser->item_offset; - rc = ftp_pl_insert_finfo(conn, finfo); - if(rc) { - PL_ERROR(conn, rc); + result = ftp_pl_insert_finfo(conn, finfo); + if(result) { + PL_ERROR(conn, result); return bufflen; } parser->state.NT.main = PL_WINNT_DATE; @@ -1023,9 +1018,9 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, case PL_WINNT_FILENAME_WINEOL: if(c == '\n') { parser->offsets.filename = parser->item_offset; - rc = ftp_pl_insert_finfo(conn, finfo); - if(rc) { - PL_ERROR(conn, rc); + result = ftp_pl_insert_finfo(conn, finfo); + if(result) { + PL_ERROR(conn, result); return bufflen; } parser->state.NT.main = PL_WINNT_DATE; @@ -1041,7 +1036,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, } break; default: - return bufflen+1; + return bufflen + 1; } i++; diff --git a/lib/getinfo.c b/lib/getinfo.c index 8905d36..910f520 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,12 +27,12 @@ #include "urldata.h" #include "getinfo.h" -#include "curl_memory.h" #include "vtls/vtls.h" #include "connect.h" /* Curl_getconnectinfo() */ #include "progress.h" -/* Make this the last #include */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* @@ -42,7 +42,7 @@ CURLcode Curl_initinfo(struct SessionHandle *data) { struct Progress *pro = &data->progress; - struct PureInfo *info =&data->info; + struct PureInfo *info = &data->info; pro->t_nslookup = 0; pro->t_connect = 0; @@ -58,8 +58,7 @@ CURLcode Curl_initinfo(struct SessionHandle *data) info->filetime = -1; /* -1 is an illegal time and thus means unknown */ info->timecond = FALSE; - if(info->contenttype) - free(info->contenttype); + free(info->contenttype); info->contenttype = NULL; info->header_size = 0; @@ -116,6 +115,7 @@ static CURLcode getinfo_char(struct SessionHandle *data, CURLINFO info, default: return CURLE_BAD_FUNCTION_ARGUMENT; } + return CURLE_OK; } @@ -202,6 +202,7 @@ static CURLcode getinfo_long(struct SessionHandle *data, CURLINFO info, default: return CURLE_BAD_FUNCTION_ARGUMENT; } + return CURLE_OK; } @@ -254,6 +255,7 @@ static CURLcode getinfo_double(struct SessionHandle *data, CURLINFO info, default: return CURLE_BAD_FUNCTION_ARGUMENT; } + return CURLE_OK; } @@ -261,8 +263,8 @@ static CURLcode getinfo_slist(struct SessionHandle *data, CURLINFO info, struct curl_slist **param_slistp) { union { - struct curl_certinfo * to_certinfo; - struct curl_slist * to_slist; + struct curl_certinfo *to_certinfo; + struct curl_slist *to_slist; } ptr; switch(info) { @@ -303,7 +305,7 @@ static CURLcode getinfo_slist(struct SessionHandle *data, CURLINFO info, break; /* no SSL session found */ /* Return the TLS session information from the relevant backend */ -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL internals = conn->ssl[sockindex].ctx; #endif #ifdef USE_GNUTLS @@ -312,9 +314,6 @@ static CURLcode getinfo_slist(struct SessionHandle *data, CURLINFO info, #ifdef USE_NSS internals = conn->ssl[sockindex].handle; #endif -#ifdef USE_QSOSSL - internals = conn->ssl[sockindex].handle; -#endif #ifdef USE_GSKIT internals = conn->ssl[sockindex].handle; #endif @@ -331,22 +330,23 @@ static CURLcode getinfo_slist(struct SessionHandle *data, CURLINFO info, default: return CURLE_BAD_FUNCTION_ARGUMENT; } + return CURLE_OK; } CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) { va_list arg; - long *param_longp=NULL; - double *param_doublep=NULL; - char **param_charp=NULL; - struct curl_slist **param_slistp=NULL; + long *param_longp = NULL; + double *param_doublep = NULL; + char **param_charp = NULL; + struct curl_slist **param_slistp = NULL; int type; /* default return code is to error out! */ - CURLcode ret = CURLE_BAD_FUNCTION_ARGUMENT; + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; if(!data) - return ret; + return result; va_start(arg, info); @@ -354,28 +354,29 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) switch(type) { case CURLINFO_STRING: param_charp = va_arg(arg, char **); - if(NULL != param_charp) - ret = getinfo_char(data, info, param_charp); + if(param_charp) + result = getinfo_char(data, info, param_charp); break; case CURLINFO_LONG: param_longp = va_arg(arg, long *); - if(NULL != param_longp) - ret = getinfo_long(data, info, param_longp); + if(param_longp) + result = getinfo_long(data, info, param_longp); break; case CURLINFO_DOUBLE: param_doublep = va_arg(arg, double *); - if(NULL != param_doublep) - ret = getinfo_double(data, info, param_doublep); + if(param_doublep) + result = getinfo_double(data, info, param_doublep); break; case CURLINFO_SLIST: param_slistp = va_arg(arg, struct curl_slist **); - if(NULL != param_slistp) - ret = getinfo_slist(data, info, param_slistp); + if(param_slistp) + result = getinfo_slist(data, info, param_slistp); break; default: break; } va_end(arg); - return ret; + + return result; } diff --git a/lib/gopher.c b/lib/gopher.c index b1dd65f..954cad8 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,10 +36,6 @@ #include "select.h" #include "url.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -121,10 +117,10 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) for(;;) { result = Curl_write(conn, sockfd, sel, k, &amount); - if(CURLE_OK == result) { /* Which may not have written it all! */ + if(!result) { /* Which may not have written it all! */ result = Curl_client_write(conn, CLIENTWRITE_HEADER, sel, amount); if(result) { - Curl_safefree(sel_org); + free(sel_org); return result; } k -= amount; @@ -134,7 +130,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) } else { failf(data, "Failed sending Gopher request"); - Curl_safefree(sel_org); + free(sel_org); return result; } /* Don't busyloop. The entire loop thing is a work-around as it causes a @@ -149,12 +145,12 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) Curl_socket_ready(CURL_SOCKET_BAD, sockfd, 100); } - Curl_safefree(sel_org); + free(sel_org); /* We can use Curl_sendf to send the terminal \r\n relatively safely and save allocing another string/doing another _write loop. */ result = Curl_sendf(sockfd, conn, "\r\n"); - if(result != CURLE_OK) { + if(result) { failf(data, "Failed sending Gopher request"); return result; } @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,10 +24,6 @@ #include "hash.h" #include "llist.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -93,32 +89,6 @@ Curl_hash_init(struct curl_hash *h, } } -struct curl_hash * -Curl_hash_alloc(int slots, - hash_function hfunc, - comp_function comparator, - curl_hash_dtor dtor) -{ - struct curl_hash *h; - - if(!slots || !hfunc || !comparator ||!dtor) { - return NULL; /* failure */ - } - - h = malloc(sizeof(struct curl_hash)); - if(h) { - if(Curl_hash_init(h, slots, hfunc, comparator, dtor)) { - /* failure */ - free(h); - h = NULL; - } - } - - return h; -} - - - static struct curl_hash_element * mk_hash_element(const void *key, size_t key_len, const void *p) { @@ -242,8 +212,11 @@ Curl_hash_apply(curl_hash *h, void *user, } #endif +/* Destroys all the entries in the given hash and resets its attributes, + * prepping the given hash for [static|dynamic] deallocation. + */ void -Curl_hash_clean(struct curl_hash *h) +Curl_hash_destroy(struct curl_hash *h) { int i; @@ -257,6 +230,17 @@ Curl_hash_clean(struct curl_hash *h) h->slots = 0; } +/* Removes all the entries in the given hash. + * + * @unittest: 1602 + */ +void +Curl_hash_clean(struct curl_hash *h) +{ + Curl_hash_clean_with_criterium(h, NULL, NULL); +} + +/* Cleans all entries that pass the comp function criteria. */ void Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, int (*comp)(void *, void *)) @@ -276,7 +260,7 @@ Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, struct curl_hash_element *he = le->ptr; lnext = le->next; /* ask the callback function if we shall remove this entry or not */ - if(comp(user, he->ptr)) { + if(comp == NULL || comp(user, he->ptr)) { Curl_llist_remove(list, le, (void *) h); --h->size; /* one less entry in the hash now */ } @@ -285,17 +269,6 @@ Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, } } -void -Curl_hash_destroy(struct curl_hash *h) -{ - if(!h) - return; - - Curl_hash_clean(h); - - free(h); -} - size_t Curl_hash_str(void* key, size_t key_length, size_t slots_num) { const char* key_str = (const char *) key; @@ -310,16 +283,11 @@ size_t Curl_hash_str(void* key, size_t key_length, size_t slots_num) return (h % slots_num); } -size_t Curl_str_key_compare(void*k1, size_t key1_len, void*k2, size_t key2_len) +size_t Curl_str_key_compare(void *k1, size_t key1_len, + void *k2, size_t key2_len) { - char *key1 = (char *)k1; - char *key2 = (char *)k2; - - if(key1_len == key2_len && - *key1 == *key2 && - memcmp(key1, key2, key1_len) == 0) { + if((key1_len == key2_len) && !memcmp(k1, k2, key1_len)) return 1; - } return 0; } @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -74,22 +74,16 @@ int Curl_hash_init(struct curl_hash *h, comp_function comparator, curl_hash_dtor dtor); -struct curl_hash *Curl_hash_alloc(int slots, - hash_function hfunc, - comp_function comparator, - curl_hash_dtor dtor); - void *Curl_hash_add(struct curl_hash *h, void *key, size_t key_len, void *p); int Curl_hash_delete(struct curl_hash *h, void *key, size_t key_len); void *Curl_hash_pick(struct curl_hash *, void * key, size_t key_len); void Curl_hash_apply(struct curl_hash *h, void *user, void (*cb)(void *user, void *ptr)); int Curl_hash_count(struct curl_hash *h); +void Curl_hash_destroy(struct curl_hash *h); void Curl_hash_clean(struct curl_hash *h); void Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, int (*comp)(void *, void *)); -void Curl_hash_destroy(struct curl_hash *h); - size_t Curl_hash_str(void* key, size_t key_length, size_t slots_num); size_t Curl_str_key_compare(void*k1, size_t key1_len, void*k2, size_t key2_len); @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,10 +27,6 @@ #ifndef CURL_DISABLE_CRYPTO_AUTH #include "curl_hmac.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" diff --git a/lib/hostasyn.c b/lib/hostasyn.c index 8151b67..17b8be0 100644 --- a/lib/hostasyn.c +++ b/lib/hostasyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,10 +47,6 @@ #include "share.h" #include "strerror.h" #include "url.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -75,7 +71,7 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn, struct Curl_addrinfo *ai) { struct Curl_dns_entry *dns = NULL; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; conn->async.status = status; @@ -92,14 +88,14 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn, if(!dns) { /* failed to store, cleanup and return error */ Curl_freeaddrinfo(ai); - rc = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } else { - rc = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } } @@ -110,9 +106,9 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn, async struct */ conn->async.done = TRUE; - /* ipv4: The input hostent struct will be freed by ares when we return from + /* IPv4: The input hostent struct will be freed by ares when we return from this function */ - return rc; + return result; } /* Call this function after Curl_connect() has returned async=TRUE and @@ -123,21 +119,21 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn, CURLcode Curl_async_resolved(struct connectdata *conn, bool *protocol_done) { - CURLcode code; + CURLcode result; if(conn->async.dns) { conn->dns_entry = conn->async.dns; conn->async.dns = NULL; } - code = Curl_setup_conn(conn, protocol_done); + result = Curl_setup_conn(conn, protocol_done); - if(code) + if(result) /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_disconnect(conn, FALSE); /* close the connection */ - return code; + return result; } /* diff --git a/lib/hostcheck.c b/lib/hostcheck.c index 42eb2ee..62a26e4 100644 --- a/lib/hostcheck.c +++ b/lib/hostcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,8 +22,7 @@ #include "curl_setup.h" -#if defined(USE_SSLEAY) || defined(USE_AXTLS) || defined(USE_QSOSSL) || \ - defined(USE_GSKIT) +#if defined(USE_OPENSSL) || defined(USE_AXTLS) || defined(USE_GSKIT) /* these backends use functions from this file */ #ifdef HAVE_NETINET_IN_H @@ -145,4 +144,4 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) return res; } -#endif /* SSLEAY or AXTLS or QSOSSL or GSKIT */ +#endif /* OPENSSL or AXTLS or GSKIT */ diff --git a/lib/hostip.c b/lib/hostip.c index 73b3f82..82f3897 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -56,10 +56,7 @@ #include "url.h" #include "inet_ntop.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -98,8 +95,8 @@ * hostip.c - method-independent resolver functions and utility functions * hostasyn.c - functions for asynchronous name resolves * hostsyn.c - functions for synchronous name resolves - * hostip4.c - ipv4-specific functions - * hostip6.c - ipv6-specific functions + * hostip4.c - IPv4 specific functions + * hostip6.c - IPv6 specific functions * * The two asynchronous name resolver backends are implemented in: * asyn-ares.c - functions for ares-using name resolves @@ -140,11 +137,7 @@ struct curl_hash *Curl_global_host_cache_init(void) void Curl_global_host_cache_dtor(void) { if(host_cache_initialized) { - /* first make sure that any custom "CURLOPT_RESOLVE" names are - cleared off */ - Curl_hostcache_clean(NULL, &hostname_cache); - /* then free the remaining hash completely */ - Curl_hash_clean(&hostname_cache); + Curl_hash_destroy(&hostname_cache); host_cache_initialized = 0; } } @@ -237,7 +230,8 @@ hostcache_timestamp_remove(void *datap, void *hc) (struct hostcache_prune_data *) datap; struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; - return !c->inuse && (data->now - c->timestamp >= data->cache_timeout); + return (0 != c->timestamp) + && (data->now - c->timestamp >= data->cache_timeout); } /* @@ -283,40 +277,54 @@ void Curl_hostcache_prune(struct SessionHandle *data) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } -/* - * Check if the entry should be pruned. Assumes a locked cache. - */ -static int -remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns) +#ifdef HAVE_SIGSETJMP +/* Beware this is a global and unique instance. This is used to store the + return address that we can jump back to from inside a signal handler. This + is not thread-safe stuff. */ +sigjmp_buf curl_jmpenv; +#endif + +/* lookup address, returns entry if found and not stale */ +static struct Curl_dns_entry * +fetch_addr(struct connectdata *conn, + const char *hostname, + int port) { - struct hostcache_prune_data user; + char *entry_id = NULL; + struct Curl_dns_entry *dns = NULL; + size_t entry_len; + struct SessionHandle *data = conn->data; - if(!dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache || - dns->inuse) - /* cache forever means never prune, and NULL hostcache means we can't do - it, if it still is in use then we leave it */ - return 0; + /* Create an entry id, based upon the hostname and port */ + entry_id = create_hostcache_id(hostname, port); + /* If we can't create the entry id, fail */ + if(!entry_id) + return dns; - time(&user.now); - user.cache_timeout = data->set.dns_cache_timeout; + entry_len = strlen(entry_id); - if(!hostcache_timestamp_remove(&user,dns) ) - return 0; + /* See if its already in our dns cache */ + dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1); - Curl_hash_clean_with_criterium(data->dns.hostcache, - (void *) &user, - hostcache_timestamp_remove); + if(dns && (data->set.dns_cache_timeout != -1)) { + /* See whether the returned entry is stale. Done before we release lock */ + struct hostcache_prune_data user; - return 1; -} + time(&user.now); + user.cache_timeout = data->set.dns_cache_timeout; + if(hostcache_timestamp_remove(&user, dns)) { + infof(data, "Hostname in DNS cache was stale, zapped\n"); + dns = NULL; /* the memory deallocation is being handled by the hash */ + Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1); + } + } -#ifdef HAVE_SIGSETJMP -/* Beware this is a global and unique instance. This is used to store the - return address that we can jump back to from inside a signal handler. This - is not thread-safe stuff. */ -sigjmp_buf curl_jmpenv; -#endif + /* free the allocated entry_id again */ + free(entry_id); + + return dns; +} /* * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. @@ -328,35 +336,27 @@ sigjmp_buf curl_jmpenv; * lookups for the same hostname requested by different handles. * * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after + * use, or we'll leak memory! */ struct Curl_dns_entry * Curl_fetch_addr(struct connectdata *conn, const char *hostname, - int port, int *stale) + int port) { - char *entry_id = NULL; - struct Curl_dns_entry *dns = NULL; - size_t entry_len; struct SessionHandle *data = conn->data; + struct Curl_dns_entry *dns = NULL; - /* Create an entry id, based upon the hostname and port */ - entry_id = create_hostcache_id(hostname, port); - /* If we can't create the entry id, fail */ - if(!entry_id) - return dns; - - entry_len = strlen(entry_id); + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - /* See if its already in our dns cache */ - dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1); + dns = fetch_addr(conn, hostname, port); - /* free the allocated entry_id again */ - free(entry_id); + if(dns) dns->inuse++; /* we use it! */ - /* See whether the returned entry is stale. Done before we release lock */ - *stale = remove_entry_if_stale(data, dns); - if(*stale) - dns = NULL; /* the memory deallocation is being handled by the hash */ + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); return dns; } @@ -395,11 +395,11 @@ Curl_cache_addr(struct SessionHandle *data, return NULL; } - dns->inuse = 0; /* init to not used */ + dns->inuse = 1; /* the cache has the first reference */ dns->addr = addr; /* this is the address(es) */ time(&dns->timestamp); if(dns->timestamp == 0) - dns->timestamp = 1; /* zero indicates that entry isn't in hash table */ + dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */ /* Store the resolved data in our DNS cache. */ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1, @@ -448,21 +448,17 @@ int Curl_resolv(struct connectdata *conn, struct Curl_dns_entry *dns = NULL; struct SessionHandle *data = conn->data; CURLcode result; - int stale, rc = CURLRESOLV_ERROR; /* default to failure */ + int rc = CURLRESOLV_ERROR; /* default to failure */ *entry = NULL; if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - dns = Curl_fetch_addr(conn, hostname, port, &stale); - - infof(data, "Hostname was %sfound in DNS cache\n", dns||stale?"":"NOT "); - if(stale) - infof(data, "Hostname in DNS cache was stale, zapped\n"); - + dns = fetch_addr(conn, hostname, port); if(dns) { + infof(data, "Hostname %s was found in DNS cache\n", hostname); dns->inuse++; /* we use it! */ rc = CURLRESOLV_RESOLVED; } @@ -611,32 +607,6 @@ int Curl_resolv_timeout(struct connectdata *conn, we want to wait less than one second we must bail out already now. */ return CURLRESOLV_TIMEDOUT; - /************************************************************* - * Set signal handler to catch SIGALRM - * Store the old value to be able to set it back later! - *************************************************************/ -#ifdef HAVE_SIGACTION - sigaction(SIGALRM, NULL, &sigact); - keep_sigact = sigact; - keep_copysig = TRUE; /* yes, we have a copy */ - sigact.sa_handler = alarmfunc; -#ifdef SA_RESTART - /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ - sigact.sa_flags &= ~SA_RESTART; -#endif - /* now set the new struct */ - sigaction(SIGALRM, &sigact, NULL); -#else /* HAVE_SIGACTION */ - /* no sigaction(), revert to the much lamer signal() */ -#ifdef HAVE_SIGNAL - keep_sigact = signal(SIGALRM, alarmfunc); -#endif -#endif /* HAVE_SIGACTION */ - - /* alarm() makes a signal get sent when the timeout fires off, and that - will abort system calls */ - prev_alarm = alarm(curlx_sltoui(timeout/1000L)); - /* This allows us to time-out from the name resolver, as the timeout will generate a signal and we will siglongjmp() from that here. This technique has problems (see alarmfunc). @@ -649,6 +619,33 @@ int Curl_resolv_timeout(struct connectdata *conn, rc = CURLRESOLV_ERROR; goto clean_up; } + else { + /************************************************************* + * Set signal handler to catch SIGALRM + * Store the old value to be able to set it back later! + *************************************************************/ +#ifdef HAVE_SIGACTION + sigaction(SIGALRM, NULL, &sigact); + keep_sigact = sigact; + keep_copysig = TRUE; /* yes, we have a copy */ + sigact.sa_handler = alarmfunc; +#ifdef SA_RESTART + /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ + sigact.sa_flags &= ~SA_RESTART; +#endif + /* now set the new struct */ + sigaction(SIGALRM, &sigact, NULL); +#else /* HAVE_SIGACTION */ + /* no sigaction(), revert to the much lamer signal() */ +#ifdef HAVE_SIGNAL + keep_sigact = signal(SIGALRM, alarmfunc); +#endif +#endif /* HAVE_SIGACTION */ + + /* alarm() makes a signal get sent when the timeout fires off, and that + will abort system calls */ + prev_alarm = alarm(curlx_sltoui(timeout/1000L)); + } #else #ifndef CURLRES_ASYNCH @@ -720,54 +717,37 @@ clean_up: */ void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) { - DEBUGASSERT(dns && (dns->inuse>0)); - if(data && data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - dns->inuse--; - /* only free if nobody is using AND it is not in hostcache (timestamp == - 0) */ - if(dns->inuse == 0 && dns->timestamp == 0) { - Curl_freeaddrinfo(dns->addr); - free(dns); - } + freednsentry(dns); if(data && data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } /* - * File-internal: free a cache dns entry. + * File-internal: release cache dns entry reference, free if inuse drops to 0 */ static void freednsentry(void *freethis) { - struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis; + struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis; + DEBUGASSERT(dns && (dns->inuse>0)); - /* mark the entry as not in hostcache */ - p->timestamp = 0; - if(p->inuse == 0) { - Curl_freeaddrinfo(p->addr); - free(p); + dns->inuse--; + if(dns->inuse == 0) { + Curl_freeaddrinfo(dns->addr); + free(dns); } } /* - * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it. + * Curl_mk_dnscache() inits a new DNS cache and returns success/failure. */ -struct curl_hash *Curl_mk_dnscache(void) +int Curl_mk_dnscache(struct curl_hash *hash) { - return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry); -} - -static int hostcache_inuse(void *data, void *hc) -{ - struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; - - if(c->inuse == 1) - Curl_resolv_unlock(data, c); - - return 1; /* free all entries */ + return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare, + freednsentry); } /* @@ -780,11 +760,13 @@ static int hostcache_inuse(void *data, void *hc) void Curl_hostcache_clean(struct SessionHandle *data, struct curl_hash *hash) { - /* Entries added to the hostcache with the CURLOPT_RESOLVE function are - * still present in the cache with the inuse counter set to 1. Detect them - * and cleanup! - */ - Curl_hash_clean_with_criterium(hash, data, hostcache_inuse); + if(data && data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + Curl_hash_clean(hash); + + if(data && data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } @@ -799,18 +781,52 @@ CURLcode Curl_loadhostpairs(struct SessionHandle *data) if(!hostp->data) continue; if(hostp->data[0] == '-') { - /* TODO: mark an entry for removal */ + char *entry_id; + size_t entry_len; + + if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { + infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n", + hostp->data); + continue; + } + + /* Create an entry id, based upon the hostname and port */ + entry_id = create_hostcache_id(hostname, port); + /* If we can't create the entry id, fail */ + if(!entry_id) { + return CURLE_OUT_OF_MEMORY; + } + + entry_len = strlen(entry_id); + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + /* delete entry, ignore if it didn't exist */ + Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + /* free the allocated entry_id again */ + free(entry_id); } - else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port, - address)) { + else { struct Curl_dns_entry *dns; Curl_addrinfo *addr; char *entry_id; size_t entry_len; + if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port, + address)) { + infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n", + hostp->data); + continue; + } + addr = Curl_str2addr(address, port); if(!addr) { - infof(data, "Resolve %s found illegal!\n", hostp->data); + infof(data, "Address in '%s' found illegal!\n", hostp->data); continue; } @@ -833,9 +849,16 @@ CURLcode Curl_loadhostpairs(struct SessionHandle *data) /* free the allocated entry_id again */ free(entry_id); - if(!dns) + if(!dns) { /* if not in the cache already, put this host in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); + if(dns) { + dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */ + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->inuse--; + } + } else /* this is a duplicate, free it again */ Curl_freeaddrinfo(addr); diff --git a/lib/hostip.h b/lib/hostip.h index 4404651..d5b44bc 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -65,11 +65,10 @@ void Curl_global_host_cache_dtor(void); struct Curl_dns_entry { Curl_addrinfo *addr; - /* timestamp == 0 -- entry not in hostcache - timestamp != 0 -- entry is in hostcache */ + /* timestamp == 0 -- CURLOPT_RESOLVE entry, doesn't timeout */ time_t timestamp; - long inuse; /* use-counter, make very sure you decrease this - when you're done using the address you received */ + /* use-counter, use Curl_resolv_unlock to release reference */ + long inuse; }; /* @@ -92,7 +91,7 @@ int Curl_resolv_timeout(struct connectdata *conn, const char *hostname, #ifdef CURLRES_IPV6 /* - * Curl_ipv6works() returns TRUE if ipv6 seems to work. + * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ bool Curl_ipv6works(void); #else @@ -125,8 +124,8 @@ void Curl_resolv_unlock(struct SessionHandle *data, /* for debugging purposes only: */ void Curl_scan_cache_used(void *user, void *ptr); -/* make a new dns cache and return the handle */ -struct curl_hash *Curl_mk_dnscache(void); +/* init a new dns cache and return success */ +int Curl_mk_dnscache(struct curl_hash *hash); /* prune old entries from the DNS cache */ void Curl_hostcache_prune(struct SessionHandle *data); @@ -175,13 +174,14 @@ const char *Curl_printable_address(const Curl_addrinfo *ip, * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. * * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after + * use, or we'll leak memory! */ struct Curl_dns_entry * Curl_fetch_addr(struct connectdata *conn, const char *hostname, - int port, - int *stale); - + int port); /* * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. * diff --git a/lib/hostip4.c b/lib/hostip4.c index 1e39f4a..37b0369 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -48,18 +48,15 @@ #include "strerror.h" #include "url.h" #include "inet_pton.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" /*********************************************************************** - * Only for plain-ipv4 builds + * Only for plain IPv4 builds **********************************************************************/ -#ifdef CURLRES_IPV4 /* plain ipv4 code coming up */ +#ifdef CURLRES_IPV4 /* plain IPv4 code coming up */ /* * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've * been set and returns TRUE if they are OK. @@ -67,7 +64,7 @@ bool Curl_ipvalid(struct connectdata *conn) { if(conn->ip_version == CURL_IPRESOLVE_V6) - /* an ipv6 address was requested and we can't get/use one */ + /* An IPv6 address was requested and we can't get/use one */ return FALSE; return TRUE; /* OK, proceed */ @@ -76,7 +73,7 @@ bool Curl_ipvalid(struct connectdata *conn) #ifdef CURLRES_SYNCH /* - * Curl_getaddrinfo() - the ipv4 synchronous version. + * Curl_getaddrinfo() - the IPv4 synchronous version. * * The original code to this function was from the Dancer source code, written * by Bjorn Reese, it has since been patched and modified considerably. diff --git a/lib/hostip6.c b/lib/hostip6.c index 8327004..6ab131a 100644 --- a/lib/hostip6.c +++ b/lib/hostip6.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -49,16 +49,13 @@ #include "url.h" #include "inet_pton.h" #include "connect.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" /*********************************************************************** - * Only for ipv6-enabled builds + * Only for IPv6-enabled builds **********************************************************************/ #ifdef CURLRES_IPV6 @@ -97,7 +94,7 @@ int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, #endif /* defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO) */ /* - * Curl_ipv6works() returns TRUE if ipv6 seems to work. + * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ bool Curl_ipv6works(void) { @@ -109,7 +106,7 @@ bool Curl_ipv6works(void) /* probe to see if we have a working IPv6 stack */ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); if(s == CURL_SOCKET_BAD) - /* an ipv6 address was requested but we can't get/use one */ + /* an IPv6 address was requested but we can't get/use one */ ipv6_works = 0; else { ipv6_works = 1; @@ -152,7 +149,7 @@ static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai) #endif /* - * Curl_getaddrinfo() when built ipv6-enabled (non-threading and + * Curl_getaddrinfo() when built IPv6-enabled (non-threading and * non-ares version). * * Returns name information about the given hostname and port number. If @@ -192,7 +189,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, } if((pf != PF_INET) && !Curl_ipv6works()) - /* the stack seems to be a non-ipv6 one */ + /* The stack seems to be a non-IPv6 one */ pf = PF_INET; memset(&hints, 0, sizeof(hints)); diff --git a/lib/hostsyn.c b/lib/hostsyn.c index 4ad3c63..fb1de35 100644 --- a/lib/hostsyn.c +++ b/lib/hostsyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,10 +47,6 @@ #include "share.h" #include "strerror.h" #include "url.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -63,7 +63,6 @@ #include "share.h" #include "hostip.h" #include "http.h" -#include "curl_memory.h" #include "select.h" #include "parsedate.h" /* for the week day and month names */ #include "strtoofft.h" @@ -73,15 +72,14 @@ #include "http_proxy.h" #include "warnless.h" #include "non-ascii.h" -#include "bundles.h" +#include "conncache.h" #include "pipeline.h" #include "http2.h" #include "connect.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* @@ -155,12 +153,18 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) { /* allocate the HTTP-specific struct for the SessionHandle, only to survive during this request */ + struct HTTP *http; DEBUGASSERT(conn->data->req.protop == NULL); - conn->data->req.protop = calloc(1, sizeof(struct HTTP)); - if(!conn->data->req.protop) + http = calloc(1, sizeof(struct HTTP)); + if(!http) return CURLE_OUT_OF_MEMORY; + conn->data->req.protop = http; + + Curl_http2_setup_conn(conn); + Curl_http2_setup_req(conn->data); + return CURLE_OK; } @@ -279,7 +283,7 @@ static CURLcode http_output_basic(struct connectdata *conn, bool proxy) char **userp; const char *user; const char *pwd; - CURLcode error; + CURLcode result; if(proxy) { userp = &conn->allocptr.proxyuserpwd; @@ -294,16 +298,16 @@ static CURLcode http_output_basic(struct connectdata *conn, bool proxy) snprintf(data->state.buffer, sizeof(data->state.buffer), "%s:%s", user, pwd); - error = Curl_base64_encode(data, - data->state.buffer, strlen(data->state.buffer), - &authorization, &size); - if(error) - return error; + result = Curl_base64_encode(data, + data->state.buffer, strlen(data->state.buffer), + &authorization, &size); + if(result) + return result; if(!authorization) return CURLE_REMOTE_ACCESS_DENIED; - Curl_safefree(*userp); + free(*userp); *userp = aprintf("%sAuthorization: Basic %s\r\n", proxy?"Proxy-":"", authorization); @@ -392,16 +396,21 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) bytessent = http->writebytecount; - if(conn->bits.authneg) + if(conn->bits.authneg) { /* This is a state where we are known to be negotiating and we don't send any data then. */ expectsend = 0; + } + else if(!conn->bits.protoconnstart) { + /* HTTP CONNECT in progress: there is no body */ + expectsend = 0; + } else { /* figure out how much data we are expected to send */ switch(data->set.httpreq) { case HTTPREQ_POST: - if(data->set.postfieldsize != -1) - expectsend = data->set.postfieldsize; + if(data->state.infilesize != -1) + expectsend = data->state.infilesize; else if(data->set.postfields) expectsend = (curl_off_t)strlen(data->set.postfields); break; @@ -420,6 +429,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) conn->bits.rewindaftersend = FALSE; /* default */ if((expectsend == -1) || (expectsend > bytessent)) { +#if defined(USE_NTLM) /* There is still data left to send */ if((data->state.authproxy.picked == CURLAUTH_NTLM) || (data->state.authhost.picked == CURLAUTH_NTLM) || @@ -439,6 +449,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) return CURLE_OK; } + if(conn->bits.close) /* this is already marked to get closed */ return CURLE_OK; @@ -447,9 +458,9 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) CURL_FORMAT_CURL_OFF_T " bytes\n", (curl_off_t)(expectsend - bytessent)); } +#endif - /* This is not NTLM or many bytes left to send: close - */ + /* This is not NTLM or many bytes left to send: close */ connclose(conn, "Mid-auth HTTP and much data left to send"); data->req.size = 0; /* don't download any more than 0 bytes */ @@ -476,7 +487,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) struct SessionHandle *data = conn->data; bool pickhost = FALSE; bool pickproxy = FALSE; - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; if(100 <= data->req.httpcode && 199 >= data->req.httpcode) /* this is a transient response code, ignore */ @@ -512,9 +523,9 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) if((data->set.httpreq != HTTPREQ_GET) && (data->set.httpreq != HTTPREQ_HEAD) && !conn->bits.rewindaftersend) { - code = http_perhapsrewind(conn); - if(code) - return code; + result = http_perhapsrewind(conn); + if(result) + return result; } } @@ -536,10 +547,10 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) if(http_should_fail(conn)) { failf (data, "The requested URL returned error: %d", data->req.httpcode); - code = CURLE_HTTP_RETURNED_ERROR; + result = CURLE_HTTP_RETURNED_ERROR; } - return code; + return result; } @@ -554,9 +565,11 @@ output_auth_headers(struct connectdata *conn, const char *path, bool proxy) { - struct SessionHandle *data = conn->data; - const char *auth=NULL; + const char *auth = NULL; CURLcode result = CURLE_OK; +#if defined(USE_SPNEGO) || !defined(CURL_DISABLE_VERBOSE_STRINGS) + struct SessionHandle *data = conn->data; +#endif #ifdef USE_SPNEGO struct negotiatedata *negdata = proxy? &data->state.proxyneg:&data->state.negotiate; @@ -672,7 +685,7 @@ Curl_http_output_auth(struct connectdata *conn, if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) || conn->bits.user_passwd) - /* continue please */ ; + /* continue please */; else { authhost->done = TRUE; authproxy->done = TRUE; @@ -773,14 +786,13 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, while(*auth) { #ifdef USE_SPNEGO if(checkprefix("Negotiate", auth)) { - int neg; *availp |= CURLAUTH_NEGOTIATE; authp->avail |= CURLAUTH_NEGOTIATE; if(authp->picked == CURLAUTH_NEGOTIATE) { if(negdata->state == GSS_AUTHSENT || negdata->state == GSS_AUTHNONE) { - neg = Curl_input_negotiate(conn, proxy, auth); - if(neg == 0) { + CURLcode result = Curl_input_negotiate(conn, proxy, auth); + if(!result) { DEBUGASSERT(!data->req.newurl); data->req.newurl = strdup(data->change.url); if(!data->req.newurl) @@ -804,9 +816,8 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, if(authp->picked == CURLAUTH_NTLM || authp->picked == CURLAUTH_NTLM_WB) { /* NTLM authentication is picked and activated */ - CURLcode ntlm = - Curl_input_ntlm(conn, proxy, auth); - if(CURLE_OK == ntlm) { + CURLcode result = Curl_input_ntlm(conn, proxy, auth); + if(!result) { data->state.authproblem = FALSE; #ifdef NTLM_WB_ENABLED if(authp->picked == CURLAUTH_NTLM_WB) { @@ -844,7 +855,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, infof(data, "Ignoring duplicate digest auth header.\n"); } else { - CURLdigest dig; + CURLcode result; *availp |= CURLAUTH_DIGEST; authp->avail |= CURLAUTH_DIGEST; @@ -852,9 +863,8 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, * authentication isn't activated yet, as we need to store the * incoming data from this header in case we are gonna use * Digest. */ - dig = Curl_input_digest(conn, proxy, auth); - - if(CURLDIGEST_FINE != dig) { + result = Curl_input_digest(conn, proxy, auth); + if(result) { infof(data, "Authentication problem. Ignoring this.\n"); data->state.authproblem = TRUE; } @@ -991,8 +1001,8 @@ static size_t readmoredata(char *buffer, /* move backup data into focus and continue on that */ http->postdata = http->backup.postdata; http->postsize = http->backup.postsize; - conn->fread_func = http->backup.fread_func; - conn->fread_in = http->backup.fread_in; + conn->data->set.fread_func = http->backup.fread_func; + conn->data->set.in = http->backup.fread_in; http->sending++; /* move one step up */ @@ -1023,6 +1033,16 @@ Curl_send_buffer *Curl_add_buffer_init(void) } /* + * Curl_add_buffer_free() frees all associated resources. + */ +void Curl_add_buffer_free(Curl_send_buffer *buff) +{ + if(buff) /* deal with NULL input */ + free(buff->buffer); + free(buff); +} + +/* * Curl_add_buffer_send() sends a header buffer and frees all associated * memory. Body data may be appended to the header data if desired. * @@ -1041,7 +1061,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, { ssize_t amount; - CURLcode res; + CURLcode result; char *ptr; size_t size; struct HTTP *http = conn->data->req.protop; @@ -1064,14 +1084,12 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, DEBUGASSERT(size > included_body_bytes); - res = Curl_convert_to_network(conn->data, ptr, headersize); + result = Curl_convert_to_network(conn->data, ptr, headersize); /* Curl_convert_to_network calls failf if unsuccessful */ - if(res) { + if(result) { /* conversion failed, free memory and return to the caller */ - if(in->buffer) - free(in->buffer); - free(in); - return res; + Curl_add_buffer_free(in); + return result; } @@ -1096,9 +1114,9 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, else sendsize = size; - res = Curl_write(conn, sockfd, ptr, sendsize, &amount); + result = Curl_write(conn, sockfd, ptr, sendsize, &amount); - if(CURLE_OK == res) { + if(!result) { /* * Note that we may not send the entire chunk at once, and we have a set * number of data bytes at the end of the big buffer (out of which we may @@ -1139,14 +1157,14 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, ptr = in->buffer + amount; /* backup the currently set pointers */ - http->backup.fread_func = conn->fread_func; - http->backup.fread_in = conn->fread_in; + http->backup.fread_func = conn->data->set.fread_func; + http->backup.fread_in = conn->data->set.in; http->backup.postdata = http->postdata; http->backup.postsize = http->postsize; /* set the new pointers for the request-sending */ - conn->fread_func = (curl_read_callback)readmoredata; - conn->fread_in = (void *)conn; + conn->data->set.fread_func = (curl_read_callback)readmoredata; + conn->data->set.in = (void *)conn; http->postdata = ptr; http->postsize = (curl_off_t)size; @@ -1169,14 +1187,12 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, */ return CURLE_SEND_ERROR; else - conn->writechannel_inuse = FALSE; + Curl_pipeline_leave_write(conn); } } - if(in->buffer) - free(in->buffer); - free(in); + Curl_add_buffer_free(in); - return res; + return result; } @@ -1197,8 +1213,7 @@ CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...) return result; } /* If we failed, we cleanup the whole buffer and return error */ - if(in->buffer) - free(in->buffer); + free(in->buffer); free(in); return CURLE_OUT_OF_MEMORY; } @@ -1378,7 +1393,7 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done) } #endif -#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \ +#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \ defined(USE_DARWINSSL) || defined(USE_POLARSSL) || defined(USE_NSS) /* This function is for OpenSSL, GnuTLS, darwinssl, schannel and polarssl only. It should be made to query the generic SSL layer instead. */ @@ -1417,7 +1432,7 @@ static int https_getsock(struct connectdata *conn, return GETSOCK_BLANK; } #endif /* USE_SSL */ -#endif /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL */ +#endif /* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL */ /* * Curl_http_done() gets called from Curl_done() after a single HTTP request @@ -1428,19 +1443,26 @@ CURLcode Curl_http_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; - struct HTTP *http =data->req.protop; + struct HTTP *http = data->req.protop; +#ifdef USE_NGHTTP2 + struct http_conn *httpc = &conn->proto.httpc; +#endif Curl_unencode_cleanup(conn); #ifdef USE_SPNEGO if(data->state.proxyneg.state == GSS_AUTHSENT || - data->state.negotiate.state == GSS_AUTHSENT) + data->state.negotiate.state == GSS_AUTHSENT) { + /* add forbid re-use if http-code != 401/407 as a WA only needed for + * 401/407 that signal auth failure (empty) otherwise state will be RECV + * with current code */ + if((data->req.httpcode != 401) && (data->req.httpcode != 407)) + connclose(conn, "Negotiate transfer completed"); Curl_cleanup_negotiate(data); + } #endif /* set the proper values (possibly modified on POST) */ - conn->fread_func = data->set.fread_func; /* restore */ - conn->fread_in = data->set.in; /* restore */ conn->seek_func = data->set.seek_func; /* restore */ conn->seek_client = data->set.seek_client; /* restore */ @@ -1448,13 +1470,27 @@ CURLcode Curl_http_done(struct connectdata *conn, return CURLE_OK; if(http->send_buffer) { - Curl_send_buffer *buff = http->send_buffer; - - free(buff->buffer); - free(buff); + Curl_add_buffer_free(http->send_buffer); http->send_buffer = NULL; /* clear the pointer */ } +#ifdef USE_NGHTTP2 + if(http->header_recvbuf) { + DEBUGF(infof(data, "free header_recvbuf!!\n")); + Curl_add_buffer_free(http->header_recvbuf); + http->header_recvbuf = NULL; /* clear the pointer */ + for(; http->push_headers_used > 0; --http->push_headers_used) { + free(http->push_headers[http->push_headers_used - 1]); + } + free(http->push_headers); + http->push_headers = NULL; + } + if(http->stream_id) { + nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0); + http->stream_id = 0; + } +#endif + if(HTTPREQ_POST_FORM == data->set.httpreq) { data->req.bytecount = http->readbytecount + http->writebytecount; @@ -1468,8 +1504,8 @@ CURLcode Curl_http_done(struct connectdata *conn, else if(HTTPREQ_PUT == data->set.httpreq) data->req.bytecount = http->readbytecount + http->writebytecount; - if(status != CURLE_OK) - return (status); + if(status) + return status; if(!premature && /* this check is pointless when DONE is called before the entire operation is complete */ @@ -1517,10 +1553,11 @@ static CURLcode expect100(struct SessionHandle *data, const char *ptr; data->state.expect100header = FALSE; /* default to false unless it is set to TRUE below */ - if(use_http_1_1plus(data, conn)) { - /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect: - 100-continue to the headers which actually speeds up post operations - (as there is one packet coming back from the web server) */ + if(use_http_1_1plus(data, conn) && + (conn->httpversion != 20)) { + /* 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) */ ptr = Curl_checkheaders(conn, "Expect:"); if(ptr) { data->state.expect100header = @@ -1529,7 +1566,7 @@ static CURLcode expect100(struct SessionHandle *data, else { result = Curl_add_bufferf(req_buffer, "Expect: 100-continue\r\n"); - if(result == CURLE_OK) + if(!result) data->state.expect100header = TRUE; } } @@ -1659,10 +1696,8 @@ CURLcode Curl_add_timecondition(struct SessionHandle *data, { const struct tm *tm; char *buf = data->state.buffer; - CURLcode result = CURLE_OK; struct tm keeptime; - - result = Curl_gmtime(data->set.timevalue, &keeptime); + CURLcode result = Curl_gmtime(data->set.timevalue, &keeptime); if(result) { failf(data, "Invalid TIMEVALUE"); return result; @@ -1713,8 +1748,8 @@ CURLcode Curl_add_timecondition(struct SessionHandle *data, */ CURLcode Curl_http(struct connectdata *conn, bool *done) { - struct SessionHandle *data=conn->data; - CURLcode result=CURLE_OK; + struct SessionHandle *data = conn->data; + CURLcode result = CURLE_OK; struct HTTP *http; const char *ppath = data->state.path; bool paste_ftp_userpwd = FALSE; @@ -1724,7 +1759,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) const char *ptr; const char *request; Curl_HttpReq httpreq = data->set.httpreq; +#if !defined(CURL_DISABLE_COOKIES) char *addcookies = NULL; +#endif curl_off_t included_body = 0; const char *httpstring; Curl_send_buffer *req_buffer; @@ -1738,8 +1775,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(conn->httpversion < 20) { /* unless the connection is re-used and already http2 */ - switch (conn->negnpn) { - case NPN_HTTP2: + switch(conn->negnpn) { + case CURL_HTTP_VERSION_2_0: + conn->httpversion = 20; /* we know we're on HTTP/2 now */ result = Curl_http2_init(conn); if(result) return result; @@ -1748,11 +1786,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(result) return result; - result = Curl_http2_switched(conn); + result = Curl_http2_switched(conn, NULL, 0); if(result) return result; break; - case NPN_HTTP1_1: + case CURL_HTTP_VERSION_1_1: /* continue with HTTP/1.1 when explicitly requested */ break; default: @@ -1770,10 +1808,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) http = data->req.protop; if(!data->state.this_is_a_follow) { - /* this is not a followed location, get the original host name */ - if(data->state.first_host) - /* Free to avoid leaking memory on multiple requests*/ - free(data->state.first_host); + /* Free to avoid leaking memory on multiple requests*/ + free(data->state.first_host); data->state.first_host = strdup(conn->host.name); if(!data->state.first_host) @@ -1817,7 +1853,7 @@ CURLcode Curl_http(struct connectdata *conn, 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(conn, "User-Agent:") && conn->allocptr.uagent) { + if(Curl_checkheaders(conn, "User-Agent:")) { free(conn->allocptr.uagent); conn->allocptr.uagent=NULL; } @@ -1846,8 +1882,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) else conn->allocptr.ref = NULL; +#if !defined(CURL_DISABLE_COOKIES) if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:")) addcookies = data->set.str[STRING_COOKIE]; +#endif if(!Curl_checkheaders(conn, "Accept-Encoding:") && data->set.str[STRING_ENCODING]) { @@ -1958,7 +1996,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } #endif - conn->allocptr.host = NULL; + if(strcmp("Host:", ptr)) { + conn->allocptr.host = aprintf("%s\r\n", ptr); + if(!conn->allocptr.host) + return CURLE_OUT_OF_MEMORY; + } + else + /* when clearing the header */ + conn->allocptr.host = NULL; } else { /* When building Host: headers, we must put the host name within @@ -2153,8 +2198,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && !Curl_checkheaders(conn, "Range:")) { /* if a line like this was already allocated, free the previous one */ - if(conn->allocptr.rangeline) - free(conn->allocptr.rangeline); + free(conn->allocptr.rangeline); conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", data->state.range); } @@ -2162,8 +2206,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) !Curl_checkheaders(conn, "Content-Range:")) { /* if a line like this was already allocated, free the previous one */ - if(conn->allocptr.rangeline) - free(conn->allocptr.rangeline); + free(conn->allocptr.rangeline); if(data->set.set_resume_from < 0) { /* Upload resume was asked for, but we don't know the size of the @@ -2227,11 +2270,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) Curl_add_bufferf(req_buffer, "%s" /* ftp typecode (;type=x) */ " HTTP/%s\r\n" /* HTTP version */ + "%s" /* host */ "%s" /* proxyuserpwd */ "%s" /* userpwd */ "%s" /* range */ "%s" /* user agent */ - "%s" /* host */ "%s" /* accept */ "%s" /* TE: */ "%s" /* accept-encoding */ @@ -2241,6 +2284,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) ftp_typecode, httpstring, + (conn->allocptr.host?conn->allocptr.host:""), conn->allocptr.proxyuserpwd? conn->allocptr.proxyuserpwd:"", conn->allocptr.userpwd?conn->allocptr.userpwd:"", @@ -2250,7 +2294,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) *data->set.str[STRING_USERAGENT] && conn->allocptr.uagent)? conn->allocptr.uagent:"", - (conn->allocptr.host?conn->allocptr.host:""), http->p_accept?http->p_accept:"", conn->allocptr.te?conn->allocptr.te:"", (data->set.str[STRING_ENCODING] && @@ -2266,18 +2309,26 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) te ); + /* clear userpwd to avoid re-using credentials from re-used connections */ + Curl_safefree(conn->allocptr.userpwd); + /* - * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM - * with basic and digest, it will be freed anyway by the next request + * Free proxyuserpwd for Negotiate/NTLM. Cannot reuse as it is associated + * with the connection and shouldn't be repeated over it either. */ - - Curl_safefree (conn->allocptr.userpwd); - conn->allocptr.userpwd = NULL; + switch (data->state.authproxy.picked) { + case CURLAUTH_NEGOTIATE: + case CURLAUTH_NTLM: + case CURLAUTH_NTLM_WB: + Curl_safefree(conn->allocptr.proxyuserpwd); + break; + } if(result) return result; if(!(conn->handler->flags&PROTOPT_SSL) && + conn->httpversion != 20 && (data->set.httpversion == CURL_HTTP_VERSION_2_0)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done over SSL */ @@ -2322,17 +2373,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } Curl_cookie_freelist(store, FALSE); /* free the cookie list */ } - if(addcookies && (CURLE_OK == result)) { + if(addcookies && !result) { if(!count) result = Curl_add_bufferf(req_buffer, "Cookie: "); - if(CURLE_OK == result) { - result = Curl_add_bufferf(req_buffer, "%s%s", - count?"; ":"", + if(!result) { + result = Curl_add_bufferf(req_buffer, "%s%s", count?"; ":"", addcookies); count++; } } - if(count && (CURLE_OK == result)) + if(count && !result) result = Curl_add_buffer(req_buffer, "\r\n", 2); if(result) @@ -2384,14 +2434,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* Get the currently set callback function pointer and store that in the form struct since we might want the actual user-provided callback later - on. The conn->fread_func pointer itself will be changed for the + on. The data->set.fread_func pointer itself will be changed for the multipart case to the function that returns a multipart formatted stream. */ - http->form.fread_func = conn->fread_func; + http->form.fread_func = data->set.fread_func; /* Set the read function to read from the generated form data */ - conn->fread_func = (curl_read_callback)Curl_FormReader; - conn->fread_in = &http->form; + data->set.fread_func = (curl_read_callback)Curl_FormReader; + data->set.in = &http->form; http->sending = HTTPSEND_BODY; @@ -2511,8 +2561,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) postsize = 0; else { /* figure out the size of the postfields */ - postsize = (data->set.postfieldsize != -1)? - data->set.postfieldsize: + postsize = (data->state.infilesize != -1)? + data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1); } @@ -2584,17 +2634,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(postsize) { /* Append the POST data chunky-style */ result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize); - if(CURLE_OK == result) { + if(!result) { result = Curl_add_buffer(req_buffer, data->set.postfields, (size_t)postsize); - if(CURLE_OK == result) - result = Curl_add_buffer(req_buffer, "\r\n", 2); + if(!result) + result = Curl_add_buffer(req_buffer, "\r\n", 2); included_body = postsize + 2; } } - if(CURLE_OK == result) - result = Curl_add_buffer(req_buffer, - "\x30\x0d\x0a\x0d\x0a", 5); + if(!result) + result = Curl_add_buffer(req_buffer, "\x30\x0d\x0a\x0d\x0a", 5); /* 0 CR LF CR LF */ included_body += 5; } @@ -2610,8 +2659,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) http->sending = HTTPSEND_BODY; - conn->fread_func = (curl_read_callback)readmoredata; - conn->fread_in = (void *)conn; + data->set.fread_func = (curl_read_callback)readmoredata; + data->set.in = (void *)conn; /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, http->postsize); @@ -2636,7 +2685,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } - else if(data->set.postfieldsize) { + else if(data->state.infilesize) { /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, postsize?postsize:-1); @@ -2997,10 +3046,12 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, infof(data, "Received 101\n"); k->upgr101 = UPGR101_RECEIVED; - /* switch to http2 now */ - result = Curl_http2_switched(conn); + /* switch to http2 now. The bytes after response headers + are also processed here, otherwise they are lost. */ + result = Curl_http2_switched(conn, k->str, *nread); if(result) return result; + *nread = 0; } break; default: @@ -3025,6 +3076,19 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, } } + /* At this point we have some idea about the fate of the connection. + If we are closing the connection it may result auth failure. */ +#if defined(USE_NTLM) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->ntlm.state == NTLMSTATE_TYPE2)) || + ((data->req.httpcode == 407) && + (conn->proxyntlm.state == NTLMSTATE_TYPE2)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n"); + data->state.authproblem = TRUE; + } +#endif + /* * When all the headers have been parsed, see if we should give * up and return an error. @@ -3203,13 +3267,26 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, #endif /* CURL_DOES_CONVERSIONS */ if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + /* + * https://tools.ietf.org/html/rfc7230#section-3.1.2 + * + * The reponse code is always a three-digit number in HTTP as the spec + * says. We try to allow any number here, but we cannot make + * guarantees on future behaviors since it isn't within the protocol. + */ nc = sscanf(HEADER1, - " HTTP/%d.%d %3d", + " HTTP/%d.%d %d", &httpversion_major, &conn->httpversion, &k->httpcode); if(nc==3) { conn->httpversion += 10 * httpversion_major; + + if(k->upgr101 == UPGR101_RECEIVED) { + /* supposedly upgraded to http2 now */ + if(conn->httpversion != 20) + infof(data, "Lying server, not serving HTTP/2\n"); + } } else { /* this is the real world, not a Nirvana @@ -3287,20 +3364,25 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, infof(data, "HTTP 1.0, assume close after body\n"); connclose(conn, "HTTP/1.0 close after body"); } + else if(conn->httpversion == 20 || + (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) { + DEBUGF(infof(data, "HTTP/2 found, allow multiplexing\n")); + + /* HTTP/2 cannot blacklist multiplexing since it is a core + functionality of the protocol */ + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + } else if(conn->httpversion >= 11 && !conn->bits.close) { - struct connectbundle *cb_ptr; - /* If HTTP version is >= 1.1 and connection is persistent server supports pipelining. */ DEBUGF(infof(data, "HTTP 1.1 or later with persistent connection, " "pipelining supported\n")); /* Activate pipelining if needed */ - cb_ptr = conn->bundle; - if(cb_ptr) { + if(conn->bundle) { if(!Curl_pipeline_site_blacklisted(data, conn)) - cb_ptr->server_supports_pipelining = TRUE; + conn->bundle->multiuse = BUNDLE_PIPELINING; } } @@ -3379,14 +3461,17 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, } } else if(checkprefix("Server:", k->p)) { - char *server_name = Curl_copy_header_value(k->p); - - /* Turn off pipelining if the server version is blacklisted */ - if(conn->bundle && conn->bundle->server_supports_pipelining) { - if(Curl_pipeline_server_blacklisted(data, server_name)) - conn->bundle->server_supports_pipelining = FALSE; + if(conn->httpversion < 20) { + /* only do this for non-h2 servers */ + char *server_name = Curl_copy_header_value(k->p); + + /* Turn off pipelining if the server version is blacklisted */ + if(conn->bundle && (conn->bundle->multiuse == BUNDLE_PIPELINING)) { + if(Curl_pipeline_server_blacklisted(data, server_name)) + conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; + } + free(server_name); } - Curl_safefree(server_name); } else if((conn->httpversion == 10) && conn->bits.httpproxy && @@ -3483,14 +3568,6 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, k->auto_decoding = GZIP; start += 6; } - else if(checkprefix("compress", start)) { - k->auto_decoding = COMPRESS; - start += 8; - } - else if(checkprefix("x-compress", start)) { - k->auto_decoding = COMPRESS; - start += 10; - } else /* unknown! */ break; @@ -3523,9 +3600,6 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, else if(checkprefix("gzip", start) || checkprefix("x-gzip", start)) k->auto_decoding = GZIP; - else if(checkprefix("compress", start) - || checkprefix("x-compress", start)) - k->auto_decoding = COMPRESS; } else if(checkprefix("Content-Range:", k->p)) { /* Content-Range: bytes [num]- @@ -3591,7 +3665,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, result = Curl_http_input_auth(conn, proxy, auth); - Curl_safefree(auth); + free(auth); if(result) return result; @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -60,6 +60,7 @@ struct Curl_send_buffer { typedef struct Curl_send_buffer Curl_send_buffer; Curl_send_buffer *Curl_add_buffer_init(void); +void Curl_add_buffer_free(Curl_send_buffer *buff); CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...); CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size); CURLcode Curl_add_buffer_send(Curl_send_buffer *in, @@ -152,42 +153,69 @@ struct HTTP { void *send_buffer; /* used if the request couldn't be sent in one chunk, points to an allocated send_buffer struct */ + +#ifdef USE_NGHTTP2 + /*********** for HTTP/2 we store stream-local data here *************/ + int32_t stream_id; /* stream we are interested in */ + + bool bodystarted; + /* We store non-final and final response headers here, per-stream */ + Curl_send_buffer *header_recvbuf; + size_t nread_header_recvbuf; /* number of bytes in header_recvbuf fed into + upper layer */ + int status_code; /* HTTP status code */ + const uint8_t *pausedata; /* pointer to data received in on_data_chunk */ + size_t pauselen; /* the number of bytes left in data */ + bool closed; /* TRUE on HTTP2 stream close */ + uint32_t error_code; /* HTTP/2 error code */ + + char *mem; /* points to a buffer in memory to store received data */ + size_t len; /* size of the buffer 'mem' points to */ + size_t memlen; /* size of data copied to mem */ + + const uint8_t *upload_mem; /* points to a buffer to read from */ + size_t upload_len; /* size of the buffer 'upload_mem' points to */ + curl_off_t upload_left; /* number of bytes left to upload */ + + char **push_headers; /* allocated array */ + size_t push_headers_used; /* number of entries filled in */ + size_t push_headers_alloc; /* number of entries allocated */ +#endif }; typedef int (*sending)(void); /* Curl_send */ typedef int (*recving)(void); /* Curl_recv */ +#ifdef USE_NGHTTP2 +/* h2 settings for this connection */ +struct h2settings { + uint32_t max_concurrent_streams; + bool enable_push; +}; +#endif + + struct http_conn { #ifdef USE_NGHTTP2 #define H2_BINSETTINGS_LEN 80 nghttp2_session *h2; uint8_t binsettings[H2_BINSETTINGS_LEN]; size_t binlen; /* length of the binsettings data */ - char *mem; /* points to a buffer in memory to store */ - size_t len; /* size of the buffer 'mem' points to */ - bool bodystarted; sending send_underlying; /* underlying send Curl_send callback */ recving recv_underlying; /* underlying recv Curl_recv callback */ - bool closed; /* TRUE on HTTP2 stream close */ - Curl_send_buffer *header_recvbuf; /* store response headers. We - store non-final and final - response headers into it. */ - size_t nread_header_recvbuf; /* number of bytes in header_recvbuf - fed into upper layer */ - int32_t stream_id; /* stream we are interested in */ - const uint8_t *data; /* pointer to data chunk, received in - on_data_chunk */ - size_t datalen; /* the number of bytes left in data */ char *inbuf; /* buffer to receive data from underlying socket */ + size_t inbuflen; /* number of bytes filled in inbuf */ + size_t nread_inbuf; /* number of bytes read from in inbuf */ /* We need separate buffer for transmission and reception because we may call nghttp2_session_send() after the nghttp2_session_mem_recv() but mem buffer is still not full. In this case, we wrongly sends the content of mem buffer if we share them for both cases. */ - const uint8_t *upload_mem; /* points to a buffer to read from */ - size_t upload_len; /* size of the buffer 'upload_mem' points to */ - size_t upload_left; /* number of bytes left to upload */ - int status_code; /* HTTP status code */ + int32_t pause_stream_id; /* stream ID which paused + nghttp2_session_mem_recv */ + + /* this is a hash of all individual streams (SessionHandle structs) */ + struct h2settings settings; #else int unused; /* prevent a compiler warning */ #endif diff --git a/lib/http2.c b/lib/http2.c index 604514d..0024add 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,22 +23,24 @@ #include "curl_setup.h" #ifdef USE_NGHTTP2 -#define _MPRINTF_REPLACE -#include <curl/mprintf.h> - +#include "curl_printf.h" #include <nghttp2/nghttp2.h> #include "urldata.h" #include "http2.h" #include "http.h" #include "sendf.h" #include "curl_base64.h" -#include "curl_memory.h" #include "rawstr.h" #include "multiif.h" +#include "conncache.h" +#include "url.h" -/* include memdebug.h last */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" +#define MIN(x,y) ((x)<(y)?(x):(y)) + #if (NGHTTP2_VERSION_NUM < 0x000600) #error too old nghttp2 version, upgrade! #endif @@ -50,7 +52,7 @@ static int http2_perform_getsock(const struct connectdata *conn, sockets */ int numsocks) { - const struct http_conn *httpc = &conn->proto.httpc; + const struct http_conn *c = &conn->proto.httpc; int bitmap = GETSOCK_BLANK; (void)numsocks; @@ -58,10 +60,10 @@ static int http2_perform_getsock(const struct connectdata *conn, because of renegotiation. */ sock[0] = conn->sock[FIRSTSOCKET]; - if(nghttp2_session_want_read(httpc->h2)) + if(nghttp2_session_want_read(c->h2)) bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - if(nghttp2_session_want_write(httpc->h2)) + if(nghttp2_session_want_write(c->h2)) bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); return bitmap; @@ -78,23 +80,54 @@ static int http2_getsock(struct connectdata *conn, static CURLcode http2_disconnect(struct connectdata *conn, bool dead_connection) { - struct http_conn *httpc = &conn->proto.httpc; + struct HTTP *http = conn->data->req.protop; + struct http_conn *c = &conn->proto.httpc; (void)dead_connection; - infof(conn->data, "HTTP/2 DISCONNECT starts now\n"); - - nghttp2_session_del(httpc->h2); + DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n")); - Curl_safefree(httpc->header_recvbuf->buffer); - Curl_safefree(httpc->header_recvbuf); + nghttp2_session_del(c->h2); + Curl_safefree(c->inbuf); - Curl_safefree(httpc->inbuf); + if(http) { + Curl_add_buffer_free(http->header_recvbuf); + http->header_recvbuf = NULL; /* clear the pointer */ + for(; http->push_headers_used > 0; --http->push_headers_used) { + free(http->push_headers[http->push_headers_used - 1]); + } + free(http->push_headers); + http->push_headers = NULL; + } - infof(conn->data, "HTTP/2 DISCONNECT done\n"); + DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n")); return CURLE_OK; } +/* called from Curl_http_setup_conn */ +void Curl_http2_setup_req(struct SessionHandle *data) +{ + struct HTTP *http = data->req.protop; + + http->nread_header_recvbuf = 0; + http->bodystarted = FALSE; + http->status_code = -1; + http->pausedata = NULL; + http->pauselen = 0; + http->error_code = NGHTTP2_NO_ERROR; + http->closed = FALSE; + http->mem = data->state.buffer; + http->len = BUFSIZE; + http->memlen = 0; +} + +/* called from Curl_http_setup_conn */ +void Curl_http2_setup_conn(struct connectdata *conn) +{ + conn->proto.httpc.settings.max_concurrent_streams = + DEFAULT_MAX_CONCURRENT_STREAMS; +} + /* * HTTP2 handler interface. This isn't added to the general list of protocols * but will be used at run-time when the protocol is dynamically switched from @@ -104,7 +137,7 @@ const struct Curl_handler Curl_handler_http2 = { "HTTP2", /* scheme */ ZERO_NULL, /* setup_connection */ Curl_http, /* do_it */ - ZERO_NULL, /* done */ + Curl_http_done, /* done */ ZERO_NULL, /* do_more */ ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ @@ -124,7 +157,7 @@ const struct Curl_handler Curl_handler_http2_ssl = { "HTTP2", /* scheme */ ZERO_NULL, /* setup_connection */ Curl_http, /* do_it */ - ZERO_NULL, /* done */ + Curl_http_done, /* done */ ZERO_NULL, /* do_more */ ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ @@ -160,17 +193,17 @@ static ssize_t send_callback(nghttp2_session *h2, void *userp) { struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; + struct http_conn *c = &conn->proto.httpc; ssize_t written; - CURLcode rc; + CURLcode result = CURLE_OK; + (void)h2; (void)flags; - rc = 0; - written = ((Curl_send*)httpc->send_underlying)(conn, FIRSTSOCKET, - data, length, &rc); + written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET, + data, length, &result); - if(rc == CURLE_AGAIN) { + if(result == CURLE_AGAIN) { return NGHTTP2_ERR_WOULDBLOCK; } @@ -185,25 +218,205 @@ static ssize_t send_callback(nghttp2_session *h2, return written; } + +/* We pass a pointer to this struct in the push callback, but the contents of + the struct are hidden from the user. */ +struct curl_pushheaders { + struct SessionHandle *data; + const nghttp2_push_promise *frame; +}; + +/* + * push header access function. Only to be used from within the push callback + */ +char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) +{ + /* Verify that we got a good easy handle in the push header struct, mostly to + detect rubbish input fast(er). */ + if(!h || !GOOD_EASY_HANDLE(h->data)) + return NULL; + else { + struct HTTP *stream = h->data->req.protop; + if(num < stream->push_headers_used) + return stream->push_headers[num]; + } + return NULL; +} + +/* + * push header access function. Only to be used from within the push callback + */ +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +{ + /* Verify that we got a good easy handle in the push header struct, + mostly to detect rubbish input fast(er). Also empty header name + is just a rubbish too. We have to allow ":" at the beginning of + the header, but header == ":" must be rejected. If we have ':' in + the middle of header, it could be matched in middle of the value, + this is because we do prefix match.*/ + if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || + Curl_raw_equal(header, ":") || strchr(header + 1, ':')) + return NULL; + else { + struct HTTP *stream = h->data->req.protop; + size_t len = strlen(header); + size_t i; + for(i=0; i<stream->push_headers_used; i++) { + if(!strncmp(header, stream->push_headers[i], len)) { + /* sub-match, make sure that it us followed by a colon */ + if(stream->push_headers[i][len] != ':') + continue; + return &stream->push_headers[i][len+1]; + } + } + } + return NULL; +} + +static CURL *duphandle(struct SessionHandle *data) +{ + struct SessionHandle *second = curl_easy_duphandle(data); + if(second) { + /* setup the request struct */ + struct HTTP *http = calloc(1, sizeof(struct HTTP)); + if(!http) { + (void)Curl_close(second); + second = NULL; + } + else { + second->req.protop = http; + http->header_recvbuf = Curl_add_buffer_init(); + if(!http->header_recvbuf) { + free(http); + (void)Curl_close(second); + second = NULL; + } + else + Curl_http2_setup_req(second); + } + } + return second; +} + + +static int push_promise(struct SessionHandle *data, + struct connectdata *conn, + const nghttp2_push_promise *frame) +{ + int rv; + DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n", + frame->promised_stream_id)); + if(data->multi->push_cb) { + struct HTTP *stream; + struct curl_pushheaders heads; + CURLMcode rc; + struct http_conn *httpc; + size_t i; + /* clone the parent */ + CURL *newhandle = duphandle(data); + if(!newhandle) { + infof(data, "failed to duplicate handle\n"); + rv = 1; /* FAIL HARD */ + goto fail; + } + + heads.data = data; + heads.frame = frame; + /* ask the application */ + DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n")); + + stream = data->req.protop; + if(!stream) { + failf(data, "Internal NULL stream!\n"); + rv = 1; + goto fail; + } + + rv = data->multi->push_cb(data, newhandle, + stream->push_headers_used, &heads, + data->multi->push_userp); + + /* free the headers again */ + for(i=0; i<stream->push_headers_used; i++) + free(stream->push_headers[i]); + free(stream->push_headers); + stream->push_headers = NULL; + + if(rv) { + /* denied, kill off the new handle again */ + (void)Curl_close(newhandle); + goto fail; + } + + /* approved, add to the multi handle and immediately switch to PERFORM + state with the given connection !*/ + rc = Curl_multi_add_perform(data->multi, newhandle, conn); + if(rc) { + infof(data, "failed to add handle to multi\n"); + Curl_close(newhandle); + rv = 1; + goto fail; + } + + httpc = &conn->proto.httpc; + nghttp2_session_set_stream_user_data(httpc->h2, + frame->promised_stream_id, newhandle); + } + else { + DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n")); + rv = 1; + } + fail: + return rv; +} + static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; + struct connectdata *conn = NULL; + struct http_conn *httpc = NULL; + struct SessionHandle *data_s = NULL; + struct HTTP *stream = NULL; + static int lastStream = -1; int rv; size_t left, ncopy; + int32_t stream_id = frame->hd.stream_id; - (void)session; - (void)frame; - infof(conn->data, "on_frame_recv() was called with header %x\n", - frame->hd.type); + (void)userp; + + if(!stream_id) { + /* stream ID zero is for connection-oriented stuff */ + return 0; + } + data_s = nghttp2_session_get_stream_user_data(session, + frame->hd.stream_id); + if(lastStream != frame->hd.stream_id) { + lastStream = frame->hd.stream_id; + } + if(!data_s) { + DEBUGF(infof(conn->data, + "No SessionHandle associated with stream: %x\n", + stream_id)); + return 0; + } + + stream = data_s->req.protop; + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; + + DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n", + frame->hd.type, stream_id)); + + conn = data_s->easy_conn; + assert(conn); + assert(conn->data == data_s); + httpc = &conn->proto.httpc; switch(frame->hd.type) { case NGHTTP2_DATA: - /* If body started, then receiving DATA is illegal. */ - if(!c->bodystarted) { + /* If body started on this stream, then receiving DATA is illegal. */ + if(!stream->bodystarted) { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); + stream_id, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -214,74 +427,96 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) break; - if(c->bodystarted) { - /* Only valid HEADERS after body started is trailer header, - which is not fully supported in this code. If HEADERS is not - trailer, then it is a PROTOCOL_ERROR. */ - if((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - - if(nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } + if(stream->bodystarted) { + /* Only valid HEADERS after body started is trailer HEADERS. We + ignores trailer HEADERS for now. nghttp2 guarantees that it + has END_STREAM flag set. */ break; } - if(c->status_code == -1) { - /* No :status header field means PROTOCOL_ERROR. */ - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - - if(nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - break; - } + /* nghttp2 guarantees that :status is received, and we store it to + stream->status_code */ + DEBUGASSERT(stream->status_code != -1); /* Only final status code signals the end of header */ - if(c->status_code / 100 != 1) { - c->bodystarted = TRUE; + if(stream->status_code / 100 != 1) { + stream->bodystarted = TRUE; + stream->status_code = -1; } - c->status_code = -1; + Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); - Curl_add_buffer(c->header_recvbuf, "\r\n", 2); + left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; + ncopy = MIN(stream->len, left); + + memcpy(&stream->mem[stream->memlen], + stream->header_recvbuf->buffer + stream->nread_header_recvbuf, + ncopy); + stream->nread_header_recvbuf += ncopy; - left = c->header_recvbuf->size_used - c->nread_header_recvbuf; - ncopy = c->len < left ? c->len : left; + DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n", + ncopy, stream_id, stream->mem)); - memcpy(c->mem, c->header_recvbuf->buffer + c->nread_header_recvbuf, ncopy); - c->nread_header_recvbuf += ncopy; + stream->len -= ncopy; + stream->memlen += ncopy; - c->mem += ncopy; - c->len -= ncopy; + data_s->state.drain++; + Curl_expire(data_s, 1); break; case NGHTTP2_PUSH_PROMISE: - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->push_promise.promised_stream_id, - NGHTTP2_CANCEL); - if(nghttp2_is_fatal(rv)) { - return rv; + rv = push_promise(data_s, conn, &frame->push_promise); + if(rv) { /* deny! */ + rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_CANCEL); + if(nghttp2_is_fatal(rv)) { + return rv; + } } break; + case NGHTTP2_SETTINGS: + { + uint32_t max_conn = httpc->settings.max_concurrent_streams; + DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id)); + httpc->settings.max_concurrent_streams = + nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + httpc->settings.enable_push = + nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_ENABLE_PUSH); + DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n", + httpc->settings.max_concurrent_streams)); + DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n", + httpc->settings.enable_push?"TRUE":"false")); + if(max_conn != httpc->settings.max_concurrent_streams) { + /* only signal change if the value actually changed */ + infof(conn->data, + "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n"); + Curl_multi_connchanged(conn->data->multi); + } + } + break; + default: + DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n", + frame->hd.type, stream_id)); + break; } return 0; } static int on_invalid_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, - uint32_t error_code, void *userp) + int lib_error_code, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - (void)session; - (void)frame; - infof(conn->data, "on_invalid_frame_recv() was called, error_code = %d\n", - error_code); + struct SessionHandle *data_s = NULL; + (void)userp; + + data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if(data_s) { + DEBUGF(infof(data_s, + "on_invalid_frame_recv() was called, error=%d:%s\n", + lib_error_code, nghttp2_strerror(lib_error_code))); + } return 0; } @@ -289,30 +524,50 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; + struct HTTP *stream; + struct SessionHandle *data_s; size_t nread; (void)session; (void)flags; (void)data; - infof(conn->data, "on_data_chunk_recv() " - "len = %u, stream = %x\n", len, stream_id); + (void)userp; - if(stream_id != c->stream_id) { - return 0; - } + DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ - nread = c->len < len ? c->len : len; - memcpy(c->mem, data, nread); + /* 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; + + stream = data_s->req.protop; + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; - c->mem += nread; - c->len -= nread; + nread = MIN(stream->len, len); + memcpy(&stream->mem[stream->memlen], data, nread); - infof(conn->data, "%zu data written\n", nread); + stream->len -= nread; + stream->memlen += nread; + + data_s->state.drain++; + Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for + immediately? */ + + DEBUGF(infof(data_s, "%zu data received for stream %u " + "(%zu left in buffer %p, total %zu)\n", + nread, stream_id, + stream->len, stream->mem, + stream->memlen)); if(nread < len) { - c->data = data + nread; - c->datalen = len - nread; + stream->pausedata = data + nread; + stream->pauselen = len - nread; + DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" + ", stream %u\n", + len - nread, stream_id)); + data_s->easy_conn->proto.httpc.pause_stream_id = stream_id; return NGHTTP2_ERR_PAUSE; } return 0; @@ -322,59 +577,89 @@ static int before_frame_send(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - (void)session; - (void)frame; - infof(conn->data, "before_frame_send() was called\n"); + struct SessionHandle *data_s; + (void)userp; + + data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if(data_s) { + DEBUGF(infof(data_s, "before_frame_send() was called\n")); + } + return 0; } static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - (void)session; - (void)frame; - infof(conn->data, "on_frame_send() was called\n"); + struct SessionHandle *data_s; + (void)userp; + + data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if(data_s) { + DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n", + frame->hd.length)); + } return 0; } static int on_frame_not_send(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - (void)session; - (void)frame; - infof(conn->data, "on_frame_not_send() was called, lib_error_code = %d\n", - lib_error_code); + struct SessionHandle *data_s; + (void)userp; + + data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if(data_s) { + DEBUGF(infof(data_s, + "on_frame_not_send() was called, lib_error_code = %d\n", + lib_error_code)); + } return 0; } static int on_stream_close(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; + struct SessionHandle *data_s; + struct HTTP *stream; (void)session; (void)stream_id; - infof(conn->data, "on_stream_close() was called, error_code = %d\n", - error_code); - - if(stream_id != c->stream_id) { - return 0; - } + (void)userp; + + if(stream_id) { + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) { + /* We could get stream ID not in the hash. For example, if we + decided to reject stream (e.g., PUSH_PROMISE). */ + return 0; + } + DEBUGF(infof(data_s, "on_stream_close(), error_code = %d, stream %u\n", + error_code, stream_id)); + stream = data_s->req.protop; + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; - c->closed = TRUE; + stream->error_code = error_code; + stream->closed = TRUE; + /* remove the entry from the hash as the stream is now gone */ + nghttp2_session_set_stream_user_data(session, stream_id, 0); + DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id)); + } return 0; } static int on_begin_headers(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - (void)session; - (void)frame; - infof(conn->data, "on_begin_headers() was called\n"); + struct SessionHandle *data_s = NULL; + (void)userp; + + data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if(data_s) { + DEBUGF(infof(data_s, "on_begin_headers() was called\n")); + } return 0; } @@ -405,8 +690,6 @@ static int decode_status_code(const uint8_t *value, size_t len) return res; } -static const char STATUS[] = ":status"; - /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, @@ -414,93 +697,94 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, uint8_t flags, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; - int rv; - int goodname; - int goodheader; + struct HTTP *stream; + struct SessionHandle *data_s; + int32_t stream_id = frame->hd.stream_id; - (void)session; - (void)frame; (void)flags; + (void)userp; - if(frame->hd.stream_id != c->stream_id) { - return 0; + DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ + + /* 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; + + stream = data_s->req.protop; + if(!stream) { + failf(data_s, "Internal NULL stream! 5\n"); + return NGHTTP2_ERR_CALLBACK_FAILURE; } - if(c->bodystarted) { + if(stream->bodystarted) /* Ignore trailer or HEADERS not mapped to HTTP semantics. The consequence is handled in on_frame_recv(). */ return 0; - } - goodname = nghttp2_check_header_name(name, namelen); - goodheader = nghttp2_check_header_value(value, valuelen); + /* Store received PUSH_PROMISE headers to be used when the subsequent + PUSH_PROMISE callback comes */ + if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { + char *h; - if(!goodname || !goodheader) { - - infof(conn->data, "Detected bad incoming header %s%s, reset stream!\n", - goodname?"":"name", - goodheader?"":"value"); - - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - - if(nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; + if(!stream->push_headers) { + stream->push_headers_alloc = 10; + stream->push_headers = malloc(stream->push_headers_alloc * + sizeof(char *)); + stream->push_headers_used = 0; } - - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - if(namelen == sizeof(":status") - 1 && - memcmp(STATUS, name, namelen) == 0) { - - /* :status must appear exactly once. */ - if(c->status_code != -1 || - (c->status_code = decode_status_code(value, valuelen)) == -1) { - - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - if(nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; + else if(stream->push_headers_used == + stream->push_headers_alloc) { + char **headp; + stream->push_headers_alloc *= 2; + headp = realloc(stream->push_headers, + stream->push_headers_alloc * sizeof(char *)); + if(!headp) { + free(stream->push_headers); + stream->push_headers = NULL; + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } - - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + stream->push_headers = headp; } - - Curl_add_buffer(c->header_recvbuf, "HTTP/2.0 ", 9); - Curl_add_buffer(c->header_recvbuf, value, valuelen); - Curl_add_buffer(c->header_recvbuf, "\r\n", 2); - + h = aprintf("%s:%s", name, value); + if(h) + stream->push_headers[stream->push_headers_used++] = h; return 0; } - else { - /* Here we are sure that namelen > 0 because of - nghttp2_check_header_name(). Pseudo header other than :status - is illegal. */ - if(c->status_code == -1 || name[0] == ':') { - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - if(nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } + if(namelen == sizeof(":status") - 1 && + memcmp(":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. */ + stream->status_code = decode_status_code(value, valuelen); + DEBUGASSERT(stream->status_code != -1); + + Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9); + Curl_add_buffer(stream->header_recvbuf, value, valuelen); + Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); + data_s->state.drain++; + Curl_expire(data_s, 1); + + DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n", + stream->status_code)); + return 0; + } - /* convert to a HTTP1-style header */ - Curl_add_buffer(c->header_recvbuf, name, namelen); - Curl_add_buffer(c->header_recvbuf, ":", 1); - Curl_add_buffer(c->header_recvbuf, value, valuelen); - Curl_add_buffer(c->header_recvbuf, "\r\n", 2); + /* nghttp2 guarantees that namelen > 0, and :status was already + received, and this is not pseudo-header field . */ + /* convert to a HTTP1-style header */ + Curl_add_buffer(stream->header_recvbuf, name, namelen); + Curl_add_buffer(stream->header_recvbuf, ":", 1); + Curl_add_buffer(stream->header_recvbuf, value, valuelen); + Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); + data_s->state.drain++; + Curl_expire(data_s, 1); - infof(conn->data, "got http2 header: %.*s: %.*s\n", - namelen, name, valuelen, value); - } + DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen, + value)); return 0; /* 0 is successful */ } @@ -512,26 +796,45 @@ static ssize_t data_source_read_callback(nghttp2_session *session, nghttp2_data_source *source, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; + struct SessionHandle *data_s; + struct HTTP *stream = NULL; size_t nread; - (void)session; - (void)stream_id; (void)source; + (void)userp; + + if(stream_id) { + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + 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; - nread = c->upload_len < length ? c->upload_len : length; + stream = data_s->req.protop; + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + else + return NGHTTP2_ERR_INVALID_ARGUMENT; + + nread = MIN(stream->upload_len, length); if(nread > 0) { - memcpy(buf, c->upload_mem, nread); - c->upload_mem += nread; - c->upload_len -= nread; - c->upload_left -= nread; + memcpy(buf, stream->upload_mem, nread); + stream->upload_mem += nread; + stream->upload_len -= nread; + stream->upload_left -= nread; } - if(c->upload_left == 0) + if(stream->upload_left == 0) *data_flags = 1; else if(nread == 0) return NGHTTP2_ERR_DEFERRED; + DEBUGF(infof(data_s, "data_source_read_callback: " + "returns %zu bytes stream %u\n", + nread, stream_id)); + return nread; } @@ -543,7 +846,7 @@ static nghttp2_settings_entry settings[] = { { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE }, }; -#define H2_BUFSIZE 4096 +#define H2_BUFSIZE 32768 /* * Initialize nghttp2 for a Curl connection @@ -595,8 +898,7 @@ CURLcode Curl_http2_init(struct connectdata *conn) nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); /* The nghttp2 session is not yet setup, do it */ - rc = nghttp2_session_client_new(&conn->proto.httpc.h2, - callbacks, conn); + rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); nghttp2_session_callbacks_del(callbacks); @@ -604,6 +906,11 @@ CURLcode Curl_http2_init(struct connectdata *conn) failf(conn->data, "Couldn't initialize nghttp2!"); return CURLE_OUT_OF_MEMORY; /* most likely at least */ } + + if(rc) { + failf(conn->data, "Couldn't init stream hash!"); + return CURLE_OUT_OF_MEMORY; /* most likely at least */ + } } return CURLE_OK; } @@ -630,14 +937,6 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, struct SingleRequest *k = &conn->data->req; uint8_t *binsettings = conn->proto.httpc.binsettings; - result = Curl_http2_init(conn); - if(result) - return result; - - result = Curl_http2_setup(conn); - if(result) - return result; - /* As long as we have a fixed set of settings, we don't have to dynamically * figure out the base64 strings since it'll always be the same. However, * the settings will likely not be fixed every time in the future. @@ -663,13 +962,32 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, "Upgrade: %s\r\n" "HTTP2-Settings: %s\r\n", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); - Curl_safefree(base64); + free(base64); k->upgr101 = UPGR101_REQUESTED; return result; } +static ssize_t http2_handle_stream_close(struct http_conn *httpc, + struct SessionHandle *data, + struct HTTP *stream, CURLcode *err) { + if(httpc->pause_stream_id == stream->stream_id) { + httpc->pause_stream_id = 0; + } + /* Reset to FALSE to prevent infinite loop in readwrite_data + function. */ + stream->closed = FALSE; + if(stream->error_code != NGHTTP2_NO_ERROR) { + failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d", + stream->stream_id, stream->error_code); + *err = CURLE_HTTP2; + return -1; + } + DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n")); + return 0; +} + /* * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return * a regular CURLcode value. @@ -677,102 +995,188 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, static ssize_t http2_recv(struct connectdata *conn, int sockindex, char *mem, size_t len, CURLcode *err) { - CURLcode rc; + CURLcode result = CURLE_OK; ssize_t rv; ssize_t nread; struct http_conn *httpc = &conn->proto.httpc; + struct SessionHandle *data = conn->data; + struct HTTP *stream = data->req.protop; (void)sockindex; /* we always do HTTP2 on sockindex 0 */ - if(httpc->closed) { - /* Reset to FALSE to prevent infinite loop in readwrite_data - function. */ - httpc->closed = FALSE; - return 0; + /* If stream is closed, return 0 to signal the http routine to close + the connection. We need to handle stream closure here, + otherwise, we may be going to read from underlying connection, + and gets EAGAIN, and we will get stuck there. */ + if(stream->memlen == 0 && stream->closed) { + return http2_handle_stream_close(httpc, data, stream, err); } /* Nullify here because we call nghttp2_session_send() and they might refer to the old buffer. */ - httpc->upload_mem = NULL; - httpc->upload_len = 0; + stream->upload_mem = NULL; + stream->upload_len = 0; - if(httpc->bodystarted && - httpc->nread_header_recvbuf < httpc->header_recvbuf->size_used) { + /* + * At this point 'stream' is just in the SessionHandle the connection + * identifies as its owner at this time. + */ + + if(stream->bodystarted && + stream->nread_header_recvbuf < stream->header_recvbuf->size_used) { + /* If there is body data pending for this stream to return, do that */ size_t left = - httpc->header_recvbuf->size_used - httpc->nread_header_recvbuf; - size_t ncopy = len < left ? len : left; - memcpy(mem, httpc->header_recvbuf->buffer + httpc->nread_header_recvbuf, + stream->header_recvbuf->size_used - stream->nread_header_recvbuf; + size_t ncopy = MIN(len, left); + memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf, ncopy); - httpc->nread_header_recvbuf += ncopy; + stream->nread_header_recvbuf += ncopy; + + infof(data, "http2_recv: Got %d bytes from header_recvbuf\n", + (int)ncopy); return ncopy; } - if(httpc->data) { - nread = len < httpc->datalen ? len : httpc->datalen; - memcpy(mem, httpc->data, nread); + infof(data, "http2_recv: %d bytes buffer at %p (stream %u)\n", + len, mem, stream->stream_id); + + if((data->state.drain) && stream->memlen) { + DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n", + stream->memlen, stream->stream_id, + stream->mem, mem)); + if(mem != stream->mem) { + /* if we didn't get the same buffer this time, we must move the data to + the beginning */ + memmove(mem, stream->mem, stream->memlen); + stream->len = len - stream->memlen; + stream->mem = mem; + } + } + else if(stream->pausedata) { + nread = MIN(len, stream->pauselen); + memcpy(mem, stream->pausedata, nread); + + stream->pausedata += nread; + stream->pauselen -= nread; - httpc->data += nread; - httpc->datalen -= nread; + infof(data, "%zu data bytes written\n", nread); + if(stream->pauselen == 0) { + DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id)); + assert(httpc->pause_stream_id == stream->stream_id); + httpc->pause_stream_id = 0; - infof(conn->data, "%zu data written\n", nread); - if(httpc->datalen == 0) { - httpc->data = NULL; - httpc->datalen = 0; + stream->pausedata = NULL; + stream->pauselen = 0; } + infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n", + nread, stream->stream_id); return nread; } + else if(httpc->pause_stream_id) { + /* If a stream paused nghttp2_session_mem_recv previously, and has + not processed all data, it still refers to the buffer in + nghttp2_session. If we call nghttp2_session_mem_recv(), we may + overwrite that buffer. To avoid that situation, just return + here with CURLE_AGAIN. This could be busy loop since data in + socket is not read. But it seems that usually streams are + notified with its drain property, and socket is read again + quickly. */ + *err = CURLE_AGAIN; + return -1; + } + else { + char *inbuf; + /* remember where to store incoming data for this stream and how big the + buffer is */ + stream->mem = mem; + stream->len = len; + stream->memlen = 0; + + if(httpc->inbuflen == 0) { + nread = ((Curl_recv *)httpc->recv_underlying)( + conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); + + if(result == CURLE_AGAIN) { + *err = result; + return -1; + } - conn->proto.httpc.mem = mem; - conn->proto.httpc.len = len; - - infof(conn->data, "http2_recv: %d bytes buffer\n", - conn->proto.httpc.len); + if(nread == -1) { + failf(data, "Failed receiving HTTP2 data"); + *err = result; + return 0; + } - rc = 0; - nread = ((Curl_recv*)httpc->recv_underlying)(conn, FIRSTSOCKET, - httpc->inbuf, H2_BUFSIZE, &rc); + if(nread == 0) { + failf(data, "Unexpected EOF"); + *err = CURLE_RECV_ERROR; + return -1; + } - if(rc == CURLE_AGAIN) { - *err = rc; - return -1; - } + DEBUGF(infof(data, "nread=%zd\n", nread)); - if(nread == -1) { - failf(conn->data, "Failed receiving HTTP2 data"); - *err = rc; - return 0; - } + httpc->inbuflen = nread; + inbuf = httpc->inbuf; + } + else { + nread = httpc->inbuflen - httpc->nread_inbuf; + inbuf = httpc->inbuf + httpc->nread_inbuf; - infof(conn->data, "nread=%zd\n", nread); - rv = nghttp2_session_mem_recv(httpc->h2, - (const uint8_t *)httpc->inbuf, nread); + DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n", + nread)); + } + rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); - if(nghttp2_is_fatal((int)rv)) { - failf(conn->data, "nghttp2_session_mem_recv() returned %d:%s\n", - rv, nghttp2_strerror((int)rv)); - *err = CURLE_RECV_ERROR; - return 0; - } - infof(conn->data, "nghttp2_session_mem_recv() returns %zd\n", rv); - /* Always send pending frames in nghttp2 session, because - nghttp2_session_mem_recv() may queue new frame */ - rv = nghttp2_session_send(httpc->h2); - if(rv != 0) { - *err = CURLE_SEND_ERROR; - return 0; + if(nghttp2_is_fatal((int)rv)) { + failf(data, "nghttp2_session_mem_recv() returned %d:%s\n", + rv, nghttp2_strerror((int)rv)); + *err = CURLE_RECV_ERROR; + return 0; + } + DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv)); + if(nread == rv) { + DEBUGF(infof(data, "All data in connection buffer processed\n")); + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + } + else { + httpc->nread_inbuf += rv; + DEBUGF(infof(data, "%zu bytes left in connection buffer\n", + httpc->inbuflen - httpc->nread_inbuf)); + } + /* Always send pending frames in nghttp2 session, because + nghttp2_session_mem_recv() may queue new frame */ + rv = nghttp2_session_send(httpc->h2); + if(rv != 0) { + *err = CURLE_SEND_ERROR; + return 0; + } } - if(len != httpc->len) { - return len - conn->proto.httpc.len; + if(stream->memlen) { + ssize_t retlen = stream->memlen; + infof(data, "http2_recv: returns %zd for stream %u\n", + retlen, stream->stream_id); + stream->memlen = 0; + + if(httpc->pause_stream_id == stream->stream_id) { + /* data for this stream is returned now, but this stream caused a pause + already so we need it called again asap */ + DEBUGF(infof(data, "Data returned for PAUSED stream %u\n", + stream->stream_id)); + } + else + data->state.drain = 0; /* this stream is hereby drained */ + + return retlen; } /* If stream is closed, return 0 to signal the http routine to close the connection */ - if(httpc->closed) { - /* Reset to FALSE to prevent infinite loop in readwrite_data - function. */ - httpc->closed = FALSE; - return 0; + if(stream->closed) { + return http2_handle_stream_close(httpc, data, stream, err); } *err = CURLE_AGAIN; + DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n", + stream->stream_id)); return -1; } @@ -791,6 +1195,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, */ int rv; struct http_conn *httpc = &conn->proto.httpc; + struct HTTP *stream = conn->data->req.protop; nghttp2_nv *nva; size_t nheader; size_t i; @@ -799,23 +1204,41 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, char *end; nghttp2_data_provider data_prd; int32_t stream_id; + nghttp2_session *h2 = httpc->h2; (void)sockindex; - infof(conn->data, "http2_send len=%zu\n", len); + DEBUGF(infof(conn->data, "http2_send len=%zu\n", len)); - if(httpc->stream_id != -1) { + if(stream->stream_id != -1) { /* If stream_id != -1, we have dispatched request HEADERS, and now are going to send or sending request body in DATA frame */ - httpc->upload_mem = mem; - httpc->upload_len = len; - nghttp2_session_resume_data(httpc->h2, httpc->stream_id); - rv = nghttp2_session_send(httpc->h2); + stream->upload_mem = mem; + stream->upload_len = len; + nghttp2_session_resume_data(h2, stream->stream_id); + rv = nghttp2_session_send(h2); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; return -1; } - return len - httpc->upload_len; + len -= stream->upload_len; + + /* Nullify here because we call nghttp2_session_send() and they + might refer to the old buffer. */ + stream->upload_mem = NULL; + stream->upload_len = 0; + + if(stream->upload_left) { + /* we are sure that we have more data to send here. Calling the + following API will make nghttp2_session_want_write() return + nonzero if remote window allows it, which then libcurl checks + socket is writable or not. See http2_perform_getsock(). */ + nghttp2_session_resume_data(h2, stream->stream_id); + } + + DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len, + stream->stream_id)); + return len; } /* Calculate number of headers contained in [mem, mem + len) */ @@ -838,6 +1261,8 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, } /* Extract :method, :path from request line */ end = strchr(hdbuf, ' '); + if(!end) + goto fail; nva[0].name = (unsigned char *)":method"; nva[0].namelen = (uint16_t)strlen((char *)nva[0].name); nva[0].value = (unsigned char *)hdbuf; @@ -847,6 +1272,8 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, hdbuf = end + 1; end = strchr(hdbuf, ' '); + if(!end) + goto fail; nva[1].name = (unsigned char *)":path"; nva[1].namelen = (uint16_t)strlen((char *)nva[1].name); nva[1].value = (unsigned char *)hdbuf; @@ -863,13 +1290,16 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, nva[2].flags = NGHTTP2_NV_FLAG_NONE; hdbuf = strchr(hdbuf, 0x0a); + if(!hdbuf) + goto fail; ++hdbuf; authority_idx = 0; for(i = 3; i < nheader; ++i) { end = strchr(hdbuf, ':'); - assert(end); + if(!end) + goto fail; if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) { authority_idx = i; nva[i].name = (unsigned char *)":authority"; @@ -882,7 +1312,8 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, hdbuf = end + 1; for(; *hdbuf == ' '; ++hdbuf); end = strchr(hdbuf, 0x0d); - assert(end); + if(!end) + goto fail; nva[i].value = (unsigned char *)hdbuf; nva[i].valuelen = (uint16_t)(end - hdbuf); nva[i].flags = NGHTTP2_NV_FLAG_NONE; @@ -894,11 +1325,15 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, if(nva[i].namelen == 14 && Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) { size_t j; + stream->upload_left = 0; for(j = 0; j < nva[i].valuelen; ++j) { - httpc->upload_left *= 10; - httpc->upload_left += nva[i].value[j] - '0'; + stream->upload_left *= 10; + stream->upload_left += nva[i].value[j] - '0'; } - infof(conn->data, "request content-length=%zu\n", httpc->upload_left); + DEBUGF(infof(conn->data, + "request content-length=%" + CURL_FORMAT_CURL_OFF_T + "\n", stream->upload_left)); } } @@ -917,31 +1352,34 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, case HTTPREQ_PUT: data_prd.read_callback = data_source_read_callback; data_prd.source.ptr = NULL; - stream_id = nghttp2_submit_request(httpc->h2, NULL, nva, nheader, - &data_prd, NULL); + stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, + &data_prd, conn->data); break; default: - stream_id = nghttp2_submit_request(httpc->h2, NULL, nva, nheader, - NULL, NULL); + stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, + NULL, conn->data); } Curl_safefree(nva); if(stream_id < 0) { + DEBUGF(infof(conn->data, "http2_send() send error\n")); *err = CURLE_SEND_ERROR; return -1; } - httpc->stream_id = stream_id; + infof(conn->data, "Using Stream ID: %x (easy handle %p)\n", + stream_id, conn->data); + stream->stream_id = stream_id; - rv = nghttp2_session_send(httpc->h2); + rv = nghttp2_session_send(h2); if(rv != 0) { *err = CURLE_SEND_ERROR; return -1; } - if(httpc->stream_id != -1) { + if(stream->stream_id != -1) { /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2 library calls data_source_read_callback. But only it found that no data available, so it deferred the DATA @@ -950,67 +1388,83 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, writable socket check is performed. To workaround this, we issue nghttp2_session_resume_data() here to bring back DATA transmission from deferred state. */ - nghttp2_session_resume_data(httpc->h2, httpc->stream_id); + nghttp2_session_resume_data(h2, stream->stream_id); } return len; + + fail: + free(nva); + *err = CURLE_SEND_ERROR; + return -1; } CURLcode Curl_http2_setup(struct connectdata *conn) { + CURLcode result; struct http_conn *httpc = &conn->proto.httpc; + struct HTTP *stream = conn->data->req.protop; + + stream->stream_id = -1; + + if(!stream->header_recvbuf) + stream->header_recvbuf = Curl_add_buffer_init(); + + if((conn->handler == &Curl_handler_http2_ssl) || + (conn->handler == &Curl_handler_http2)) + return CURLE_OK; /* already done */ + if(conn->handler->flags & PROTOPT_SSL) conn->handler = &Curl_handler_http2_ssl; else conn->handler = &Curl_handler_http2; - infof(conn->data, "Using HTTP2\n"); - httpc->bodystarted = FALSE; - httpc->closed = FALSE; - httpc->header_recvbuf = Curl_add_buffer_init(); - httpc->nread_header_recvbuf = 0; - httpc->data = NULL; - httpc->datalen = 0; - httpc->upload_left = 0; - httpc->upload_mem = NULL; - httpc->upload_len = 0; - httpc->stream_id = -1; - httpc->status_code = -1; + result = Curl_http2_init(conn); + if(result) + return result; + + infof(conn->data, "Using HTTP2, server supports multi-use\n"); + stream->upload_left = 0; + stream->upload_mem = NULL; + stream->upload_len = 0; + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + + httpc->pause_stream_id = 0; + + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->httpversion = 20; + conn->bundle->multiuse = BUNDLE_MULTIPLEX; - return 0; + infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n"); + Curl_multi_connchanged(conn->data->multi); + + return CURLE_OK; } -CURLcode Curl_http2_switched(struct connectdata *conn) +CURLcode Curl_http2_switched(struct connectdata *conn, + const char *mem, size_t nread) { - CURLcode rc; + CURLcode result; struct http_conn *httpc = &conn->proto.httpc; int rv; + ssize_t nproc; struct SessionHandle *data = conn->data; + struct HTTP *stream = conn->data->req.protop; + + result = Curl_http2_setup(conn); + if(result) + return result; httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET]; httpc->send_underlying = (sending)conn->send[FIRSTSOCKET]; conn->recv[FIRSTSOCKET] = http2_recv; conn->send[FIRSTSOCKET] = http2_send; - rv = (int) ((Curl_send*)httpc->send_underlying) - (conn, FIRSTSOCKET, - NGHTTP2_CLIENT_CONNECTION_PREFACE, - NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN, - &rc); - if(rc) - /* TODO: This may get CURLE_AGAIN */ - return rc; - - if(rv != 24) { - failf(data, "Only sent partial HTTP2 packet"); - return CURLE_SEND_ERROR; - } - if(conn->data->req.upgr101 == UPGR101_RECEIVED) { /* stream 1 is opened implicitly on upgrade */ - httpc->stream_id = 1; + stream->stream_id = 1; /* queue SETTINGS frame (again) */ rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, httpc->binlen, NULL); @@ -1019,10 +1473,14 @@ CURLcode Curl_http2_switched(struct connectdata *conn) nghttp2_strerror(rv), rv); return CURLE_HTTP2; } + + nghttp2_session_set_stream_user_data(httpc->h2, + stream->stream_id, + conn->data); } else { /* stream ID is unknown at this point */ - httpc->stream_id = -1; + stream->stream_id = -1; rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0); if(rv != 0) { failf(data, "nghttp2_submit_settings() failed: %s(%d)", @@ -1030,7 +1488,75 @@ CURLcode Curl_http2_switched(struct connectdata *conn) return CURLE_HTTP2; } } + + /* we are going to copy mem to httpc->inbuf. This is required since + mem is part of buffer pointed by stream->mem, and callbacks + called by nghttp2_session_mem_recv() will write stream specific + data into stream->mem, overwriting data already there. */ + if(H2_BUFSIZE < nread) { + failf(data, "connection buffer size is too small to store data following " + "HTTP Upgrade response header: buflen=%zu, datalen=%zu", + H2_BUFSIZE, nread); + return CURLE_HTTP2; + } + + infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer" + " after upgrade: len=%zu\n", + nread); + + memcpy(httpc->inbuf, mem, nread); + httpc->inbuflen = nread; + + nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf, + httpc->inbuflen); + + if(nghttp2_is_fatal((int)nproc)) { + failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", + nghttp2_strerror((int)nproc), (int)nproc); + return CURLE_HTTP2; + } + + DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc)); + + if((ssize_t)nread == nproc) { + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + } + else { + httpc->nread_inbuf += nproc; + } + + /* Try to send some frames since we may read SETTINGS already. */ + rv = nghttp2_session_send(httpc->h2); + + if(rv != 0) { + failf(data, "nghttp2_session_send() failed: %s(%d)", + nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } + return CURLE_OK; } -#endif +#else /* !USE_NGHTTP2 */ + +/* Satisfy external references even if http2 is not compiled in. */ + +#define CURL_DISABLE_TYPECHECK +#include <curl/curl.h> + +char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) +{ + (void) h; + (void) num; + return NULL; +} + +char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) +{ + (void) h; + (void) header; + return NULL; +} + +#endif /* USE_NGHTTP2 */ diff --git a/lib/http2.h b/lib/http2.h index 66aa6fd..bb7ad9c 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,11 @@ #ifdef USE_NGHTTP2 #include "http.h" + +/* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting + from the peer */ +#define DEFAULT_MAX_CONCURRENT_STREAMS 13 + /* * Store nghttp2 version info in this buffer, Prefix with a space. Return * total length written. @@ -37,13 +42,19 @@ CURLcode Curl_http2_send_request(struct connectdata *conn); CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, struct connectdata *conn); CURLcode Curl_http2_setup(struct connectdata *conn); -CURLcode Curl_http2_switched(struct connectdata *conn); +CURLcode Curl_http2_switched(struct connectdata *conn, + const char *data, size_t nread); +/* called from Curl_http_setup_conn */ +void Curl_http2_setup_conn(struct connectdata *conn); +void Curl_http2_setup_req(struct SessionHandle *data); #else /* USE_NGHTTP2 */ #define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switched(x) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_setup_conn(x) +#define Curl_http2_setup_req(x) #endif #endif /* HEADER_CURL_HTTP2_H */ diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 61a6098..7e91b37 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,15 +29,12 @@ #include "content_encoding.h" #include "http.h" -#include "curl_memory.h" #include "non-ascii.h" /* for Curl_convert_to_network prototype */ #include "strtoofft.h" #include "warnless.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* @@ -158,7 +155,7 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, if(result) { /* Curl_convert_from_network calls failf if unsuccessful */ /* Treat it as a bad hex character */ - return CHUNKE_ILLEGAL_HEX ; + return CHUNKE_ILLEGAL_HEX; } ch->datasize=curlx_strtoofft(ch->hexbuffer, &endptr, 16); @@ -221,7 +218,6 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, (ssize_t)piece); break; - case COMPRESS: default: failf (conn->data, "Unrecognized content encoding type. " diff --git a/lib/http_digest.c b/lib/http_digest.c index 55f5108..929e2c6 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,94 +26,14 @@ #include "urldata.h" #include "rawstr.h" -#include "curl_base64.h" -#include "curl_md5.h" +#include "curl_sasl.h" #include "http_digest.h" -#include "strtok.h" -#include "curl_memory.h" -#include "vtls/vtls.h" /* for Curl_rand() */ -#include "non-ascii.h" /* included for Curl_convert_... prototypes */ -#include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -#define MAX_VALUE_LENGTH 256 -#define MAX_CONTENT_LENGTH 1024 - -static void digest_cleanup_one(struct digestdata *dig); - -/* - * Return 0 on success and then the buffers are filled in fine. - * - * Non-zero means failure to parse. - */ -static int get_pair(const char *str, char *value, char *content, - const char **endptr) -{ - int c; - bool starts_with_quote = FALSE; - bool escape = FALSE; - - for(c=MAX_VALUE_LENGTH-1; (*str && (*str != '=') && c--); ) - *value++ = *str++; - *value=0; - - if('=' != *str++) - /* eek, no match */ - return 1; - - if('\"' == *str) { - /* this starts with a quote so it must end with one as well! */ - str++; - starts_with_quote = TRUE; - } - - for(c=MAX_CONTENT_LENGTH-1; *str && c--; str++) { - switch(*str) { - case '\\': - if(!escape) { - /* possibly the start of an escaped quote */ - escape = TRUE; - *content++ = '\\'; /* even though this is an escape character, we still - store it as-is in the target buffer */ - continue; - } - break; - case ',': - if(!starts_with_quote) { - /* this signals the end of the content if we didn't get a starting - quote and then we do "sloppy" parsing */ - c=0; /* the end */ - continue; - } - break; - case '\r': - case '\n': - /* end of string */ - c=0; - continue; - case '\"': - if(!escape && starts_with_quote) { - /* end of string */ - c=0; - continue; - } - break; - } - escape = FALSE; - *content++ = *str; - } - *content=0; - - *endptr = str; - - return 0; /* all is fine! */ -} - /* Test example headers: WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" @@ -121,177 +41,31 @@ Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598" */ -CURLdigest Curl_input_digest(struct connectdata *conn, - bool proxy, - const char *header) /* rest of the *-authenticate: - header */ +CURLcode Curl_input_digest(struct connectdata *conn, + bool proxy, + const char *header) /* rest of the *-authenticate: + header */ { - char *token = NULL; - char *tmp = NULL; - bool foundAuth = FALSE; - bool foundAuthInt = FALSE; - struct SessionHandle *data=conn->data; - bool before = FALSE; /* got a nonce before */ - struct digestdata *d; + struct SessionHandle *data = conn->data; + + /* Point to the correct struct with this */ + struct digestdata *digest; if(proxy) { - d = &data->state.proxydigest; + digest = &data->state.proxydigest; } else { - d = &data->state.digest; - } - - if(checkprefix("Digest", header)) { - header += strlen("Digest"); - - /* If we already have received a nonce, keep that in mind */ - if(d->nonce) - before = TRUE; - - /* clear off any former leftovers and init to defaults */ - digest_cleanup_one(d); - - for(;;) { - char value[MAX_VALUE_LENGTH]; - char content[MAX_CONTENT_LENGTH]; - - while(*header && ISSPACE(*header)) - header++; - - /* extract a value=content pair */ - if(!get_pair(header, value, content, &header)) { - if(Curl_raw_equal(value, "nonce")) { - d->nonce = strdup(content); - if(!d->nonce) - return CURLDIGEST_NOMEM; - } - else if(Curl_raw_equal(value, "stale")) { - if(Curl_raw_equal(content, "true")) { - d->stale = TRUE; - d->nc = 1; /* we make a new nonce now */ - } - } - else if(Curl_raw_equal(value, "realm")) { - d->realm = strdup(content); - if(!d->realm) - return CURLDIGEST_NOMEM; - } - else if(Curl_raw_equal(value, "opaque")) { - d->opaque = strdup(content); - if(!d->opaque) - return CURLDIGEST_NOMEM; - } - else if(Curl_raw_equal(value, "qop")) { - char *tok_buf; - /* tokenize the list and choose auth if possible, use a temporary - clone of the buffer since strtok_r() ruins it */ - tmp = strdup(content); - if(!tmp) - return CURLDIGEST_NOMEM; - token = strtok_r(tmp, ",", &tok_buf); - while(token != NULL) { - if(Curl_raw_equal(token, "auth")) { - foundAuth = TRUE; - } - else if(Curl_raw_equal(token, "auth-int")) { - foundAuthInt = TRUE; - } - token = strtok_r(NULL, ",", &tok_buf); - } - free(tmp); - /*select only auth o auth-int. Otherwise, ignore*/ - if(foundAuth) { - d->qop = strdup("auth"); - if(!d->qop) - return CURLDIGEST_NOMEM; - } - else if(foundAuthInt) { - d->qop = strdup("auth-int"); - if(!d->qop) - return CURLDIGEST_NOMEM; - } - } - else if(Curl_raw_equal(value, "algorithm")) { - d->algorithm = strdup(content); - if(!d->algorithm) - return CURLDIGEST_NOMEM; - if(Curl_raw_equal(content, "MD5-sess")) - d->algo = CURLDIGESTALGO_MD5SESS; - else if(Curl_raw_equal(content, "MD5")) - d->algo = CURLDIGESTALGO_MD5; - else - return CURLDIGEST_BADALGO; - } - else { - /* unknown specifier, ignore it! */ - } - } - else - break; /* we're done here */ - - /* pass all additional spaces here */ - while(*header && ISSPACE(*header)) - header++; - if(',' == *header) - /* allow the list to be comma-separated */ - header++; - } - /* We had a nonce since before, and we got another one now without - 'stale=true'. This means we provided bad credentials in the previous - request */ - if(before && !d->stale) - return CURLDIGEST_BAD; - - /* We got this header without a nonce, that's a bad Digest line! */ - if(!d->nonce) - return CURLDIGEST_BAD; + digest = &data->state.digest; } - else - /* else not a digest, get out */ - return CURLDIGEST_NONE; - - return CURLDIGEST_FINE; -} -/* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ -static void md5_to_ascii(unsigned char *source, /* 16 bytes */ - unsigned char *dest) /* 33 bytes */ -{ - int i; - for(i=0; i<16; i++) - snprintf((char *)&dest[i*2], 3, "%02x", source[i]); -} - -/* Perform quoted-string escaping as described in RFC2616 and its errata */ -static char *string_quoted(const char *source) -{ - char *dest, *d; - const char *s = source; - size_t n = 1; /* null terminator */ - - /* Calculate size needed */ - while(*s) { - ++n; - if(*s == '"' || *s == '\\') { - ++n; - } - ++s; - } + if(!checkprefix("Digest", header)) + return CURLE_BAD_CONTENT_ENCODING; - dest = malloc(n); - if(dest) { - s = source; - d = dest; - while(*s) { - if(*s == '"' || *s == '\\') { - *d++ = '\\'; - } - *d++ = *s++; - } - *d = 0; - } + header += strlen("Digest"); + while(*header && ISSPACE(*header)) + header++; - return dest; + return Curl_sasl_decode_digest_http_message(header, digest); } CURLcode Curl_output_digest(struct connectdata *conn, @@ -299,49 +73,35 @@ CURLcode Curl_output_digest(struct connectdata *conn, const unsigned char *request, const unsigned char *uripath) { - /* We have a Digest setup for this, use it! Now, to get all the details for - this sorted out, I must urge you dear friend to read up on the RFC2617 - section 3.2.2, */ - size_t urilen; - unsigned char md5buf[16]; /* 16 bytes/128 bits */ - unsigned char request_digest[33]; - unsigned char *md5this; - unsigned char ha1[33];/* 32 digits and 1 zero byte */ - unsigned char ha2[33];/* 32 digits and 1 zero byte */ - char cnoncebuf[33]; - char *cnonce = NULL; - size_t cnonce_sz = 0; - char *tmp = NULL; + CURLcode result; + struct SessionHandle *data = conn->data; + unsigned char *path; + char *tmp; + char *response; + size_t len; + bool have_chlg; + + /* Point to the address of the pointer that holds the string to send to the + server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; - size_t userlen; + + /* Point to the name and password for this */ const char *userp; - char *userp_quoted; const char *passwdp; - struct auth *authp; - struct SessionHandle *data = conn->data; - struct digestdata *d; - CURLcode rc; -/* 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) \ - rc = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \ - if(rc != CURLE_OK) { \ - free(b); \ - return rc; \ - } + /* Point to the correct struct with this */ + struct digestdata *digest; + struct auth *authp; if(proxy) { - d = &data->state.proxydigest; + digest = &data->state.proxydigest; allocuserpwd = &conn->allocptr.proxyuserpwd; userp = conn->proxyuser; passwdp = conn->proxypasswd; authp = &data->state.authproxy; } else { - d = &data->state.digest; + digest = &data->state.digest; allocuserpwd = &conn->allocptr.userpwd; userp = conn->user; passwdp = conn->passwd; @@ -352,75 +112,21 @@ CURLcode Curl_output_digest(struct connectdata *conn, /* not set means empty */ if(!userp) - userp=""; + userp = ""; if(!passwdp) - passwdp=""; + passwdp = ""; + +#if defined(USE_WINDOWS_SSPI) + have_chlg = digest->input_token ? TRUE : FALSE; +#else + have_chlg = digest->nonce ? TRUE : FALSE; +#endif - if(!d->nonce) { + if(!have_chlg) { authp->done = FALSE; return CURLE_OK; } - authp->done = TRUE; - - if(!d->nc) - d->nc = 1; - - if(!d->cnonce) { - snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", - Curl_rand(data), Curl_rand(data), - Curl_rand(data), Curl_rand(data)); - rc = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), - &cnonce, &cnonce_sz); - if(rc) - return rc; - d->cnonce = cnonce; - } - - /* - if the algorithm is "MD5" or unspecified (which then defaults to MD5): - - A1 = unq(username-value) ":" unq(realm-value) ":" passwd - - if the algorithm is "MD5-sess" then: - - A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) - ":" unq(nonce-value) ":" unq(cnonce-value) - */ - - md5this = (unsigned char *) - aprintf("%s:%s:%s", userp, d->realm, passwdp); - if(!md5this) - return CURLE_OUT_OF_MEMORY; - - CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, md5this); - Curl_safefree(md5this); - md5_to_ascii(md5buf, ha1); - - if(d->algo == CURLDIGESTALGO_MD5SESS) { - /* nonce and cnonce are OUTSIDE the hash */ - tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, (unsigned char *)tmp); - Curl_safefree(tmp); - md5_to_ascii(md5buf, ha1); - } - - /* - If the "qop" directive's value is "auth" or is unspecified, then A2 is: - - A2 = Method ":" digest-uri-value - - If the "qop" value is "auth-int", then A2 is: - - A2 = Method ":" digest-uri-value ":" H(entity-body) - - (The "Method" value is the HTTP request method as specified in section - 5.1.1 of RFC 2616) - */ /* So IE browsers < v7 cut off the URI part at the query part when they evaluate the MD5 and some (IIS?) servers work with them so we may need to @@ -435,164 +141,39 @@ CURLcode Curl_output_digest(struct connectdata *conn, http://www.fngtps.com/2006/09/http-authentication */ - if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) - urilen = tmp - (char *)uripath; - else - urilen = strlen((char *)uripath); - - md5this = (unsigned char *)aprintf("%s:%.*s", request, urilen, uripath); + if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) { + size_t urilen = tmp - (char *)uripath; - if(d->qop && Curl_raw_equal(d->qop, "auth-int")) { - /* We don't support auth-int for PUT or POST at the moment. - TODO: replace md5 of empty string with entity-body for PUT/POST */ - unsigned char *md5this2 = (unsigned char *) - aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); - Curl_safefree(md5this); - md5this = md5this2; + path = (unsigned char *) aprintf("%.*s", urilen, uripath); } + else + path = (unsigned char *) strdup((char *) uripath); - if(!md5this) - return CURLE_OUT_OF_MEMORY; - - CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, md5this); - Curl_safefree(md5this); - md5_to_ascii(md5buf, ha2); - - if(d->qop) { - md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s", - ha1, - d->nonce, - d->nc, - d->cnonce, - d->qop, - ha2); - } - else { - md5this = (unsigned char *)aprintf("%s:%s:%s", - ha1, - d->nonce, - ha2); - } - if(!md5this) + if(!path) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, md5this); - Curl_safefree(md5this); - md5_to_ascii(md5buf, request_digest); - - /* for test case 64 (snooped from a Mozilla 1.3a request) - - Authorization: Digest username="testuser", realm="testrealm", \ - nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" - - Digest parameters are all quoted strings. Username which is provided by - the user will need double quotes and backslashes within it escaped. For - the other fields, this shouldn't be an issue. realm, nonce, and opaque - are copied as is from the server, escapes and all. cnonce is generated - with web-safe characters. uri is already percent encoded. nc is 8 hex - characters. algorithm and qop with standard values only contain web-safe - chracters. - */ - userp_quoted = string_quoted(userp); - if(!userp_quoted) - return CURLE_OUT_OF_MEMORY; + result = Curl_sasl_create_digest_http_message(data, userp, passwdp, request, + path, digest, &response, &len); + free(path); + if(result) + return result; - if(d->qop) { - *allocuserpwd = - aprintf( "%sAuthorization: Digest " - "username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%.*s\", " - "cnonce=\"%s\", " - "nc=%08x, " - "qop=%s, " - "response=\"%s\"", - proxy?"Proxy-":"", - userp_quoted, - d->realm, - d->nonce, - urilen, uripath, /* this is the PATH part of the URL */ - d->cnonce, - d->nc, - d->qop, - request_digest); - - if(Curl_raw_equal(d->qop, "auth")) - d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded - which tells to the server how many times you are using the - same nonce in the qop=auth mode. */ - } - else { - *allocuserpwd = - aprintf( "%sAuthorization: Digest " - "username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%.*s\", " - "response=\"%s\"", - proxy?"Proxy-":"", - userp_quoted, - d->realm, - d->nonce, - urilen, uripath, /* this is the PATH part of the URL */ - request_digest); - } - Curl_safefree(userp_quoted); + *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", + proxy ? "Proxy-" : "", + response); + free(response); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; - /* Add optional fields */ - if(d->opaque) { - /* append opaque */ - tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - free(*allocuserpwd); - *allocuserpwd = tmp; - } - - if(d->algorithm) { - /* append algorithm */ - tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - free(*allocuserpwd); - *allocuserpwd = tmp; - } - - /* append CRLF + zero (3 bytes) to the userpwd header */ - userlen = strlen(*allocuserpwd); - tmp = realloc(*allocuserpwd, userlen + 3); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - strcpy(&tmp[userlen], "\r\n"); /* append the data */ - *allocuserpwd = tmp; + authp->done = TRUE; return CURLE_OK; } -static void digest_cleanup_one(struct digestdata *d) -{ - Curl_safefree(d->nonce); - Curl_safefree(d->cnonce); - Curl_safefree(d->realm); - Curl_safefree(d->opaque); - Curl_safefree(d->qop); - Curl_safefree(d->algorithm); - - d->nc = 0; - d->algo = CURLDIGESTALGO_MD5; /* default algorithm */ - d->stale = FALSE; /* default means normal, not stale */ -} - - void Curl_digest_cleanup(struct SessionHandle *data) { - digest_cleanup_one(&data->state.digest); - digest_cleanup_one(&data->state.proxydigest); + Curl_sasl_digest_cleanup(&data->state.digest); + Curl_sasl_digest_cleanup(&data->state.proxydigest); } #endif diff --git a/lib/http_digest.h b/lib/http_digest.h index c6a4e91..d13d563 100644 --- a/lib/http_digest.h +++ b/lib/http_digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,24 +23,9 @@ ***************************************************************************/ #include "curl_setup.h" -typedef enum { - CURLDIGEST_NONE, /* not a digest */ - CURLDIGEST_BAD, /* a digest, but one we don't like */ - CURLDIGEST_BADALGO, /* unsupported algorithm requested */ - CURLDIGEST_NOMEM, - CURLDIGEST_FINE, /* a digest we act on */ - - CURLDIGEST_LAST /* last entry in this enum, don't use */ -} CURLdigest; - -enum { - CURLDIGESTALGO_MD5, - CURLDIGESTALGO_MD5SESS -}; - /* this is for digest header input */ -CURLdigest Curl_input_digest(struct connectdata *conn, - bool proxy, const char *header); +CURLcode Curl_input_digest(struct connectdata *conn, + bool proxy, const char *header); /* this is for creating digest header output */ CURLcode Curl_output_digest(struct connectdata *conn, diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index c8bfa29..a1baf29 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,13 +22,7 @@ #include "curl_setup.h" -#ifdef HAVE_GSSAPI -#ifdef HAVE_OLD_GSSMIT -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#define NCOMPAT 1 -#endif - -#ifndef CURL_DISABLE_HTTP +#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) #include "urldata.h" #include "sendf.h" @@ -36,97 +30,63 @@ #include "rawstr.h" #include "curl_base64.h" #include "http_negotiate.h" -#include "curl_memory.h" +#include "curl_sasl.h" #include "url.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -static int -get_gss_name(struct connectdata *conn, bool proxy, gss_name_t *server) -{ - OM_uint32 major_status, minor_status; - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - char name[2048]; - const char* service = "HTTP"; - - token.length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name : - conn->host.name) + 1; - if(token.length + 1 > sizeof(name)) - return EMSGSIZE; - - snprintf(name, sizeof(name), "%s@%s", service, proxy ? conn->proxy.name : - conn->host.name); - - token.value = (void *) name; - major_status = gss_import_name(&minor_status, - &token, - GSS_C_NT_HOSTBASED_SERVICE, - server); - - return GSS_ERROR(major_status) ? -1 : 0; -} - -static void -log_gss_error(struct connectdata *conn, OM_uint32 error_status, - const char *prefix) -{ - OM_uint32 maj_stat, min_stat; - OM_uint32 msg_ctx = 0; - gss_buffer_desc status_string; - char buf[1024]; - size_t len; - - snprintf(buf, sizeof(buf), "%s", prefix); - len = strlen(buf); - do { - maj_stat = gss_display_status(&min_stat, - error_status, - GSS_C_MECH_CODE, - GSS_C_NO_OID, - &msg_ctx, - &status_string); - if(sizeof(buf) > len + status_string.length + 1) { - snprintf(buf + len, sizeof(buf) - len, - ": %s", (char*) status_string.value); - len += status_string.length; - } - gss_release_buffer(&min_stat, &status_string); - } while(!GSS_ERROR(maj_stat) && msg_ctx != 0); - - infof(conn->data, "%s\n", buf); -} - -/* returning zero (0) means success, everything else is treated as "failure" - with no care exactly what the failure was */ -int Curl_input_negotiate(struct connectdata *conn, bool proxy, - const char *header) +CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, + const char *header) { struct SessionHandle *data = conn->data; struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: &data->state.negotiate; OM_uint32 major_status, minor_status, discard_st; + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - int ret; size_t len; size_t rawlen = 0; - CURLcode error; + CURLcode result; if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(data); - return -1; + return CURLE_LOGIN_DENIED; } - if(neg_ctx->server_name == NULL && - (ret = get_gss_name(conn, proxy, &neg_ctx->server_name))) - return ret; + if(!neg_ctx->server_name) { + /* Generate our SPN */ + char *spn = Curl_sasl_build_gssapi_spn( + proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] : + data->set.str[STRING_SERVICE_NAME], + proxy ? conn->proxy.name : conn->host.name); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Populate the SPN structure */ + spn_token.value = spn; + spn_token.length = strlen(spn); + + /* Import the SPN */ + major_status = gss_import_name(&minor_status, &spn_token, + GSS_C_NT_HOSTBASED_SERVICE, + &neg_ctx->server_name); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, minor_status, "gss_import_name() failed: "); + + free(spn); + + return CURLE_OUT_OF_MEMORY; + } + + free(spn); + } header += strlen("Negotiate"); while(*header && ISSPACE(*header)) @@ -134,10 +94,17 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, len = strlen(header); if(len > 0) { - error = Curl_base64_decode(header, - (unsigned char **)&input_token.value, &rawlen); - if(error || rawlen == 0) - return -1; + result = Curl_base64_decode(header, (unsigned char **)&input_token.value, + &rawlen); + if(result) + return result; + + if(!rawlen) { + infof(data, "Negotiate handshake failure (empty challenge message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + input_token.length = rawlen; DEBUGASSERT(input_token.value != NULL); @@ -151,6 +118,7 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, + TRUE, NULL); Curl_safefree(input_token.value); @@ -158,20 +126,21 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, if(GSS_ERROR(major_status)) { if(output_token.value) gss_release_buffer(&discard_st, &output_token); - log_gss_error(conn, minor_status, "gss_init_sec_context() failed: "); - return -1; + Curl_gss_log_error(conn->data, minor_status, + "gss_init_sec_context() failed: "); + return CURLE_OUT_OF_MEMORY; } if(!output_token.value || !output_token.length) { if(output_token.value) gss_release_buffer(&discard_st, &output_token); - return -1; + return CURLE_OUT_OF_MEMORY; } neg_ctx->output_token = output_token; - return 0; -} + return CURLE_OK; +} CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) { @@ -180,18 +149,18 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) char *encoded = NULL; size_t len = 0; char *userp; - CURLcode error; + CURLcode result; OM_uint32 discard_st; - error = Curl_base64_encode(conn->data, - neg_ctx->output_token.value, - neg_ctx->output_token.length, - &encoded, &len); - if(error) { + result = Curl_base64_encode(conn->data, + neg_ctx->output_token.value, + neg_ctx->output_token.length, + &encoded, &len); + if(result) { gss_release_buffer(&discard_st, &neg_ctx->output_token); neg_ctx->output_token.value = NULL; neg_ctx->output_token.length = 0; - return error; + return result; } if(!encoded || !len) { @@ -212,7 +181,7 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) conn->allocptr.userpwd = userp; } - Curl_safefree(encoded); + free(encoded); return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; } @@ -238,6 +207,4 @@ void Curl_cleanup_negotiate(struct SessionHandle *data) cleanup(&data->state.proxyneg); } - -#endif -#endif +#endif /* HAVE_GSSAPI && !CURL_DISABLE_HTTP && USE_SPNEGO */ diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h index f7efe8c..a8eb980 100644 --- a/lib/http_negotiate.h +++ b/lib/http_negotiate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,8 +25,8 @@ #ifdef USE_SPNEGO /* this is for Negotiate header input */ -int Curl_input_negotiate(struct connectdata *conn, bool proxy, - const char *header); +CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, + const char *header); /* this is for creating Negotiate header output */ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy); diff --git a/lib/http_negotiate_sspi.c b/lib/http_negotiate_sspi.c index 61581f1..a50ea96 100644 --- a/lib/http_negotiate_sspi.c +++ b/lib/http_negotiate_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -33,30 +33,27 @@ #include "curl_base64.h" #include "curl_sasl.h" #include "http_negotiate.h" -#include "curl_memory.h" #include "curl_multibyte.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -/* returning zero (0) means success, everything else is treated as "failure" - with no care exactly what the failure was */ -int Curl_input_negotiate(struct connectdata *conn, bool proxy, - const char *header) +CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, + const char *header) { + struct SessionHandle *data = conn->data; BYTE *input_token = NULL; SecBufferDesc out_buff_desc; SecBuffer out_sec_buff; SecBufferDesc in_buff_desc; SecBuffer in_sec_buff; - unsigned long context_attributes; - TimeStamp lifetime; - int ret; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ size_t len = 0, input_token_len = 0; - CURLcode error; + CURLcode result; /* Point to the username and password */ const char *userp; @@ -68,12 +65,12 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, if(proxy) { userp = conn->proxyuser; passwdp = conn->proxypasswd; - neg_ctx = &conn->data->state.proxyneg; + neg_ctx = &data->state.proxyneg; } else { userp = conn->user; passwdp = conn->passwd; - neg_ctx = &conn->data->state.negotiate; + neg_ctx = &data->state.negotiate; } /* Not set means empty */ @@ -87,34 +84,36 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ - Curl_cleanup_negotiate(conn->data); - return -1; + Curl_cleanup_negotiate(data); + return CURLE_LOGIN_DENIED; } if(!neg_ctx->server_name) { /* Check proxy auth requested but no given proxy name */ if(proxy && !conn->proxy.name) - return -1; + return CURLE_BAD_FUNCTION_ARGUMENT; /* Generate our SPN */ - neg_ctx->server_name = Curl_sasl_build_spn("HTTP", - proxy ? conn->proxy.name : - conn->host.name); + neg_ctx->server_name = Curl_sasl_build_spn( + proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] : + data->set.str[STRING_SERVICE_NAME], + proxy ? conn->proxy.name : conn->host.name); if(!neg_ctx->server_name) - return -1; + return CURLE_OUT_OF_MEMORY; } if(!neg_ctx->output_token) { PSecPkgInfo SecurityPackage; - ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Negotiate"), - &SecurityPackage); - if(ret != SEC_E_OK) - return -1; + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_NEGOTIATE), + &SecurityPackage); + if(status != SEC_E_OK) + return CURLE_NOT_BUILT_IN; /* Allocate input and output buffers according to the max token size as indicated by the security package */ - neg_ctx->max_token_length = SecurityPackage->cbMaxToken; - neg_ctx->output_token = malloc(neg_ctx->max_token_length); + neg_ctx->token_max = SecurityPackage->cbMaxToken; + neg_ctx->output_token = malloc(neg_ctx->token_max); s_pSecFn->FreeContextBuffer(SecurityPackage); } @@ -129,7 +128,7 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, if(neg_ctx->context) { /* The server rejected our authentication and hasn't suppled any more negotiation mechanisms */ - return -1; + return CURLE_LOGIN_DENIED; } /* We have to acquire credentials and allocate memory for the context */ @@ -137,13 +136,13 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, neg_ctx->context = malloc(sizeof(CtxtHandle)); if(!neg_ctx->credentials || !neg_ctx->context) - return -1; + return CURLE_OUT_OF_MEMORY; if(userp && *userp) { /* Populate our identity structure */ - error = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity); - if(error) - return -1; + result = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity); + if(result) + return result; /* Allow proper cleanup of the identity structure */ neg_ctx->p_identity = &neg_ctx->identity; @@ -155,19 +154,26 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, /* Acquire our credientials handle */ neg_ctx->status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT("Negotiate"), + (TCHAR *) TEXT(SP_NAME_NEGOTIATE), SECPKG_CRED_OUTBOUND, NULL, neg_ctx->p_identity, NULL, NULL, - neg_ctx->credentials, &lifetime); + neg_ctx->credentials, &expiry); if(neg_ctx->status != SEC_E_OK) - return -1; + return CURLE_LOGIN_DENIED; } else { - error = Curl_base64_decode(header, - (unsigned char **)&input_token, - &input_token_len); - if(error || !input_token_len) - return -1; + result = Curl_base64_decode(header, + (unsigned char **)&input_token, + &input_token_len); + if(result) + return result; + + if(!input_token_len) { + infof(data, + "Negotiate handshake failure (empty challenge message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } } /* Setup the "output" security buffer */ @@ -176,7 +182,7 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, out_buff_desc.pBuffers = &out_sec_buff; out_sec_buff.BufferType = SECBUFFER_TOKEN; out_sec_buff.pvBuffer = neg_ctx->output_token; - out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->max_token_length); + out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->token_max); /* Setup the "input" security buffer if present */ if(input_token) { @@ -200,28 +206,27 @@ int Curl_input_negotiate(struct connectdata *conn, bool proxy, 0, neg_ctx->context, &out_buff_desc, - &context_attributes, - &lifetime); + &attrs, + &expiry); - Curl_safefree(input_token); + free(input_token); if(GSS_ERROR(neg_ctx->status)) - return -1; + return CURLE_OUT_OF_MEMORY; if(neg_ctx->status == SEC_I_COMPLETE_NEEDED || neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) { neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, &out_buff_desc); if(GSS_ERROR(neg_ctx->status)) - return -1; + return CURLE_RECV_ERROR; } neg_ctx->output_token_length = out_sec_buff.cbBuffer; - return 0; + return CURLE_OK; } - CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) { struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: @@ -258,25 +263,30 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) static void cleanup(struct negotiatedata *neg_ctx) { + /* Free our security context */ if(neg_ctx->context) { s_pSecFn->DeleteSecurityContext(neg_ctx->context); free(neg_ctx->context); neg_ctx->context = NULL; } + /* Free our credentials handle */ if(neg_ctx->credentials) { s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials); free(neg_ctx->credentials); neg_ctx->credentials = NULL; } - neg_ctx->max_token_length = 0; - Curl_safefree(neg_ctx->output_token); + /* Free our identity */ + Curl_sspi_free_identity(neg_ctx->p_identity); + neg_ctx->p_identity = NULL; + /* Free the SPN and output token */ Curl_safefree(neg_ctx->server_name); + Curl_safefree(neg_ctx->output_token); - Curl_sspi_free_identity(neg_ctx->p_identity); - neg_ctx->p_identity = NULL; + /* Reset any variables */ + neg_ctx->token_max = 0; } void Curl_cleanup_negotiate(struct SessionHandle *data) diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 5343eb7..4373d62 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,10 +35,7 @@ #include "progress.h" #include "non-ascii.h" #include "connect.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curlx.h" #include "curl_memory.h" @@ -71,10 +68,11 @@ CURLcode Curl_proxy_connect(struct connectdata *conn) conn->data->req.protop = &http_proxy; connkeep(conn, "HTTP proxy CONNECT"); result = Curl_proxyCONNECT(conn, FIRSTSOCKET, - conn->host.name, conn->remote_port); + conn->host.name, conn->remote_port, FALSE); conn->data->req.protop = prot_save; if(CURLE_OK != result) return result; + Curl_safefree(conn->allocptr.proxyuserpwd); #else return CURLE_NOT_BUILT_IN; #endif @@ -87,12 +85,16 @@ CURLcode Curl_proxy_connect(struct connectdata *conn) * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This * function will issue the necessary commands to get a seamless tunnel through * this proxy. After that, the socket can be used just as a normal socket. + * + * 'blocking' set to TRUE means that this function will do the entire CONNECT + * + response in a blocking fashion. Should be avoided! */ CURLcode Curl_proxyCONNECT(struct connectdata *conn, int sockindex, const char *hostname, - int remote_port) + int remote_port, + bool blocking) { int subversion=0; struct SessionHandle *data=conn->data; @@ -123,13 +125,11 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, infof(data, "Establish HTTP proxy tunnel to %s:%hu\n", hostname, remote_port); - if(data->req.newurl) { /* This only happens if we've looped here due to authentication reasons, and we don't really use the newly cloned URL here then. Just free() it. */ - free(data->req.newurl); - data->req.newurl = NULL; - } + free(data->req.newurl); + data->req.newurl = NULL; /* initialize a dynamic send-buffer */ req_buffer = Curl_add_buffer_init(); @@ -139,7 +139,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, host_port = aprintf("%s:%hu", hostname, remote_port); if(!host_port) { - free(req_buffer); + Curl_add_buffer_free(req_buffer); return CURLE_OUT_OF_MEMORY; } @@ -148,7 +148,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, free(host_port); - if(CURLE_OK == result) { + if(!result) { char *host=(char *)""; const char *proxyconn=""; const char *useragent=""; @@ -159,7 +159,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, hostname, conn->bits.ipv6_ip?"]":"", remote_port); if(!hostheader) { - free(req_buffer); + Curl_add_buffer_free(req_buffer); return CURLE_OUT_OF_MEMORY; } @@ -167,7 +167,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, host = aprintf("Host: %s\r\n", hostheader); if(!host) { free(hostheader); - free(req_buffer); + Curl_add_buffer_free(req_buffer); return CURLE_OUT_OF_MEMORY; } } @@ -197,14 +197,14 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, free(host); free(hostheader); - if(CURLE_OK == result) + if(!result) result = Curl_add_custom_headers(conn, TRUE, req_buffer); - if(CURLE_OK == result) + if(!result) /* CRLF terminate the request */ result = Curl_add_bufferf(req_buffer, "\r\n"); - if(CURLE_OK == result) { + if(!result) { /* Send the connect request to the proxy */ /* BLOCKING */ result = @@ -216,7 +216,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, failf(data, "Failed sending CONNECT to proxy"); } - Curl_safefree(req_buffer); + Curl_add_buffer_free(req_buffer); if(result) return result; @@ -229,12 +229,14 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, return CURLE_RECV_ERROR; } - if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0)) - /* return so we'll be called again polling-style */ - return CURLE_OK; - else { - DEBUGF(infof(data, - "Read response immediately from proxy CONNECT\n")); + if(!blocking) { + if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0)) + /* return so we'll be called again polling-style */ + return CURLE_OK; + else { + DEBUGF(infof(data, + "Read response immediately from proxy CONNECT\n")); + } } /* at this point, the tunnel_connecting phase is over. */ @@ -252,7 +254,6 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, nread=0; perline=0; - keepon=TRUE; while((nread<BUFSIZE) && (keepon && !error)) { @@ -286,7 +287,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ conn->bits.proxy_connect_closed = TRUE; - infof(data, "Proxy CONNECT connection closed"); + infof(data, "Proxy CONNECT connection closed\n"); } else { error = SELECT_ERROR; @@ -468,7 +469,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, result = Curl_http_input_auth(conn, proxy, auth); - Curl_safefree(auth); + free(auth); if(result) return result; @@ -555,11 +556,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, infof(data, "Connect me again please\n"); } else { - if(data->req.newurl) { - /* this won't be used anymore for the CONNECT so free it now */ - free(data->req.newurl); - data->req.newurl = NULL; - } + free(data->req.newurl); + data->req.newurl = NULL; /* failure, close this connection to avoid re-use */ connclose(conn, "proxy CONNECT failure"); Curl_closesocket(conn, conn->sock[sockindex]); diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 2b5e9c9..9c4f020 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,7 +26,8 @@ /* ftp can use this as well */ CURLcode Curl_proxyCONNECT(struct connectdata *conn, int tunnelsocket, - const char *hostname, int remote_port); + const char *hostname, int remote_port, + bool blocking); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600*1000) @@ -34,7 +35,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, CURLcode Curl_proxy_connect(struct connectdata *conn); #else -#define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN +#define Curl_proxyCONNECT(x,y,z,w,v) CURLE_NOT_BUILT_IN #define Curl_proxy_connect(x) CURLE_OK #endif diff --git a/lib/idn_win32.c b/lib/idn_win32.c index 464964b..b369723 100644 --- a/lib/idn_win32.c +++ b/lib/idn_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,8 +35,31 @@ #include "memdebug.h" #ifdef WANT_IDN_PROTOTYPES -WINBASEAPI int WINAPI IdnToAscii(DWORD, const WCHAR *, int, WCHAR *, int); -WINBASEAPI int WINAPI IdnToUnicode(DWORD, const WCHAR *, int, WCHAR *, int); +# if defined(_SAL_VERSION) +WINNORMALIZEAPI int WINAPI +IdnToAscii(_In_ DWORD dwFlags, + _In_reads_(cchUnicodeChar) LPCWSTR lpUnicodeCharStr, + _In_ int cchUnicodeChar, + _Out_writes_opt_(cchASCIIChar) LPWSTR lpASCIICharStr, + _In_ int cchASCIIChar); +WINNORMALIZEAPI int WINAPI +IdnToUnicode(_In_ DWORD dwFlags, + _In_reads_(cchASCIIChar) LPCWSTR lpASCIICharStr, + _In_ int cchASCIIChar, + _Out_writes_opt_(cchUnicodeChar) LPWSTR lpUnicodeCharStr, + _In_ int cchUnicodeChar); +# else +WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, + const WCHAR *lpUnicodeCharStr, + int cchUnicodeChar, + WCHAR *lpASCIICharStr, + int cchASCIIChar); +WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, + const WCHAR *lpASCIICharStr, + int cchASCIIChar, + WCHAR *lpUnicodeCharStr, + int cchUnicodeChar); +# endif #endif #define IDN_MAX_LENGTH 255 diff --git a/lib/if2ip.c b/lib/if2ip.c index 05ae7d6..6e6f969 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -53,9 +53,7 @@ #include "inet_ntop.h" #include "strequal.h" #include "if2ip.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -63,6 +61,38 @@ /* ------------------------------------------------------------------ */ +/* 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 *) sa; + const unsigned char * b = sa6->sin6_addr.s6_addr; + unsigned short w = (unsigned short) ((b[0] << 8) | b[1]); + + switch(w & 0xFFC0) { + case 0xFE80: + return IPV6_SCOPE_LINKLOCAL; + case 0xFEC0: + return IPV6_SCOPE_SITELOCAL; + case 0x0000: + w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] | + b[10] | b[11] | b[12] | b[13] | b[14]; + if(w || b[15] != 0x01) + break; + return IPV6_SCOPE_NODELOCAL; + default: + break; + } + } +#endif + + return IPV6_SCOPE_GLOBAL; +} + + #if defined(HAVE_GETIFADDRS) bool Curl_if_is_interface_name(const char *interf) @@ -84,41 +114,58 @@ bool Curl_if_is_interface_name(const char *interf) } if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - const char *interf, char *buf, int buf_size) + unsigned int remote_scope_id, 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; + +#ifndef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + (void) remote_scope_id; +#endif + #endif if(getifaddrs(&head) >= 0) { - for(iface=head; iface != NULL; iface=iface->ifa_next) { + for(iface = head; iface != NULL; iface=iface->ifa_next) { if(iface->ifa_addr != NULL) { if(iface->ifa_addr->sa_family == af) { if(curl_strequal(iface->ifa_name, interf)) { void *addr; char *ip; - char scope[12]=""; + char scope[12] = ""; char ipstr[64]; #ifdef ENABLE_IPV6 if(af == AF_INET6) { unsigned int scopeid = 0; + unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr); + + if(ifscope != remote_scope) { + /* We are interested only in interface addresses whose + scope matches the remote address we want to + connect to: global for global, link-local for + link-local, etc... */ + if(res == IF2IP_NOT_FOUND) res = IF2IP_AF_NOT_SUPPORTED; + continue; + } + addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr; #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID /* Include the scope of this interface as part of the address */ scopeid = ((struct sockaddr_in6 *)iface->ifa_addr)->sin6_scope_id; -#endif - if(scopeid != remote_scope) { - /* We are interested only in interface addresses whose - scope ID matches the remote address we want to - connect to: global (0) for global, link-local for - link-local, etc... */ - if(res == IF2IP_NOT_FOUND) res = IF2IP_AF_NOT_SUPPORTED; + + /* If given, scope id should match. */ + if(remote_scope_id && scopeid != remote_scope_id) { + if(res == IF2IP_NOT_FOUND) + res = IF2IP_AF_NOT_SUPPORTED; + continue; } +#endif if(scopeid) snprintf(scope, sizeof(scope), "%%%u", scopeid); } @@ -137,8 +184,10 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, } } } + freeifaddrs(head); } + return res; } @@ -149,12 +198,13 @@ bool Curl_if_is_interface_name(const char *interf) /* This is here just to support the old interfaces */ char buf[256]; - return (Curl_if2ip(AF_INET, 0, interf, buf, sizeof(buf)) == + return (Curl_if2ip(AF_INET, 0 /* unused */, 0, interf, buf, sizeof(buf)) == IF2IP_NOT_FOUND) ? FALSE : TRUE; } if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - const char *interf, char *buf, int buf_size) + unsigned int remote_scope_id, const char *interf, + char *buf, int buf_size) { struct ifreq req; struct in_addr in; @@ -163,6 +213,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, size_t len; (void)remote_scope; + (void)remote_scope_id; if(!interf || (af != AF_INET)) return IF2IP_NOT_FOUND; @@ -205,10 +256,12 @@ bool Curl_if_is_interface_name(const char *interf) } if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - const char *interf, char *buf, int buf_size) + unsigned int remote_scope_id, const char *interf, + char *buf, int buf_size) { (void) af; (void) remote_scope; + (void) remote_scope_id; (void) interf; (void) buf; (void) buf_size; diff --git a/lib/if2ip.h b/lib/if2ip.h index ac58752..78bb0bd 100644 --- a/lib/if2ip.h +++ b/lib/if2ip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,6 +23,14 @@ ***************************************************************************/ #include "curl_setup.h" +/* IPv6 address scopes. */ +#define IPV6_SCOPE_GLOBAL 0 /* Global scope. */ +#define IPV6_SCOPE_LINKLOCAL 1 /* Link-local scope. */ +#define IPV6_SCOPE_SITELOCAL 2 /* Site-local scope (deprecated). */ +#define IPV6_SCOPE_NODELOCAL 3 /* Loopback. */ + +unsigned int Curl_ipv6_scope(const struct sockaddr *sa); + bool Curl_if_is_interface_name(const char *interf); typedef enum { @@ -32,7 +40,8 @@ typedef enum { } if2ip_result_t; if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, - const char *interf, char *buf, int buf_size); + unsigned int remote_scope_id, const char *interf, + char *buf, int buf_size); #ifdef __INTERIX @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -61,7 +61,6 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" -#include "if2ip.h" #include "hostip.h" #include "progress.h" #include "transfer.h" @@ -81,9 +80,7 @@ #include "rawstr.h" #include "curl_sasl.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -106,10 +103,12 @@ static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...); static CURLcode imap_parse_url_options(struct connectdata *conn); static CURLcode imap_parse_url_path(struct connectdata *conn); static CURLcode imap_parse_custom_request(struct connectdata *conn); -static CURLcode imap_calc_sasl_details(struct connectdata *conn, - const char **mech, - char **initresp, size_t *len, - imapstate *state1, imapstate *state2); +static CURLcode imap_perform_authenticate(struct connectdata *conn, + const char *mech, + const char *initresp); +static CURLcode imap_continue_authenticate(struct connectdata *conn, + const char *resp); +static void imap_get_message(char *buffer, char** outptr); /* * IMAP protocol handler. @@ -132,7 +131,7 @@ const struct Curl_handler Curl_handler_imap = { ZERO_NULL, /* readwrite */ PORT_IMAP, /* defport */ CURLPROTO_IMAP, /* protocol */ - PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD /* flags */ + PROTOPT_CLOSEACTION /* flags */ }; #ifdef USE_SSL @@ -157,8 +156,7 @@ const struct Curl_handler Curl_handler_imaps = { ZERO_NULL, /* readwrite */ PORT_IMAPS, /* defport */ CURLPROTO_IMAPS, /* protocol */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | - PROTOPT_NEEDSPWD /* flags */ + PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */ }; #endif @@ -214,6 +212,18 @@ static const struct Curl_handler Curl_handler_imaps_proxy = { #endif #endif +/* SASL parameters for the imap protocol */ +static const struct SASLproto saslimap = { + "imap", /* The service name */ + '+', /* Code received when continuation is expected */ + 'O', /* Code to receive upon authentication success */ + 0, /* Maximum initial response length (no max) */ + imap_perform_authenticate, /* Send authentication command */ + imap_continue_authenticate, /* Send authentication continuation */ + imap_get_message /* Get SASL response message */ +}; + + #ifdef USE_SSL static void imap_to_imaps(struct connectdata *conn) { @@ -354,16 +364,7 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, (len >= 2 && !memcmp("+ ", line, 2))) { switch(imapc->state) { /* States which are interested in continuation responses */ - case IMAP_AUTHENTICATE_PLAIN: - case IMAP_AUTHENTICATE_LOGIN: - case IMAP_AUTHENTICATE_LOGIN_PASSWD: - case IMAP_AUTHENTICATE_CRAMMD5: - case IMAP_AUTHENTICATE_DIGESTMD5: - case IMAP_AUTHENTICATE_DIGESTMD5_RESP: - case IMAP_AUTHENTICATE_NTLM: - case IMAP_AUTHENTICATE_NTLM_TYPE2MSG: - case IMAP_AUTHENTICATE_XOAUTH2: - case IMAP_AUTHENTICATE_FINAL: + case IMAP_AUTHENTICATE: case IMAP_APPEND: *resp = '+'; break; @@ -426,20 +427,7 @@ static void state(struct connectdata *conn, imapstate newstate) "CAPABILITY", "STARTTLS", "UPGRADETLS", - "AUTHENTICATE_PLAIN", - "AUTHENTICATE_LOGIN", - "AUTHENTICATE_LOGIN_PASSWD", - "AUTHENTICATE_CRAMMD5", - "AUTHENTICATE_DIGESTMD5", - "AUTHENTICATE_DIGESTMD5_RESP", - "AUTHENTICATE_NTLM", - "AUTHENTICATE_NTLM_TYPE2MSG", - "AUTHENTICATE_GSSAPI", - "AUTHENTICATE_GSSAPI_TOKEN", - "AUTHENTICATE_GSSAPI_NO_DATA", - "AUTHENTICATE_XOAUTH2", - "AUTHENTICATE_CANCEL", - "AUTHENTICATE_FINAL", + "AUTHENTICATE", "LOGIN", "LIST", "SELECT", @@ -472,9 +460,9 @@ static CURLcode imap_perform_capability(struct connectdata *conn) CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; - imapc->authmechs = 0; /* No known authentication mechanisms yet */ - imapc->authused = 0; /* Clear the authentication mechanism used */ - imapc->tls_supported = FALSE; /* Clear the TLS capability */ + imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ + imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ + imapc->tls_supported = FALSE; /* Clear the TLS capability */ /* Send the CAPABILITY command */ result = imap_sendf(conn, "CAPABILITY"); @@ -559,8 +547,8 @@ static CURLcode imap_perform_login(struct connectdata *conn) result = imap_sendf(conn, "LOGIN %s %s", user ? user : "", passwd ? passwd : ""); - Curl_safefree(user); - Curl_safefree(passwd); + free(user); + free(passwd); if(!result) state(conn, IMAP_LOGIN); @@ -577,24 +565,17 @@ static CURLcode imap_perform_login(struct connectdata *conn) */ static CURLcode imap_perform_authenticate(struct connectdata *conn, const char *mech, - const char *initresp, - imapstate state1, imapstate state2) + const char *initresp) { CURLcode result = CURLE_OK; if(initresp) { /* Send the AUTHENTICATE command with the initial response */ result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp); - - if(!result) - state(conn, state2); } else { /* Send the AUTHENTICATE command */ result = imap_sendf(conn, "AUTHENTICATE %s", mech); - - if(!result) - state(conn, state1); } return result; @@ -602,6 +583,20 @@ static CURLcode imap_perform_authenticate(struct connectdata *conn, /*********************************************************************** * + * imap_continue_authenticate() + * + * Sends SASL continuation data or cancellation. + */ +static CURLcode imap_continue_authenticate(struct connectdata *conn, + const char *resp) +{ + struct imap_conn *imapc = &conn->proto.imapc; + + return Curl_pp_sendf(&imapc->pp, "%s", resp); +} + +/*********************************************************************** + * * imap_perform_authentication() * * Initiates the authentication sequence, with the appropriate SASL @@ -612,33 +607,22 @@ static CURLcode imap_perform_authentication(struct connectdata *conn) { CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; - const char *mech = NULL; - char *initresp = NULL; - size_t len = 0; - imapstate state1 = IMAP_STOP; - imapstate state2 = IMAP_STOP; + saslprogress progress; - /* Check we have a username and password to authenticate with and end the + /* Check we have enough data to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) { state(conn, IMAP_STOP); - return result; } /* Calculate the SASL login details */ - result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1, - &state2); + result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress); if(!result) { - if(mech && (imapc->preftype & IMAP_TYPE_SASL)) { - /* Perform SASL based authentication */ - result = imap_perform_authenticate(conn, mech, initresp, state1, state2); - - Curl_safefree(initresp); - } - else if((!imapc->login_disabled) && - (imapc->preftype & IMAP_TYPE_CLEARTEXT)) + if(progress == SASL_INPROGRESS) + state(conn, IMAP_AUTHENTICATE); + else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) /* Perform clear text authentication */ result = imap_perform_login(conn); else { @@ -677,7 +661,7 @@ static CURLcode imap_perform_list(struct connectdata *conn) /* Send the LIST command */ result = imap_sendf(conn, "LIST \"%s\" *", mailbox); - Curl_safefree(mailbox); + free(mailbox); } if(!result) @@ -718,7 +702,7 @@ static CURLcode imap_perform_select(struct connectdata *conn) /* Send the SELECT command */ result = imap_sendf(conn, "SELECT %s", mailbox); - Curl_safefree(mailbox); + free(mailbox); if(!result) state(conn, IMAP_SELECT); @@ -793,7 +777,7 @@ static CURLcode imap_perform_append(struct connectdata *conn) result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", mailbox, conn->data->state.infilesize); - Curl_safefree(mailbox); + free(mailbox); if(!result) state(conn, IMAP_APPEND); @@ -915,26 +899,16 @@ static CURLcode imap_state_capability_resp(struct connectdata *conn, /* Do we have a SASL based authentication mechanism? */ else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { + size_t llen; + unsigned int mechbit; + line += 5; wordlen -= 5; /* Test the word for a matching authentication mechanism */ - if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) - imapc->authmechs |= SASL_MECH_LOGIN; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) - imapc->authmechs |= SASL_MECH_PLAIN; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) - imapc->authmechs |= SASL_MECH_CRAM_MD5; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) - imapc->authmechs |= SASL_MECH_DIGEST_MD5; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) - imapc->authmechs |= SASL_MECH_GSSAPI; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) - imapc->authmechs |= SASL_MECH_EXTERNAL; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) - imapc->authmechs |= SASL_MECH_NTLM; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) - imapc->authmechs |= SASL_MECH_XOAUTH2; + if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) && + llen == wordlen) + imapc->sasl.authmechs |= mechbit; } line += wordlen; @@ -987,569 +961,36 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn, return result; } -/* For AUTHENTICATE PLAIN (without initial response) responses */ -static CURLcode imap_state_auth_plain_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *plainauth = NULL; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied. %c", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the authorisation message */ - result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, - &plainauth, &len); - if(!result && plainauth) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); - } - } - - Curl_safefree(plainauth); - - return result; -} - -/* For AUTHENTICATE LOGIN (without initial response) responses */ -static CURLcode imap_state_auth_login_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *authuser = NULL; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the user message */ - result = Curl_sasl_create_login_message(data, conn->user, - &authuser, &len); - if(!result && authuser) { - /* Send the user */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser); - - if(!result) - state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD); - } - } - - Curl_safefree(authuser); - - return result; -} - -/* For AUTHENTICATE LOGIN user entry responses */ -static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *authpasswd = NULL; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the password message */ - result = Curl_sasl_create_login_message(data, conn->passwd, - &authpasswd, &len); - if(!result && authpasswd) { - /* Send the password */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); - } - } - - Curl_safefree(authpasswd); - - return result; -} - -#ifndef CURL_DISABLE_CRYPTO_AUTH -/* For AUTHENTICATE CRAM-MD5 responses */ -static CURLcode imap_state_auth_cram_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlg = NULL; - char *chlg64 = NULL; - char *rplyb64 = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - return CURLE_LOGIN_DENIED; - } - - /* Get the challenge message */ - imap_get_message(data->state.buffer, &chlg64); - - /* Decode the challenge message */ - result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len); - if(result) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); - - if(!result) - state(conn, IMAP_AUTHENTICATE_CANCEL); - } - else { - /* Create the response message */ - result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user, - conn->passwd, &rplyb64, &len); - if(!result && rplyb64) { - /* Send the response */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); - } - } - - Curl_safefree(chlg); - Curl_safefree(rplyb64); - - return result; -} - -/* For AUTHENTICATE DIGEST-MD5 challenge responses */ -static CURLcode imap_state_auth_digest_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlg64 = NULL; - char *rplyb64 = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - return CURLE_LOGIN_DENIED; - } - - /* Get the challenge message */ - imap_get_message(data->state.buffer, &chlg64); - - /* Create the response message */ - result = Curl_sasl_create_digest_md5_message(data, chlg64, - conn->user, conn->passwd, - "imap", &rplyb64, &len); - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); - - if(!result) - state(conn, IMAP_AUTHENTICATE_CANCEL); - } - } - else { - /* Send the response */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64); - - if(!result) - state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP); - } - - Curl_safefree(rplyb64); - - return result; -} - -/* For AUTHENTICATE DIGEST-MD5 challenge-response responses */ -static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Authentication failed: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", ""); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); - } - - return result; -} -#endif - -#ifdef USE_NTLM -/* For AUTHENTICATE NTLM (without initial response) responses */ -static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *type1msg = NULL; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the type-1 message */ - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - &type1msg, &len); - if(!result && type1msg) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg); - - if(!result) - state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG); - } - } - - Curl_safefree(type1msg); - - return result; -} - -/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ -static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *type2msg = NULL; - char *type3msg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - imap_get_message(data->state.buffer, &type2msg); - - /* Decode the type-2 message */ - result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); - if(result) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); - - if(!result) - state(conn, IMAP_AUTHENTICATE_CANCEL); - } - else { - /* Create the type-3 message */ - result = Curl_sasl_create_ntlm_type3_message(data, conn->user, - conn->passwd, &conn->ntlm, - &type3msg, &len); - if(!result && type3msg) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); - } - } - } - - Curl_safefree(type3msg); - - return result; -} -#endif - -#if defined(USE_WINDOWS_SSPI) -/* For AUTHENTICATE GSSAPI (without initial response) responses */ -static CURLcode imap_state_auth_gssapi_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct imap_conn *imapc = &conn->proto.imapc; - size_t len = 0; - char *respmsg = NULL; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the initial response message */ - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, "imap", - imapc->mutual_auth, - NULL, &conn->krb5, - &respmsg, &len); - if(!result && respmsg) { - /* Send the message */ - result = Curl_pp_sendf(&imapc->pp, "%s", respmsg); - - if(!result) - state(conn, IMAP_AUTHENTICATE_GSSAPI_TOKEN); - } - } - - Curl_safefree(respmsg); - - return result; -} - -/* For AUTHENTICATE GSSAPI user token responses */ -static CURLcode imap_state_auth_gssapi_token_resp(struct connectdata *conn, - int imapcode, - imapstate instate) +/* For SASL authentication responses */ +static CURLcode imap_state_auth_resp(struct connectdata *conn, + int imapcode, + imapstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; struct imap_conn *imapc = &conn->proto.imapc; - char *chlgmsg = NULL; - char *respmsg = NULL; - size_t len = 0; + saslprogress progress; (void)instate; /* no use for this yet */ - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - imap_get_message(data->state.buffer, &chlgmsg); - - if(imapc->mutual_auth) - /* Decode the user token challenge and create the optional response - message */ - result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL, - imapc->mutual_auth, - chlgmsg, &conn->krb5, - &respmsg, &len); - else - /* Decode the security challenge and create the response message */ - result = Curl_sasl_create_gssapi_security_message(data, chlgmsg, - &conn->krb5, - &respmsg, &len); - - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&imapc->pp, "%s", "*"); - - if(!result) - state(conn, IMAP_AUTHENTICATE_CANCEL); - } - } - else { - /* Send the response */ - if(respmsg) - result = Curl_pp_sendf(&imapc->pp, "%s", respmsg); - else - result = Curl_pp_sendf(&imapc->pp, "%s", ""); - - if(!result) - state(conn, (imapc->mutual_auth ? IMAP_AUTHENTICATE_GSSAPI_NO_DATA : - IMAP_AUTHENTICATE_FINAL)); - } - } - - Curl_safefree(respmsg); - - return result; -} - -/* For AUTHENTICATE GSSAPI no data responses */ -static CURLcode imap_state_auth_gssapi_no_data_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlgmsg = NULL; - char *respmsg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - imap_get_message(data->state.buffer, &chlgmsg); - - /* Decode the security challenge and create the response message */ - result = Curl_sasl_create_gssapi_security_message(data, chlgmsg, - &conn->krb5, - &respmsg, &len); - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); - - if(!result) - state(conn, IMAP_AUTHENTICATE_CANCEL); - } - } - else { - /* Send the response */ - if(respmsg) { - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", respmsg); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); + result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + state(conn, IMAP_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ + if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) + /* Perform clear text authentication */ + result = imap_perform_login(conn); + else { + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; } + break; + default: + break; } - } - - Curl_safefree(respmsg); - - return result; -} -#endif - -/* For AUTHENTICATE XOAUTH2 (without initial response) responses */ -static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *xoauth = NULL; - - (void)instate; /* no use for this yet */ - - if(imapcode != '+') { - failf(data, "Access denied: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the authorisation message */ - result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, - conn->xoauth2_bearer, - &xoauth, &len); - if(!result && xoauth) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth); - - if(!result) - state(conn, IMAP_AUTHENTICATE_FINAL); - } - } - - Curl_safefree(xoauth); - - return result; -} - -/* For AUTHENTICATE cancellation responses */ -static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct imap_conn *imapc = &conn->proto.imapc; - const char *mech = NULL; - char *initresp = NULL; - size_t len = 0; - imapstate state1 = IMAP_STOP; - imapstate state2 = IMAP_STOP; - - (void)imapcode; - (void)instate; /* no use for this yet */ - - /* Remove the offending mechanism from the supported list */ - imapc->authmechs ^= imapc->authused; - - /* Calculate alternative SASL login details */ - result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1, - &state2); - - if(!result) { - /* Do we have any mechanisms left or can we fallback to clear text? */ - if(mech) { - /* Retry SASL based authentication */ - result = imap_perform_authenticate(conn, mech, initresp, state1, state2); - - Curl_safefree(initresp); - } - else if((!imapc->login_disabled) && - (imapc->preftype & IMAP_TYPE_CLEARTEXT)) - /* Perform clear text authentication */ - result = imap_perform_login(conn); - else { - failf(data, "Authentication cancelled"); - - result = CURLE_LOGIN_DENIED; - } - } - - return result; -} - -/* For final responses in the AUTHENTICATE sequence */ -static CURLcode imap_state_auth_final_resp(struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - (void)instate; /* no use for this yet */ - - if(imapcode != 'O') { - failf(data, "Authentication failed: %d", imapcode); - result = CURLE_LOGIN_DENIED; - } - else - /* End of connect phase */ - state(conn, IMAP_STOP); return result; } @@ -1873,69 +1314,8 @@ static CURLcode imap_statemach_act(struct connectdata *conn) result = imap_state_starttls_resp(conn, imapcode, imapc->state); break; - case IMAP_AUTHENTICATE_PLAIN: - result = imap_state_auth_plain_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_LOGIN: - result = imap_state_auth_login_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_LOGIN_PASSWD: - result = imap_state_auth_login_password_resp(conn, imapcode, - imapc->state); - break; - -#ifndef CURL_DISABLE_CRYPTO_AUTH - case IMAP_AUTHENTICATE_CRAMMD5: - result = imap_state_auth_cram_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_DIGESTMD5: - result = imap_state_auth_digest_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_DIGESTMD5_RESP: - result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state); - break; -#endif - -#ifdef USE_NTLM - case IMAP_AUTHENTICATE_NTLM: - result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_NTLM_TYPE2MSG: - result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode, - imapc->state); - break; -#endif - -#if defined(USE_WINDOWS_SSPI) - case IMAP_AUTHENTICATE_GSSAPI: - result = imap_state_auth_gssapi_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_GSSAPI_TOKEN: - result = imap_state_auth_gssapi_token_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_GSSAPI_NO_DATA: - result = imap_state_auth_gssapi_no_data_resp(conn, imapcode, - imapc->state); - break; -#endif - - case IMAP_AUTHENTICATE_XOAUTH2: - result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_CANCEL: - result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE_FINAL: - result = imap_state_auth_final_resp(conn, imapcode, imapc->state); + case IMAP_AUTHENTICATE: + result = imap_state_auth_resp(conn, imapcode, imapc->state); break; case IMAP_LOGIN: @@ -2062,7 +1442,7 @@ static CURLcode imap_connect(struct connectdata *conn, bool *done) /* Set the default preferred authentication type and mechanism */ imapc->preftype = IMAP_TYPE_ANY; - imapc->prefmech = SASL_AUTH_ANY; + Curl_sasl_init(&imapc->sasl, &saslimap); /* Initialise the pingpong layer */ Curl_pp_init(pp); @@ -2275,7 +1655,7 @@ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection) Curl_pp_disconnect(&imapc->pp); /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, imapc->authused); + Curl_sasl_cleanup(conn, imapc->sasl.authused); /* Cleanup our connection based variables */ Curl_safefree(imapc->mailbox); @@ -2420,7 +1800,7 @@ static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...) result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap); va_end(ap); - Curl_safefree(taggedfmt); + free(taggedfmt); return result; } @@ -2448,7 +1828,7 @@ static char *imap_atom(const char *str) if(!str) return NULL; - /* Count any unescapped characters */ + /* Count any unescaped characters */ p1 = str; while(*p1) { if(*p1 == '\\') @@ -2461,7 +1841,7 @@ static char *imap_atom(const char *str) p1++; } - /* Does the input contain any unescapped characters? */ + /* Does the input contain any unescaped characters? */ if(!backsp_count && !quote_count && !space_exists) return strdup(str); @@ -2549,69 +1929,42 @@ static CURLcode imap_parse_url_options(struct connectdata *conn) { CURLcode result = CURLE_OK; struct imap_conn *imapc = &conn->proto.imapc; - const char *options = conn->options; - const char *ptr = options; - bool reset = TRUE; + const char *ptr = conn->options; + + imapc->sasl.resetprefs = TRUE; - while(ptr && *ptr) { + while(!result && ptr && *ptr) { const char *key = ptr; + const char *value; while(*ptr && *ptr != '=') ptr++; - if(strnequal(key, "AUTH", 4)) { - size_t len = 0; - const char *value = ++ptr; + value = ptr + 1; - if(reset) { - reset = FALSE; - imapc->preftype = IMAP_TYPE_NONE; - imapc->prefmech = SASL_AUTH_NONE; - } - - while(*ptr && *ptr != ';') { - ptr++; - len++; - } - - if(strnequal(value, "*", len)) { - imapc->preftype = IMAP_TYPE_ANY; - imapc->prefmech = SASL_AUTH_ANY; - } - else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_LOGIN; - } - else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_PLAIN; - } - else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_CRAM_MD5; - } - else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_DIGEST_MD5; - } - else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_GSSAPI; - } - else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_NTLM; - } - else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) { - imapc->preftype = IMAP_TYPE_SASL; - imapc->prefmech |= SASL_MECH_XOAUTH2; - } + while(*ptr && *ptr != ';') + ptr++; - if(*ptr == ';') - ptr++; - } + if(strnequal(key, "AUTH=", 5)) + result = Curl_sasl_parse_url_auth_option(&imapc->sasl, + value, ptr - value); else result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + switch(imapc->sasl.prefmech) { + case SASL_AUTH_NONE: + imapc->preftype = IMAP_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + imapc->preftype = IMAP_TYPE_ANY; + break; + default: + imapc->preftype = IMAP_TYPE_SASL; + break; } return result; @@ -2678,7 +2031,7 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) /* Decode the value parameter */ result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE); if(result) { - Curl_safefree(name); + free(name); return result; } @@ -2717,14 +2070,14 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) value = NULL; } else { - Curl_safefree(name); - Curl_safefree(value); + free(name); + free(value); return CURLE_URL_MALFORMAT; } - Curl_safefree(name); - Curl_safefree(value); + free(name); + free(value); } /* Does the URL contain a query parameter? Only valid when we have a mailbox @@ -2786,108 +2139,4 @@ static CURLcode imap_parse_custom_request(struct connectdata *conn) return result; } -/*********************************************************************** - * - * imap_calc_sasl_details() - * - * Calculate the required login details for SASL authentication. - */ -static CURLcode imap_calc_sasl_details(struct connectdata *conn, - const char **mech, - char **initresp, size_t *len, - imapstate *state1, imapstate *state2) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct imap_conn *imapc = &conn->proto.imapc; - - /* Calculate the supported authentication mechanism, by decreasing order of - security, as well as the initial response where appropriate */ -#if defined(USE_WINDOWS_SSPI) - if((imapc->authmechs & SASL_MECH_GSSAPI) && - (imapc->prefmech & SASL_MECH_GSSAPI)) { - imapc->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ - - *mech = SASL_MECH_STRING_GSSAPI; - *state1 = IMAP_AUTHENTICATE_GSSAPI; - *state2 = IMAP_AUTHENTICATE_GSSAPI_TOKEN; - imapc->authused = SASL_MECH_GSSAPI; - - if(imapc->ir_supported || data->set.sasl_ir) - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, "imap", - imapc->mutual_auth, - NULL, &conn->krb5, - initresp, len); - } - else -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if((imapc->authmechs & SASL_MECH_DIGEST_MD5) && - (imapc->prefmech & SASL_MECH_DIGEST_MD5)) { - *mech = SASL_MECH_STRING_DIGEST_MD5; - *state1 = IMAP_AUTHENTICATE_DIGESTMD5; - imapc->authused = SASL_MECH_DIGEST_MD5; - } - else if((imapc->authmechs & SASL_MECH_CRAM_MD5) && - (imapc->prefmech & SASL_MECH_CRAM_MD5)) { - *mech = SASL_MECH_STRING_CRAM_MD5; - *state1 = IMAP_AUTHENTICATE_CRAMMD5; - imapc->authused = SASL_MECH_CRAM_MD5; - } - else -#endif -#ifdef USE_NTLM - if((imapc->authmechs & SASL_MECH_NTLM) && - (imapc->prefmech & SASL_MECH_NTLM)) { - *mech = SASL_MECH_STRING_NTLM; - *state1 = IMAP_AUTHENTICATE_NTLM; - *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG; - imapc->authused = SASL_MECH_NTLM; - - if(imapc->ir_supported || data->set.sasl_ir) - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - initresp, len); - } - else -#endif - if(((imapc->authmechs & SASL_MECH_XOAUTH2) && - (imapc->prefmech & SASL_MECH_XOAUTH2) && - (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { - *mech = SASL_MECH_STRING_XOAUTH2; - *state1 = IMAP_AUTHENTICATE_XOAUTH2; - *state2 = IMAP_AUTHENTICATE_FINAL; - imapc->authused = SASL_MECH_XOAUTH2; - - if(imapc->ir_supported || data->set.sasl_ir) - result = Curl_sasl_create_xoauth2_message(data, conn->user, - conn->xoauth2_bearer, - initresp, len); - } - else if((imapc->authmechs & SASL_MECH_LOGIN) && - (imapc->prefmech & SASL_MECH_LOGIN)) { - *mech = SASL_MECH_STRING_LOGIN; - *state1 = IMAP_AUTHENTICATE_LOGIN; - *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD; - imapc->authused = SASL_MECH_LOGIN; - - if(imapc->ir_supported || data->set.sasl_ir) - result = Curl_sasl_create_login_message(data, conn->user, initresp, len); - } - else if((imapc->authmechs & SASL_MECH_PLAIN) && - (imapc->prefmech & SASL_MECH_PLAIN)) { - *mech = SASL_MECH_STRING_PLAIN; - *state1 = IMAP_AUTHENTICATE_PLAIN; - *state2 = IMAP_AUTHENTICATE_FINAL; - imapc->authused = SASL_MECH_PLAIN; - - if(imapc->ir_supported || data->set.sasl_ir) - result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, - initresp, len); - } - - return result; -} - #endif /* CURL_DISABLE_IMAP */ @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2009 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,6 +23,7 @@ ***************************************************************************/ #include "pingpong.h" +#include "curl_sasl.h" /**************************************************************************** * IMAP unique setup @@ -35,20 +36,7 @@ typedef enum { IMAP_STARTTLS, IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS (multi mode only) */ - IMAP_AUTHENTICATE_PLAIN, - IMAP_AUTHENTICATE_LOGIN, - IMAP_AUTHENTICATE_LOGIN_PASSWD, - IMAP_AUTHENTICATE_CRAMMD5, - IMAP_AUTHENTICATE_DIGESTMD5, - IMAP_AUTHENTICATE_DIGESTMD5_RESP, - IMAP_AUTHENTICATE_NTLM, - IMAP_AUTHENTICATE_NTLM_TYPE2MSG, - IMAP_AUTHENTICATE_GSSAPI, - IMAP_AUTHENTICATE_GSSAPI_TOKEN, - IMAP_AUTHENTICATE_GSSAPI_NO_DATA, - IMAP_AUTHENTICATE_XOAUTH2, - IMAP_AUTHENTICATE_CANCEL, - IMAP_AUTHENTICATE_FINAL, + IMAP_AUTHENTICATE, IMAP_LOGIN, IMAP_LIST, IMAP_SELECT, @@ -83,16 +71,13 @@ struct imap_conn { struct pingpong pp; imapstate state; /* Always use imap.c:state() to change state! */ bool ssldone; /* Is connect() over SSL done? */ - unsigned int authmechs; /* Accepted authentication mechanisms */ + struct SASL sasl; /* SASL-related parameters */ unsigned int preftype; /* Preferred authentication type */ - unsigned int prefmech; /* Preferred authentication mechanism */ - unsigned int authused; /* Auth mechanism used for the connection */ int cmdid; /* Last used command ID */ char resptag[5]; /* Response tag to wait for */ bool tls_supported; /* StartTLS capability supported by server */ bool login_disabled; /* LOGIN command disabled by server */ bool ir_supported; /* Initial response supported by server */ - bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ char *mailbox; /* The last selected mailbox */ char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ }; diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c index c327150..da9a3ab 100644 --- a/lib/inet_ntop.c +++ b/lib/inet_ntop.c @@ -32,8 +32,7 @@ #include <arpa/inet.h> #endif -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "inet_ntop.h" diff --git a/lib/inet_ntop.h b/lib/inet_ntop.h index db28ed8..cc4bdbb 100644 --- a/lib/inet_ntop.h +++ b/lib/inet_ntop.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,7 +31,7 @@ char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); #include <arpa/inet.h> #endif #define Curl_inet_ntop(af,addr,buf,size) \ - inet_ntop(af,addr,buf,(curl_socklen_t)size) + inet_ntop(af, addr, buf, (curl_socklen_t)size) #endif #endif /* HEADER_CURL_INET_NTOP_H */ @@ -1,8 +1,8 @@ /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c * - * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2013 Kungliga Tekniska Högskolan + * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (c) 2004 - 2012 Daniel Stenberg + * Copyright (c) 2004 - 2015 Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,13 +34,7 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_FTP -#ifdef HAVE_GSSAPI - -#ifdef HAVE_OLD_GSSMIT -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#define NCOMPAT 1 -#endif +#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP) #ifdef HAVE_NETDB_H #include <netdb.h> @@ -52,13 +46,11 @@ #include "curl_gssapi.h" #include "sendf.h" #include "curl_sec.h" -#include "curl_memory.h" #include "warnless.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #define LOCAL_ADDR (&conn->local_addr) @@ -121,8 +113,7 @@ krb5_overhead(void *app_data, int level, int len) } static int -krb5_encode(void *app_data, const void *from, int length, int level, void **to, - struct connectdata *conn UNUSED_PARAM) +krb5_encode(void *app_data, const void *from, int length, int level, void **to) { gss_ctx_id_t *context = app_data; gss_buffer_desc dec, enc; @@ -130,9 +121,6 @@ krb5_encode(void *app_data, const void *from, int length, int level, void **to, int state; int len; - /* shut gcc up */ - conn = NULL; - /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal * libraries modify the input buffer in gss_seal() */ @@ -240,6 +228,7 @@ krb5_auth(void *app_data, struct connectdata *conn) &chan, gssresp, &output_buffer, + TRUE, NULL); if(gssresp) { @@ -257,7 +246,8 @@ krb5_auth(void *app_data, struct connectdata *conn) result = Curl_base64_encode(data, (char *)output_buffer.value, output_buffer.length, &p, &base64_sz); if(result) { - Curl_infof(data,"base64-encoding: %s\n", curl_easy_strerror(result)); + Curl_infof(data, "base64-encoding: %s\n", + curl_easy_strerror(result)); ret = AUTH_CONTINUE; break; } @@ -289,7 +279,8 @@ krb5_auth(void *app_data, struct connectdata *conn) (unsigned char **)&_gssresp.value, &_gssresp.length); if(result) { - Curl_failf(data,"base64-decoding: %s", curl_easy_strerror(result)); + Curl_failf(data, "base64-decoding: %s", + curl_easy_strerror(result)); ret = AUTH_CONTINUE; break; } @@ -338,5 +329,4 @@ struct Curl_sec_client_mech Curl_krb5_client_mech = { krb5_decode }; -#endif /* HAVE_GSSAPI */ -#endif /* CURL_DISABLE_FTP */ +#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */ @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,7 +35,7 @@ * OpenLDAP library versions, USE_OPENLDAP shall not be defined. */ -#ifdef CURL_LDAP_WIN /* Use Windows LDAP implementation. */ +#ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ # include <winldap.h> # ifndef LDAP_VENDOR_NAME # error Your Platform SDK is NOT sufficient for LDAP support! \ @@ -54,6 +54,15 @@ # endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ #endif +/* These are macros in both <wincrypt.h> (in above <winldap.h>) and typedefs + * in BoringSSL's <openssl/x509.h> + */ +#ifdef HAVE_BORINGSSL +# undef X509_NAME +# undef X509_CERT_PAIR +# undef X509_EXTENSIONS +#endif + #include "urldata.h" #include <curl/curl.h> #include "sendf.h" @@ -63,14 +72,14 @@ #include "strequal.h" #include "strtok.h" #include "curl_ldap.h" -#include "curl_memory.h" +#include "curl_multibyte.h" #include "curl_base64.h" #include "rawstr.h" #include "connect.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #ifndef HAVE_LDAP_URL_PARSE @@ -80,10 +89,19 @@ typedef struct { char *lud_host; int lud_port; +#if defined(USE_WIN32_LDAP) + TCHAR *lud_dn; + TCHAR **lud_attrs; +#else char *lud_dn; char **lud_attrs; +#endif int lud_scope; +#if defined(USE_WIN32_LDAP) + TCHAR *lud_filter; +#else char *lud_filter; +#endif char **lud_exts; size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the "real" struct so can only be used in code @@ -168,11 +186,11 @@ const struct Curl_handler Curl_handler_ldaps = { static CURLcode Curl_ldap(struct connectdata *conn, bool *done) { - CURLcode status = CURLE_OK; + CURLcode result = CURLE_OK; int rc = 0; LDAP *server = NULL; LDAPURLDesc *ludp = NULL; - LDAPMessage *result = NULL; + LDAPMessage *ldapmsg = NULL; LDAPMessage *entryIterator; int num = 0; struct SessionHandle *data=conn->data; @@ -182,7 +200,16 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) size_t val_b64_sz = 0; curl_off_t dlsize = 0; #ifdef LDAP_OPT_NETWORK_TIMEOUT - struct timeval ldap_timeout = {10,0}; /* 10 sec connection/search timeout */ + struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ +#endif +#if defined(USE_WIN32_LDAP) + TCHAR *host = NULL; + TCHAR *user = NULL; + TCHAR *passwd = NULL; +#else + char *host = NULL; + char *user = NULL; + char *passwd = NULL; #endif *done = TRUE; /* unconditionally */ @@ -197,7 +224,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) #endif if(rc != 0) { failf(data, "LDAP local: %s", ldap_err2string(rc)); - status = CURLE_LDAP_INVALID_URL; + result = CURLE_LDAP_INVALID_URL; goto quit; } @@ -207,6 +234,32 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) infof(data, "LDAP local: trying to establish %s connection\n", ldap_ssl ? "encrypted" : "cleartext"); +#if defined(USE_WIN32_LDAP) + host = Curl_convert_UTF8_to_tchar(conn->host.name); + if(!host) { + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } + + if(conn->bits.user_passwd) { + user = Curl_convert_UTF8_to_tchar(conn->user); + passwd = Curl_convert_UTF8_to_tchar(conn->passwd); + if(!user || !passwd) { + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } + } +#else + host = conn->host.name; + + if(conn->bits.user_passwd) { + user = conn->user; + passwd = conn->passwd; + } +#endif + #ifdef LDAP_OPT_NETWORK_TIMEOUT ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout); #endif @@ -214,9 +267,9 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(ldap_ssl) { #ifdef HAVE_LDAP_SSL -#ifdef CURL_LDAP_WIN +#ifdef USE_WIN32_LDAP /* Win32 LDAP SDK doesn't support insecure mode without CA! */ - server = ldap_sslinit(conn->host.name, (int)conn->port, 1); + server = ldap_sslinit(host, (int)conn->port, 1); ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); #else int ldap_option; @@ -225,7 +278,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) rc = ldapssl_client_init(NULL, NULL); if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } if(data->set.ssl.verifypeer) { @@ -237,7 +290,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(!ldap_ca) { failf(data, "LDAP local: ERROR %s CA cert not set!", (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM")); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } infof(data, "LDAP local: using %s CA cert '%s'\n", @@ -248,7 +301,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) failf(data, "LDAP local: ERROR setting %s CA cert: %s", (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"), ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } ldap_option = LDAPSSL_VERIFY_SERVER; @@ -259,14 +312,14 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting cert verify mode: %s", ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } - server = ldapssl_init(conn->host.name, (int)conn->port, 1); + server = ldapssl_init(host, (int)conn->port, 1); if(server == NULL) { failf(data, "LDAP local: Cannot connect to %s:%ld", - conn->host.name, conn->port); - status = CURLE_COULDNT_CONNECT; + conn->host.dispname, conn->port); + result = CURLE_COULDNT_CONNECT; goto quit; } #elif defined(LDAP_OPT_X_TLS) @@ -275,12 +328,12 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if((data->set.str[STRING_CERT_TYPE]) && (!Curl_raw_equal(data->set.str[STRING_CERT_TYPE], "PEM"))) { failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type!"); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } if(!ldap_ca) { failf(data, "LDAP local: ERROR PEM CA cert not set!"); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca); @@ -288,7 +341,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting PEM CA cert: %s", ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } ldap_option = LDAP_OPT_X_TLS_DEMAND; @@ -300,14 +353,14 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting cert verify mode: %s", ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } - server = ldap_init(conn->host.name, (int)conn->port); + server = ldap_init(host, (int)conn->port); if(server == NULL) { failf(data, "LDAP local: Cannot connect to %s:%ld", - conn->host.name, conn->port); - status = CURLE_COULDNT_CONNECT; + conn->host.dispname, conn->port); + result = CURLE_COULDNT_CONNECT; goto quit; } ldap_option = LDAP_OPT_X_TLS_HARD; @@ -315,7 +368,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s", ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } /* @@ -323,7 +376,7 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s", ldap_err2string(rc)); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; } */ @@ -332,126 +385,278 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done) should check in first place if we can support LDAP SSL/TLS */ failf(data, "LDAP local: SSL/TLS not supported with this version " "of the OpenLDAP toolkit\n"); - status = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto quit; #endif #endif #endif /* CURL_LDAP_USE_SSL */ } else { - server = ldap_init(conn->host.name, (int)conn->port); + server = ldap_init(host, (int)conn->port); if(server == NULL) { failf(data, "LDAP local: Cannot connect to %s:%ld", - conn->host.name, conn->port); - status = CURLE_COULDNT_CONNECT; + conn->host.dispname, conn->port); + result = CURLE_COULDNT_CONNECT; goto quit; } } -#ifdef CURL_LDAP_WIN +#ifdef USE_WIN32_LDAP ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); #endif - rc = ldap_simple_bind_s(server, - conn->bits.user_passwd ? conn->user : NULL, - conn->bits.user_passwd ? conn->passwd : NULL); + rc = ldap_simple_bind_s(server, user, passwd); if(!ldap_ssl && rc != 0) { ldap_proto = LDAP_VERSION2; ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); - rc = ldap_simple_bind_s(server, - conn->bits.user_passwd ? conn->user : NULL, - conn->bits.user_passwd ? conn->passwd : NULL); + rc = ldap_simple_bind_s(server, user, passwd); } if(rc != 0) { failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc)); - status = CURLE_LDAP_CANNOT_BIND; + result = CURLE_LDAP_CANNOT_BIND; goto quit; } rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope, - ludp->lud_filter, ludp->lud_attrs, 0, &result); + ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); if(rc != 0 && rc != LDAP_SIZELIMIT_EXCEEDED) { failf(data, "LDAP remote: %s", ldap_err2string(rc)); - status = CURLE_LDAP_SEARCH_FAILED; + result = CURLE_LDAP_SEARCH_FAILED; goto quit; } - for(num = 0, entryIterator = ldap_first_entry(server, result); + for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg); entryIterator; entryIterator = ldap_next_entry(server, entryIterator), num++) { BerElement *ber = NULL; +#if defined(USE_WIN32_LDAP) + TCHAR *attribute; +#else char *attribute; /*! suspicious that this isn't 'const' */ - char *dn = ldap_get_dn(server, entryIterator); +#endif int i; - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)dn, 0); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + /* Get the DN and write it to the client */ + { + char *name; + size_t name_len; +#if defined(USE_WIN32_LDAP) + TCHAR *dn = ldap_get_dn(server, entryIterator); + name = Curl_convert_tchar_to_UTF8(dn); + if(!name) { + ldap_memfree(dn); + + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } +#else + char *dn = name = ldap_get_dn(server, entryIterator); +#endif + name_len = strlen(name); + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); + if(result) { +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(name); +#endif + ldap_memfree(dn); + + goto quit; + } + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *) name, + name_len); + if(result) { +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(name); +#endif + ldap_memfree(dn); + + goto quit; + } + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + if(result) { +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(name); +#endif + ldap_memfree(dn); + + goto quit; + } - dlsize += strlen(dn)+5; + dlsize += name_len + 5; +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(name); +#endif + ldap_memfree(dn); + } + + /* Get the attributes and write them to the client */ for(attribute = ldap_first_attribute(server, entryIterator, &ber); attribute; attribute = ldap_next_attribute(server, entryIterator, ber)) { - BerValue **vals = ldap_get_values_len(server, entryIterator, attribute); + BerValue **vals; + size_t attr_len; +#if defined(USE_WIN32_LDAP) + char *attr = Curl_convert_tchar_to_UTF8(attribute); + if(!attr) { + if(ber) + ber_free(ber, 0); + + result = CURLE_OUT_OF_MEMORY; + + goto quit; + } +#else + char *attr = attribute; +#endif + attr_len = strlen(attr); + vals = ldap_get_values_len(server, entryIterator, attribute); if(vals != NULL) { for(i = 0; (vals[i] != NULL); i++) { - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *) attribute, 0); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2); - dlsize += strlen(attribute)+3; - - if((strlen(attribute) > 7) && - (strcmp(";binary", - (char *)attribute + - (strlen((char *)attribute) - 7)) == 0)) { + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1); + if(result) { + ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + result = Curl_client_write(conn, CLIENTWRITE_BODY, + (char *) attr, attr_len); + if(result) { + ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2); + if(result) { + ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + + dlsize += attr_len + 3; + + if((attr_len > 7) && + (strcmp(";binary", (char *) attr + (attr_len - 7)) == 0)) { /* Binary attribute, encode to base64. */ - CURLcode error = Curl_base64_encode(data, - vals[i]->bv_val, - vals[i]->bv_len, - &val_b64, - &val_b64_sz); - if(error) { + result = Curl_base64_encode(data, + vals[i]->bv_val, + vals[i]->bv_len, + &val_b64, + &val_b64_sz); + if(result) { ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif ldap_memfree(attribute); - ldap_memfree(dn); if(ber) ber_free(ber, 0); - status = error; + goto quit; } + if(val_b64_sz > 0) { - Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz); + result = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, + val_b64_sz); free(val_b64); + if(result) { + ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + dlsize += val_b64_sz; } } else { - Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val, - vals[i]->bv_len); + result = Curl_client_write(conn, CLIENTWRITE_BODY, vals[i]->bv_val, + vals[i]->bv_len); + if(result) { + ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + dlsize += vals[i]->bv_len; } - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + if(result) { + ldap_value_free_len(vals); +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + dlsize++; } /* Free memory used to store values */ ldap_value_free_len(vals); } - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + + /* Free the attribute as we are done with it */ +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(attr); +#endif + ldap_memfree(attribute); + + result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + if(result) + goto quit; dlsize++; Curl_pgrsSetDownloadCounter(data, dlsize); - ldap_memfree(attribute); } - ldap_memfree(dn); + if(ber) ber_free(ber, 0); } quit: - if(result) { - ldap_msgfree(result); + if(ldapmsg) { + ldap_msgfree(ldapmsg); LDAP_TRACE (("Received %d entries\n", num)); } if(rc == LDAP_SIZELIMIT_EXCEEDED) @@ -465,11 +670,17 @@ quit: ldapssl_client_deinit(); #endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */ +#if defined(USE_WIN32_LDAP) + Curl_unicodefree(passwd); + Curl_unicodefree(user); + Curl_unicodefree(host); +#endif + /* no data to transfer */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); connclose(conn, "LDAP connection always disable re-use"); - return status; + return result; } #ifdef DEBUG_LDAP @@ -513,57 +724,34 @@ static int str2scope (const char *p) /* * Split 'str' into strings separated by commas. - * Note: res[] points into 'str'. + * Note: out[] points into 'str'. */ -static char **split_str (char *str) +static bool split_str(char *str, char ***out, size_t *count) { - char **res, *lasts, *s; - int i; - - for(i = 2, s = strchr(str,','); s; i++) - s = strchr(++s,','); + char **res; + char *lasts; + char *s; + size_t i; + size_t items = 1; + + s = strchr(str, ','); + while(s) { + items++; + s = strchr(++s, ','); + } - res = calloc(i, sizeof(char*)); + res = calloc(items, sizeof(char *)); if(!res) - return NULL; + return FALSE; - for(i = 0, s = strtok_r(str, ",", &lasts); s; + for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items; s = strtok_r(NULL, ",", &lasts), i++) res[i] = s; - return res; -} - -/* - * Unescape the LDAP-URL components - */ -static bool unescape_elements (void *data, LDAPURLDesc *ludp) -{ - int i; - - if(ludp->lud_filter) { - ludp->lud_filter = curl_easy_unescape(data, ludp->lud_filter, 0, NULL); - if(!ludp->lud_filter) - return FALSE; - } - - for(i = 0; ludp->lud_attrs && ludp->lud_attrs[i]; i++) { - ludp->lud_attrs[i] = curl_easy_unescape(data, ludp->lud_attrs[i], - 0, NULL); - if(!ludp->lud_attrs[i]) - return FALSE; - ludp->lud_attrs_dups++; - } - if(ludp->lud_dn) { - char *dn = ludp->lud_dn; - char *new_dn = curl_easy_unescape(data, dn, 0, NULL); + *out = res; + *count = items; - free(dn); - ludp->lud_dn = new_dn; - if(!new_dn) - return (FALSE); - } - return (TRUE); + return TRUE; } /* @@ -582,8 +770,11 @@ static bool unescape_elements (void *data, LDAPURLDesc *ludp) */ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) { - char *p, *q; - int i; + int rc = LDAP_SUCCESS; + char *path; + char *p; + char *q; + size_t i; if(!conn->data || !conn->data->state.path || @@ -595,74 +786,190 @@ static int _ldap_url_parse2 (const struct connectdata *conn, LDAPURLDesc *ludp) ludp->lud_port = conn->remote_port; ludp->lud_host = conn->host.name; - /* parse DN (Distinguished Name). - */ - ludp->lud_dn = strdup(conn->data->state.path+1); - if(!ludp->lud_dn) + /* Duplicate the path */ + p = path = strdup(conn->data->state.path + 1); + if(!path) return LDAP_NO_MEMORY; - p = strchr(ludp->lud_dn, '?'); - LDAP_TRACE (("DN '%.*s'\n", p ? (size_t)(p-ludp->lud_dn) : - strlen(ludp->lud_dn), ludp->lud_dn)); + /* Parse the DN (Distinguished Name) */ + q = strchr(p, '?'); + if(q) + *q++ = '\0'; + + if(*p) { + char *dn = p; + char *unescaped; - if(!p) - goto success; + LDAP_TRACE (("DN '%s'\n", dn)); - *p++ = '\0'; + /* Unescape the DN */ + unescaped = curl_easy_unescape(conn->data, dn, 0, NULL); + if(!unescaped) { + rc = LDAP_NO_MEMORY; - /* parse attributes. skip "??". - */ + goto quit; + } + +#if defined(USE_WIN32_LDAP) + /* Convert the unescaped string to a tchar */ + ludp->lud_dn = Curl_convert_UTF8_to_tchar(unescaped); + + /* Free the unescaped string as we are done with it */ + Curl_unicodefree(unescaped); + + if(!ludp->lud_dn) { + rc = LDAP_NO_MEMORY; + + goto quit; + } +#else + ludp->lud_dn = unescaped; +#endif + } + + p = q; + if(!p) + goto quit; + + /* Parse the attributes. skip "??" */ q = strchr(p, '?'); if(q) *q++ = '\0'; - if(*p && *p != '?') { - ludp->lud_attrs = split_str(p); - if(!ludp->lud_attrs) - return LDAP_NO_MEMORY; + if(*p) { + char **attributes; + size_t count = 0; + + /* Split the string into an array of attributes */ + if(!split_str(p, &attributes, &count)) { + rc = LDAP_NO_MEMORY; + + goto quit; + } + + /* Allocate our array (+1 for the NULL entry) */ +#if defined(USE_WIN32_LDAP) + ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *)); +#else + ludp->lud_attrs = calloc(count + 1, sizeof(char *)); +#endif + if(!ludp->lud_attrs) { + free(attributes); + + rc = LDAP_NO_MEMORY; + + goto quit; + } + + for(i = 0; i < count; i++) { + char *unescaped; + + LDAP_TRACE (("attr[%d] '%s'\n", i, attributes[i])); + + /* Unescape the attribute */ + unescaped = curl_easy_unescape(conn->data, attributes[i], 0, NULL); + if(!unescaped) { + free(attributes); + + rc = LDAP_NO_MEMORY; + + goto quit; + } + +#if defined(USE_WIN32_LDAP) + /* Convert the unescaped string to a tchar */ + ludp->lud_attrs[i] = Curl_convert_UTF8_to_tchar(unescaped); + + /* Free the unescaped string as we are done with it */ + Curl_unicodefree(unescaped); + + if(!ludp->lud_attrs[i]) { + free(attributes); - for(i = 0; ludp->lud_attrs[i]; i++) - LDAP_TRACE (("attr[%d] '%s'\n", i, ludp->lud_attrs[i])); + rc = LDAP_NO_MEMORY; + + goto quit; + } +#else + ludp->lud_attrs[i] = unescaped; +#endif + + ludp->lud_attrs_dups++; + } + + free(attributes); } p = q; if(!p) - goto success; + goto quit; - /* parse scope. skip "??" - */ + /* Parse the scope. skip "??" */ q = strchr(p, '?'); if(q) *q++ = '\0'; - if(*p && *p != '?') { + if(*p) { ludp->lud_scope = str2scope(p); if(ludp->lud_scope == -1) { - return LDAP_INVALID_SYNTAX; + rc = LDAP_INVALID_SYNTAX; + + goto quit; } LDAP_TRACE (("scope %d\n", ludp->lud_scope)); } p = q; if(!p) - goto success; + goto quit; - /* parse filter - */ + /* Parse the filter */ q = strchr(p, '?'); if(q) *q++ = '\0'; - if(!*p) { - return LDAP_INVALID_SYNTAX; + + if(*p) { + char *filter = p; + char *unescaped; + + LDAP_TRACE (("filter '%s'\n", filter)); + + /* Unescape the filter */ + unescaped = curl_easy_unescape(conn->data, filter, 0, NULL); + if(!unescaped) { + rc = LDAP_NO_MEMORY; + + goto quit; + } + +#if defined(USE_WIN32_LDAP) + /* Convert the unescaped string to a tchar */ + ludp->lud_filter = Curl_convert_UTF8_to_tchar(unescaped); + + /* Free the unescaped string as we are done with it */ + Curl_unicodefree(unescaped); + + if(!ludp->lud_filter) { + rc = LDAP_NO_MEMORY; + + goto quit; + } +#else + ludp->lud_filter = unescaped; +#endif } - ludp->lud_filter = p; - LDAP_TRACE (("filter '%s'\n", ludp->lud_filter)); + p = q; + if(p && !*p) { + rc = LDAP_INVALID_SYNTAX; - success: - if(!unescape_elements(conn->data, ludp)) - return LDAP_NO_MEMORY; - return LDAP_SUCCESS; + goto quit; + } + +quit: + free(path); + + return rc; } static int _ldap_url_parse (const struct connectdata *conn, @@ -691,11 +998,8 @@ static void _ldap_free_urldesc (LDAPURLDesc *ludp) if(!ludp) return; - if(ludp->lud_dn) - free(ludp->lud_dn); - - if(ludp->lud_filter) - free(ludp->lud_filter); + free(ludp->lud_dn); + free(ludp->lud_filter); if(ludp->lud_attrs) { for(i = 0; i < ludp->lud_attrs_dups; i++) @@ -1,282 +1,304 @@ -/*- - Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. - - License to copy and use this software is granted provided that it - is identified as the "RSA Data Security, Inc. MD4 Message-Digest - Algorithm" in all material mentioning or referencing this software - or this function. - - License is also granted to make and use derivative works provided - that such works are identified as "derived from the RSA Data - Security, Inc. MD4 Message-Digest Algorithm" in all material - mentioning or referencing the derived work. - - RSA Data Security, Inc. makes no representations concerning either - the merchantability of this software or the suitability of this - software for any particular purpose. It is provided "as is" - without express or implied warranty of any kind. - - These notices must be retained in any copies of any part of this - documentation and/or software. +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD4 Message-Digest Algorithm (RFC 1320). + * + * Homepage: + http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. In case + * this attempt to disclaim copyright and place the software in the public + * domain is deemed null and void, then the software is Copyright (c) 2001 + * Alexander Peslyak and it is hereby released to the general public under the + * following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. */ #include "curl_setup.h" -/* NSS crypto library does not provide the MD4 hash algorithm, so that we have - * a local implementation of it */ -#ifdef USE_NSS +/* NSS and OS/400 crypto library do not provide the MD4 hash algorithm, so + * that we have a local implementation of it */ +#if defined(USE_NSS) || defined(USE_OS400CRYPTO) #include "curl_md4.h" #include "warnless.h" -typedef unsigned int UINT4; +#ifndef HAVE_OPENSSL -typedef struct MD4Context { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ +#include <string.h> + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD4_u32plus; + +typedef struct { + MD4_u32plus lo, hi; + MD4_u32plus a, b, c, d; + unsigned char buffer[64]; + MD4_u32plus block[16]; } MD4_CTX; -/* Constants for MD4Transform routine. - */ -#define S11 3 -#define S12 7 -#define S13 11 -#define S14 19 -#define S21 3 -#define S22 5 -#define S23 9 -#define S24 13 -#define S31 3 -#define S32 9 -#define S33 11 -#define S34 15 - -static void MD4Transform(UINT4 [4], const unsigned char [64]); -static void Encode(unsigned char *, UINT4 *, unsigned int); -static void Decode(UINT4 *, const unsigned char *, unsigned int); - -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G and H are basic MD4 functions. - */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) +static void MD4_Init(MD4_CTX *ctx); +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); +static void MD4_Final(unsigned char *result, MD4_CTX *ctx); -/* ROTATE_LEFT rotates x left n bits. +/* + * The basic MD4 functions. + * + * F and G are optimized compared to their RFC 1320 definitions, with the + * optimization for F borrowed from Colin Plumb's MD5 implementation. */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ -/* Rotation is separate from addition to prevent recomputation */ -#define FF(a, b, c, d, x, s) { \ - (a) += F ((b), (c), (d)) + (x); \ - (a) = ROTATE_LEFT ((a), (s)); \ - } -#define GG(a, b, c, d, x, s) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \ - (a) = ROTATE_LEFT ((a), (s)); \ - } -#define HH(a, b, c, d, x, s) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \ - (a) = ROTATE_LEFT ((a), (s)); \ - } +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) -/* MD4 initialization. Begins an MD4 operation, writing a new context. +/* + * The MD4 transformation for all three rounds. */ -static void MD4Init(MD4_CTX *context) +#define STEP(f, a, b, c, d, x, s) \ + (a) += f((b), (c), (d)) + (x); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD4_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD4_u32plus)ptr[(n) * 4] | \ + ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD4_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * 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(MD4_CTX *ctx, const void *data, unsigned long size) { - context->count[0] = context->count[1] = 0; - - /* Load magic initialization constants. - */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; + const unsigned char *ptr; + MD4_u32plus a, b, c, d; + MD4_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 3) + STEP(F, d, a, b, c, SET(1), 7) + STEP(F, c, d, a, b, SET(2), 11) + STEP(F, b, c, d, a, SET(3), 19) + STEP(F, a, b, c, d, SET(4), 3) + STEP(F, d, a, b, c, SET(5), 7) + STEP(F, c, d, a, b, SET(6), 11) + STEP(F, b, c, d, a, SET(7), 19) + STEP(F, a, b, c, d, SET(8), 3) + STEP(F, d, a, b, c, SET(9), 7) + STEP(F, c, d, a, b, SET(10), 11) + STEP(F, b, c, d, a, SET(11), 19) + STEP(F, a, b, c, d, SET(12), 3) + STEP(F, d, a, b, c, SET(13), 7) + STEP(F, c, d, a, b, SET(14), 11) + STEP(F, b, c, d, a, SET(15), 19) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3) + STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5) + STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9) + STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13) + STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3) + STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5) + STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9) + STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13) + STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3) + STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5) + STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9) + STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13) + STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3) + STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5) + STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9) + STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3) + STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9) + STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11) + STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15) + STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3) + STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9) + STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11) + STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15) + STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3) + STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9) + STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11) + STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15) + STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3) + STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9) + STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11) + STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while(size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; } -/* MD4 block update operation. Continues an MD4 message-digest - operation, processing another message block, and updating the - context. - */ -static void MD4Update(MD4_CTX *context, const unsigned char *input, - unsigned int inputLen) +static void MD4_Init(MD4_CTX *ctx) { - unsigned int i, bufindex, partLen; - - /* Compute number of bytes mod 64 */ - bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F); - /* Update number of bits */ - if((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - bufindex; - /* Transform as many times as possible. - */ - if(inputLen >= partLen) { - memcpy(&context->buffer[bufindex], input, partLen); - MD4Transform (context->state, context->buffer); - - for(i = partLen; i + 63 < inputLen; i += 64) - MD4Transform (context->state, &input[i]); - - bufindex = 0; - } - else - i = 0; + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; - /* Buffer remaining input */ - memcpy(&context->buffer[bufindex], &input[i], inputLen-i); + ctx->lo = 0; + ctx->hi = 0; } -/* MD4 padding. */ -static void MD4Pad(MD4_CTX *context) +static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) { - unsigned char bits[8]; - unsigned int bufindex, padLen; + MD4_u32plus saved_lo; + unsigned long used, available; - /* Save number of bits */ - Encode (bits, context->count, 8); + saved_lo = ctx->lo; + if((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += (MD4_u32plus)size >> 29; - /* Pad out to 56 mod 64. - */ - bufindex = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (bufindex < 56) ? (56 - bufindex) : (120 - bufindex); - MD4Update (context, PADDING, padLen); + used = saved_lo & 0x3f; - /* Append length (before padding) */ - MD4Update (context, bits, 8); -} + if(used) { + available = 64 - used; -/* MD4 finalization. Ends an MD4 message-digest operation, writing the - the message digest and zeroizing the context. - */ -static void MD4Final (unsigned char digest[16], MD4_CTX *context) -{ - /* Do padding */ - MD4Pad (context); + if(size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } - /* Store state in digest */ - Encode (digest, context->state, 16); + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } - /* Zeroize sensitive information. - */ - memset(context, 0, sizeof(*context)); -} + if(size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } -/* MD4 basic transformation. Transforms state based on block. - */ -static void MD4Transform (UINT4 state[4], const unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11); /* 1 */ - FF (d, a, b, c, x[ 1], S12); /* 2 */ - FF (c, d, a, b, x[ 2], S13); /* 3 */ - FF (b, c, d, a, x[ 3], S14); /* 4 */ - FF (a, b, c, d, x[ 4], S11); /* 5 */ - FF (d, a, b, c, x[ 5], S12); /* 6 */ - FF (c, d, a, b, x[ 6], S13); /* 7 */ - FF (b, c, d, a, x[ 7], S14); /* 8 */ - FF (a, b, c, d, x[ 8], S11); /* 9 */ - FF (d, a, b, c, x[ 9], S12); /* 10 */ - FF (c, d, a, b, x[10], S13); /* 11 */ - FF (b, c, d, a, x[11], S14); /* 12 */ - FF (a, b, c, d, x[12], S11); /* 13 */ - FF (d, a, b, c, x[13], S12); /* 14 */ - FF (c, d, a, b, x[14], S13); /* 15 */ - FF (b, c, d, a, x[15], S14); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 0], S21); /* 17 */ - GG (d, a, b, c, x[ 4], S22); /* 18 */ - GG (c, d, a, b, x[ 8], S23); /* 19 */ - GG (b, c, d, a, x[12], S24); /* 20 */ - GG (a, b, c, d, x[ 1], S21); /* 21 */ - GG (d, a, b, c, x[ 5], S22); /* 22 */ - GG (c, d, a, b, x[ 9], S23); /* 23 */ - GG (b, c, d, a, x[13], S24); /* 24 */ - GG (a, b, c, d, x[ 2], S21); /* 25 */ - GG (d, a, b, c, x[ 6], S22); /* 26 */ - GG (c, d, a, b, x[10], S23); /* 27 */ - GG (b, c, d, a, x[14], S24); /* 28 */ - GG (a, b, c, d, x[ 3], S21); /* 29 */ - GG (d, a, b, c, x[ 7], S22); /* 30 */ - GG (c, d, a, b, x[11], S23); /* 31 */ - GG (b, c, d, a, x[15], S24); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 0], S31); /* 33 */ - HH (d, a, b, c, x[ 8], S32); /* 34 */ - HH (c, d, a, b, x[ 4], S33); /* 35 */ - HH (b, c, d, a, x[12], S34); /* 36 */ - HH (a, b, c, d, x[ 2], S31); /* 37 */ - HH (d, a, b, c, x[10], S32); /* 38 */ - HH (c, d, a, b, x[ 6], S33); /* 39 */ - HH (b, c, d, a, x[14], S34); /* 40 */ - HH (a, b, c, d, x[ 1], S31); /* 41 */ - HH (d, a, b, c, x[ 9], S32); /* 42 */ - HH (c, d, a, b, x[ 5], S33); /* 43 */ - HH (b, c, d, a, x[13], S34); /* 44 */ - HH (a, b, c, d, x[ 3], S31); /* 45 */ - HH (d, a, b, c, x[11], S32); /* 46 */ - HH (c, d, a, b, x[ 7], S33); /* 47 */ - HH (b, c, d, a, x[15], S34); /* 48 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. - */ - memset(x, 0, sizeof(x)); + memcpy(ctx->buffer, data, size); } -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ -static void Encode(unsigned char *output, UINT4 *input, unsigned int len) +static void MD4_Final(unsigned char *result, MD4_CTX *ctx) { - unsigned int i, j; + unsigned long used, available; - for(i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } -} + used = ctx->lo & 0x3f; -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - */ -static void Decode (UINT4 *output, const unsigned char *input, - unsigned int len) -{ - unsigned int i, j; + ctx->buffer[used++] = 0x80; + + available = 64 - used; - for(i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); + if(available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); + ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); + ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); + ctx->buffer[59] = curlx_ultouc((ctx->lo >> 24)&0xff); + ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); + ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); + ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); + ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); + + body(ctx, ctx->buffer, 64); + + result[0] = curlx_ultouc((ctx->a)&0xff); + result[1] = curlx_ultouc((ctx->a >> 8)&0xff); + result[2] = curlx_ultouc((ctx->a >> 16)&0xff); + result[3] = curlx_ultouc(ctx->a >> 24); + result[4] = curlx_ultouc((ctx->b)&0xff); + result[5] = curlx_ultouc((ctx->b >> 8)&0xff); + result[6] = curlx_ultouc((ctx->b >> 16)&0xff); + result[7] = curlx_ultouc(ctx->b >> 24); + result[8] = curlx_ultouc((ctx->c)&0xff); + result[9] = curlx_ultouc((ctx->c >> 8)&0xff); + result[10] = curlx_ultouc((ctx->c >> 16)&0xff); + result[11] = curlx_ultouc(ctx->c >> 24); + result[12] = curlx_ultouc((ctx->d)&0xff); + result[13] = curlx_ultouc((ctx->d >> 8)&0xff); + result[14] = curlx_ultouc((ctx->d >> 16)&0xff); + result[15] = curlx_ultouc(ctx->d >> 24); + + memset(ctx, 0, sizeof(*ctx)); } +#endif + void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len) { MD4_CTX ctx; - MD4Init(&ctx); - MD4Update(&ctx, input, curlx_uztoui(len)); - MD4Final(output, &ctx); + MD4_Init(&ctx); + MD4_Update(&ctx, input, curlx_uztoui(len)); + MD4_Final(output, &ctx); } -#endif /* USE_NSS */ +#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) */ @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,11 +28,10 @@ #include "curl_hmac.h" #include "warnless.h" -#include "curl_memory.h" - #if defined(USE_GNUTLS_NETTLE) #include <nettle/md5.h> +#include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -58,6 +57,7 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) #elif defined(USE_GNUTLS) #include <gcrypt.h> +#include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -81,14 +81,12 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) gcry_md_close(*ctx); } -#elif defined(USE_SSLEAY) +#elif defined(USE_OPENSSL) /* When OpenSSL is available we use the MD5-function from OpenSSL */ - -# ifdef USE_OPENSSL -# include <openssl/md5.h> -# else -# include <md5.h> -# endif +#include <openssl/md5.h> +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ @@ -103,6 +101,9 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx) reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ # include <CommonCrypto/CommonDigest.h> # define MD5_CTX CC_MD5_CTX +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" static void MD5_Init(MD5_CTX *ctx) { @@ -124,6 +125,9 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) #elif defined(_WIN32) #include <wincrypt.h> +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" typedef struct { HCRYPTPROV hCryptProv; @@ -157,314 +161,326 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) CryptReleaseContext(ctx->hCryptProv, 0); } +#elif defined(USE_AXTLS) +#include <axTLS/config.h> +#include <axTLS/os_int.h> +#include <axTLS/crypto.h> +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" #else /* When no other crypto library is available we use this code segment */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. */ -/* UINT4 defines a four byte word */ -typedef unsigned int UINT4; +#include <string.h> -/* MD5 context. */ -struct md5_ctx { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -}; - -typedef struct md5_ctx MD5_CTX; +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" -static void MD5_Init(struct md5_ctx *); -static void MD5_Update(struct md5_ctx *, const unsigned char *, unsigned int); -static void MD5_Final(unsigned char [16], struct md5_ctx *); +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; -/* Constants for MD5Transform routine. - */ +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -static void MD5Transform(UINT4 [4], const unsigned char [64]); -static void Encode(unsigned char *, UINT4 *, unsigned int); -static void Decode(UINT4 *, const unsigned char *, unsigned int); - -static const unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; +static void 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); -/* F, G, H and I are basic MD5 functions. +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits. +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -Rotation is separate from addition to prevent recomputation. +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - -/* MD5 initialization. Begins an MD5 operation, writing a new context. +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. */ -static void MD5_Init(struct md5_ctx *context) +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) { - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while(size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; } -/* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the - context. - */ -static void MD5_Update (struct md5_ctx *context, /* context */ - const unsigned char *input, /* input block */ - unsigned int inputLen) /* length of input block */ +static void MD5_Init(MD5_CTX *ctx) { - unsigned int i, bufindex, partLen; + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; - /* Compute number of bytes mod 64 */ - bufindex = (unsigned int)((context->count[0] >> 3) & 0x3F); + ctx->lo = 0; + ctx->hi = 0; +} - /* Update number of bits */ - if((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); +static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += (MD5_u32plus)size >> 29; - partLen = 64 - bufindex; + used = saved_lo & 0x3f; - /* Transform as many times as possible. */ - if(inputLen >= partLen) { - memcpy(&context->buffer[bufindex], input, partLen); - MD5Transform(context->state, context->buffer); + if(used) { + available = 64 - used; - for(i = partLen; i + 63 < inputLen; i += 64) - MD5Transform(context->state, &input[i]); + if(size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } - bufindex = 0; + if(size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; } - else - i = 0; - /* Buffer remaining input */ - memcpy(&context->buffer[bufindex], &input[i], inputLen-i); + memcpy(ctx->buffer, data, size); } -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - the message digest and zeroizing the context. -*/ -static void MD5_Final(unsigned char digest[16], /* message digest */ - struct md5_ctx *context) /* context */ +static void MD5_Final(unsigned char *result, MD5_CTX *ctx) { - unsigned char bits[8]; - unsigned int count, padLen; + unsigned long used, available; - /* Save number of bits */ - Encode (bits, context->count, 8); + used = ctx->lo & 0x3f; - /* Pad out to 56 mod 64. */ - count = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (count < 56) ? (56 - count) : (120 - count); - MD5_Update (context, PADDING, padLen); + ctx->buffer[used++] = 0x80; - /* Append length (before padding) */ - MD5_Update (context, bits, 8); + available = 64 - used; - /* Store state in digest */ - Encode (digest, context->state, 16); - - /* Zeroize sensitive information. */ - memset ((void *)context, 0, sizeof (*context)); -} - -/* MD5 basic transformation. Transforms state based on block. */ -static void MD5Transform(UINT4 state[4], - const unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. */ - memset((void *)x, 0, sizeof (x)); -} - -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ -static void Encode (unsigned char *output, - UINT4 *input, - unsigned int len) -{ - unsigned int i, j; - - for(i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + if(available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; } -} - -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. -*/ -static void Decode (UINT4 *output, - const unsigned char *input, - unsigned int len) -{ - unsigned int i, j; - for(i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); + ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); + ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); + ctx->buffer[59] = curlx_ultouc(ctx->lo >> 24); + ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); + ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); + ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); + ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); + + body(ctx, ctx->buffer, 64); + + result[0] = curlx_ultouc((ctx->a)&0xff); + result[1] = curlx_ultouc((ctx->a >> 8)&0xff); + result[2] = curlx_ultouc((ctx->a >> 16)&0xff); + result[3] = curlx_ultouc(ctx->a >> 24); + result[4] = curlx_ultouc((ctx->b)&0xff); + result[5] = curlx_ultouc((ctx->b >> 8)&0xff); + result[6] = curlx_ultouc((ctx->b >> 16)&0xff); + result[7] = curlx_ultouc(ctx->b >> 24); + result[8] = curlx_ultouc((ctx->c)&0xff); + result[9] = curlx_ultouc((ctx->c >> 8)&0xff); + result[10] = curlx_ultouc((ctx->c >> 16)&0xff); + result[11] = curlx_ultouc(ctx->c >> 24); + result[12] = curlx_ultouc((ctx->d)&0xff); + result[13] = curlx_ultouc((ctx->d >> 8)&0xff); + result[14] = curlx_ultouc((ctx->d >> 16)&0xff); + result[15] = curlx_ultouc(ctx->d >> 24); + + memset(ctx, 0, sizeof(*ctx)); } #endif /* CRYPTO LIBS */ -/* The last #include file should be: */ -#include "memdebug.h" - const HMAC_params Curl_HMAC_MD5[] = { { (HMAC_hinit_func) MD5_Init, /* Hash initialization function. */ @@ -486,6 +502,9 @@ const MD5_params Curl_DIGEST_MD5[] = { } }; +/* + * @unittest: 1601 + */ void Curl_md5it(unsigned char *outbuffer, /* 16 bytes */ const unsigned char *input) { diff --git a/lib/memdebug.c b/lib/memdebug.c index 4afa620..dd8889b 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,8 +26,7 @@ #include <curl/curl.h> -#define _MPRINTF_REPLACE -#include <curl/mprintf.h> +#include "curl_printf.h" #include "urldata.h" #define MEMDEBUG_NODEFINES /* don't redefine the standard functions */ @@ -113,7 +112,7 @@ void curl_memdebug(const char *logname) { if(!logfile) { if(logname && *logname) - logfile = fopen(logname, "w"); + logfile = fopen(logname, FOPEN_WRITETEXT); else logfile = stderr; #ifdef MEMDEBUG_LOG_SYNC @@ -343,10 +342,10 @@ curl_socket_t curl_socket(int domain, int type, int protocol, int line, const char *source) { const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d socket() = %d\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d socket() = %ld\n" : - "FD %s:%d socket() = %zd\n" ; + "FD %s:%d socket() = %d\n" : + (sizeof(curl_socket_t) == sizeof(long)) ? + "FD %s:%d socket() = %ld\n" : + "FD %s:%d socket() = %zd\n"; curl_socket_t sockfd = socket(domain, type, protocol); @@ -362,10 +361,10 @@ int curl_socketpair(int domain, int type, int protocol, int line, const char *source) { const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d socketpair() = %d %d\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d socketpair() = %ld %ld\n" : - "FD %s:%d socketpair() = %zd %zd\n" ; + "FD %s:%d socketpair() = %d %d\n" : + (sizeof(curl_socket_t) == sizeof(long)) ? + "FD %s:%d socketpair() = %ld %ld\n" : + "FD %s:%d socketpair() = %zd %zd\n"; int res = socketpair(domain, type, protocol, socket_vector); @@ -380,10 +379,10 @@ curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen, int line, const char *source) { const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d accept() = %d\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d accept() = %ld\n" : - "FD %s:%d accept() = %zd\n" ; + "FD %s:%d accept() = %d\n" : + (sizeof(curl_socket_t) == sizeof(long)) ? + "FD %s:%d accept() = %ld\n" : + "FD %s:%d accept() = %zd\n"; struct sockaddr *addr = (struct sockaddr *)saddr; curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; @@ -400,10 +399,10 @@ curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen, void curl_mark_sclose(curl_socket_t sockfd, int line, const char *source) { const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d sclose(%d)\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d sclose(%ld)\n" : - "FD %s:%d sclose(%zd)\n" ; + "FD %s:%d sclose(%d)\n": + (sizeof(curl_socket_t) == sizeof(long)) ? + "FD %s:%d sclose(%ld)\n": + "FD %s:%d sclose(%zd)\n"; if(source) curl_memlog(fmt, source, line, sockfd); diff --git a/lib/memdebug.h b/lib/memdebug.h index bd565c8..cfac1e0 100644 --- a/lib/memdebug.h +++ b/lib/memdebug.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -104,13 +104,13 @@ CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source); #endif #define socket(domain,type,protocol)\ - curl_socket(domain,type,protocol,__LINE__,__FILE__) + curl_socket(domain, type, protocol, __LINE__, __FILE__) #undef accept /* for those with accept as a macro */ #define accept(sock,addr,len)\ - curl_accept(sock,addr,len,__LINE__,__FILE__) + curl_accept(sock, addr, len, __LINE__, __FILE__) #ifdef HAVE_SOCKETPAIR #define socketpair(domain,type,protocol,socket_vector)\ - curl_socketpair(domain,type,protocol,socket_vector,__LINE__,__FILE__) + curl_socketpair(domain, type, protocol, socket_vector, __LINE__, __FILE__) #endif #ifdef HAVE_GETADDRINFO @@ -119,25 +119,25 @@ CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source); our macro as for other platforms. Instead, we redefine the new name they define getaddrinfo to become! */ #define ogetaddrinfo(host,serv,hint,res) \ - curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__) + curl_dogetaddrinfo(host, serv, hint, res, __LINE__, __FILE__) #else #undef getaddrinfo #define getaddrinfo(host,serv,hint,res) \ - curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__) + curl_dogetaddrinfo(host, serv, hint, res, __LINE__, __FILE__) #endif #endif /* HAVE_GETADDRINFO */ #ifdef HAVE_GETNAMEINFO #undef getnameinfo #define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \ - curl_dogetnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \ - __FILE__) + curl_dogetnameinfo(sa, salen, host, hostlen, serv, servlen, flags, \ + __LINE__, __FILE__) #endif /* HAVE_GETNAMEINFO */ #ifdef HAVE_FREEADDRINFO #undef freeaddrinfo #define freeaddrinfo(data) \ - curl_dofreeaddrinfo(data,__LINE__,__FILE__) + curl_dofreeaddrinfo(data, __LINE__, __FILE__) #endif /* HAVE_FREEADDRINFO */ /* sclose is probably already defined, redefine it! */ @@ -171,6 +171,6 @@ CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source); */ #define Curl_safefree(ptr) \ - do {if((ptr)) {free((ptr)); (ptr) = NULL;}} WHILE_FALSE + do { free((ptr)); (ptr) = NULL;} WHILE_FALSE #endif /* HEADER_CURL_MEMDEBUG_H */ diff --git a/lib/multi.c b/lib/multi.c index a1dc2c8..0052087 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -39,14 +39,10 @@ #include "warnless.h" #include "speedcheck.h" #include "conncache.h" -#include "bundles.h" #include "multihandle.h" #include "pipeline.h" #include "sigpipe.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -66,15 +62,11 @@ #define GOOD_MULTI_HANDLE(x) \ ((x) && (((struct Curl_multi *)(x))->type == CURL_MULTI_HANDLE)) -#define GOOD_EASY_HANDLE(x) \ - ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER)) static void singlesocket(struct Curl_multi *multi, struct SessionHandle *data); static int update_timer(struct Curl_multi *multi); -static bool isHandleAtHead(struct SessionHandle *handle, - struct curl_llist *pipeline); static CURLMcode add_next_timeout(struct timeval now, struct Curl_multi *multi, struct SessionHandle *d); @@ -89,6 +81,7 @@ static const char * const statename[]={ "WAITRESOLVE", "WAITCONNECT", "WAITPROXYCONNECT", + "SENDPROTOCONNECT", "PROTOCONNECT", "WAITDO", "DO", @@ -113,20 +106,23 @@ static void mstate(struct SessionHandle *data, CURLMstate state #endif ) { -#ifdef DEBUGBUILD - long connection_id = -5000; -#endif CURLMstate oldstate = data->mstate; +#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) lineno; +#endif + if(oldstate == state) /* don't bother when the new state is the same as the old state */ return; data->mstate = state; -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(data->mstate >= CURLM_STATE_CONNECT_PEND && data->mstate < CURLM_STATE_COMPLETED) { + long connection_id = -5000; + if(data->easy_conn) connection_id = data->easy_conn->connection_id; @@ -136,6 +132,7 @@ static void mstate(struct SessionHandle *data, CURLMstate state (void *)data, lineno, connection_id); } #endif + if(state == CURLM_STATE_COMPLETED) /* changing to COMPLETED means there's one less easy handle 'alive' */ data->multi->num_alive--; @@ -153,7 +150,6 @@ static void mstate(struct SessionHandle *data, CURLMstate state struct Curl_sh_entry { struct SessionHandle *easy; - time_t timestamp; int action; /* what action READ/WRITE this socket waits for */ curl_socket_t socket; /* mainly to ease debugging */ void *socketp; /* settable by users with curl_multi_assign() */ @@ -180,11 +176,12 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh, check = calloc(1, sizeof(struct Curl_sh_entry)); if(!check) return NULL; /* major failure */ + check->easy = data; check->socket = s; /* make/add new hash entry */ - if(NULL == Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { + if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { free(check); return NULL; /* major failure */ } @@ -214,8 +211,7 @@ static void sh_freeentry(void *freethis) { struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; - if(p) - free(p); + free(p); } static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) @@ -251,10 +247,10 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num) * per call." * */ -static struct curl_hash *sh_init(int hashsize) +static int sh_init(struct curl_hash *hash, int hashsize) { - return Curl_hash_alloc(hashsize, hash_fd, fd_key_compare, - sh_freeentry); + return Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, + sh_freeentry); } /* @@ -293,16 +289,13 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ multi->type = CURL_MULTI_HANDLE; - multi->hostcache = Curl_mk_dnscache(); - if(!multi->hostcache) + if(Curl_mk_dnscache(&multi->hostcache)) goto error; - multi->sockhash = sh_init(hashsize); - if(!multi->sockhash) + if(sh_init(&multi->sockhash, hashsize)) goto error; - multi->conn_cache = Curl_conncache_init(chashsize); - if(!multi->conn_cache) + if(Curl_conncache_init(&multi->conn_cache, chashsize)) goto error; multi->msglist = Curl_llist_alloc(multi_freeamsg); @@ -319,7 +312,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ goto error; multi->closure_handle->multi = multi; - multi->closure_handle->state.conn_cache = multi->conn_cache; + multi->closure_handle->state.conn_cache = &multi->conn_cache; multi->max_pipeline_length = 5; @@ -329,12 +322,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ error: - Curl_hash_destroy(multi->sockhash); - multi->sockhash = NULL; - Curl_hash_destroy(multi->hostcache); - multi->hostcache = NULL; - Curl_conncache_destroy(multi->conn_cache); - multi->conn_cache = NULL; + Curl_hash_destroy(&multi->sockhash); + Curl_hash_destroy(&multi->hostcache); + Curl_conncache_destroy(&multi->conn_cache); Curl_close(multi->closure_handle); multi->closure_handle = NULL; Curl_llist_destroy(multi->msglist, NULL); @@ -403,14 +393,12 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, easy handle's one is currently not set. */ else if(!data->dns.hostcache || (data->dns.hostcachetype == HCACHE_NONE)) { - data->dns.hostcache = multi->hostcache; + data->dns.hostcache = &multi->hostcache; data->dns.hostcachetype = HCACHE_MULTI; } /* Point to the multi's connection cache */ - data->state.conn_cache = multi->conn_cache; - - data->state.infilesize = data->set.filesize; + data->state.conn_cache = &multi->conn_cache; /* This adds the new entry at the 'end' of the doubly-linked circular list of SessionHandle structs to try and maintain a FIFO queue so @@ -426,8 +414,7 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, multi->easylp = data; /* the new last node */ } else { - /* first node, make both prev and next be NULL! */ - data->next = NULL; + /* first node, make prev NULL! */ data->prev = NULL; multi->easylp = multi->easyp = data; /* both first and last */ } @@ -487,6 +474,9 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct SessionHandle *easy = curl_handle; struct SessionHandle *data = easy; + bool premature; + bool easy_owns_conn; + struct curl_llist_element *e; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -500,131 +490,127 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle, if(!data->multi) return CURLM_OK; /* it is already removed so let's say it is fine! */ - if(easy) { - bool premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; - bool easy_owns_conn = (data->easy_conn && - (data->easy_conn->data == easy)) ? - TRUE : FALSE; + premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; + easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ? + TRUE : FALSE; - /* If the 'state' is not INIT or COMPLETED, we might need to do something - nice to put the easy_handle in a good known state when this returns. */ - if(premature) - /* this handle is "alive" so we need to count down the total number of - alive connections when this is removed */ - multi->num_alive--; + /* If the 'state' is not INIT or COMPLETED, we might need to do something + nice to put the easy_handle in a good known state when this returns. */ + if(premature) { + /* this handle is "alive" so we need to count down the total number of + alive connections when this is removed */ + multi->num_alive--; - if(data->easy_conn && - (data->easy_conn->send_pipe->size + - data->easy_conn->recv_pipe->size > 1) && - data->mstate > CURLM_STATE_WAITDO && - data->mstate < CURLM_STATE_COMPLETED) { - /* If the handle is in a pipeline and has started sending off its - request but not received its response yet, we need to close - connection. */ - connclose(data->easy_conn, "Removed with partial response"); - /* Set connection owner so that Curl_done() closes it. - We can sefely do this here since connection is killed. */ - data->easy_conn->data = easy; - } + /* When this handle gets removed, other handles may be able to get the + connection */ + Curl_multi_process_pending_handles(multi); + } - /* The timer must be shut down before data->multi is set to NULL, - else the timenode will remain in the splay tree after - curl_easy_cleanup is called. */ - Curl_expire(data, 0); + if(data->easy_conn && + data->mstate > CURLM_STATE_DO && + data->mstate < CURLM_STATE_COMPLETED) { + /* If the handle is in a pipeline and has started sending off its + request but not received its response yet, we need to close + connection. */ + connclose(data->easy_conn, "Removed with partial response"); + /* Set connection owner so that Curl_done() closes it. + We can safely do this here since connection is killed. */ + data->easy_conn->data = easy; + easy_owns_conn = TRUE; + } - /* destroy the timeout list that is held in the easy handle */ - if(data->state.timeoutlist) { - Curl_llist_destroy(data->state.timeoutlist, NULL); - data->state.timeoutlist = NULL; - } + /* The timer must be shut down before data->multi is set to NULL, + else the timenode will remain in the splay tree after + curl_easy_cleanup is called. */ + Curl_expire(data, 0); - if(data->dns.hostcachetype == HCACHE_MULTI) { - /* stop using the multi handle's DNS cache */ - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } + /* destroy the timeout list that is held in the easy handle */ + if(data->state.timeoutlist) { + Curl_llist_destroy(data->state.timeoutlist, NULL); + data->state.timeoutlist = NULL; + } - if(data->easy_conn) { + if(data->dns.hostcachetype == HCACHE_MULTI) { + /* stop using the multi handle's DNS cache */ + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } - /* we must call Curl_done() here (if we still "own it") so that we don't - leave a half-baked one around */ - if(easy_owns_conn) { + if(data->easy_conn) { - /* Curl_done() clears the conn->data field to lose the association - between the easy handle and the connection + /* we must call Curl_done() here (if we still "own it") so that we don't + leave a half-baked one around */ + if(easy_owns_conn) { - Note that this ignores the return code simply because there's - nothing really useful to do with it anyway! */ - (void)Curl_done(&data->easy_conn, data->result, premature); - } - else - /* Clear connection pipelines, if Curl_done above was not called */ - Curl_getoff_all_pipelines(data, data->easy_conn); + /* Curl_done() clears the conn->data field to lose the association + between the easy handle and the connection + + Note that this ignores the return code simply because there's + nothing really useful to do with it anyway! */ + (void)Curl_done(&data->easy_conn, data->result, premature); } + else + /* Clear connection pipelines, if Curl_done above was not called */ + Curl_getoff_all_pipelines(data, data->easy_conn); + } - Curl_wildcard_dtor(&data->wildcard); + Curl_wildcard_dtor(&data->wildcard); - /* as this was using a shared connection cache we clear the pointer - to that since we're not part of that multi handle anymore */ - data->state.conn_cache = NULL; + /* as this was using a shared connection cache we clear the pointer to that + since we're not part of that multi handle anymore */ + data->state.conn_cache = NULL; - /* change state without using multistate(), only to make singlesocket() do - what we want */ - data->mstate = CURLM_STATE_COMPLETED; - singlesocket(multi, easy); /* to let the application know what sockets - that vanish with this handle */ + /* change state without using multistate(), only to make singlesocket() do + what we want */ + data->mstate = CURLM_STATE_COMPLETED; + singlesocket(multi, easy); /* to let the application know what sockets that + vanish with this handle */ - /* Remove the association between the connection and the handle */ - if(data->easy_conn) { - data->easy_conn->data = NULL; - data->easy_conn = NULL; - } + /* Remove the association between the connection and the handle */ + if(data->easy_conn) { + data->easy_conn->data = NULL; + data->easy_conn = NULL; + } - data->multi = NULL; /* clear the association to this multi handle */ + data->multi = NULL; /* clear the association to this multi handle */ - { - /* make sure there's no pending message in the queue sent from this easy - handle */ - struct curl_llist_element *e; + /* make sure there's no pending message in the queue sent from this easy + handle */ - for(e = multi->msglist->head; e; e = e->next) { - struct Curl_message *msg = e->ptr; + for(e = multi->msglist->head; e; e = e->next) { + struct Curl_message *msg = e->ptr; - if(msg->extmsg.easy_handle == easy) { - Curl_llist_remove(multi->msglist, e, NULL); - /* there can only be one from this specific handle */ - break; - } - } + if(msg->extmsg.easy_handle == easy) { + Curl_llist_remove(multi->msglist, e, NULL); + /* there can only be one from this specific handle */ + break; } + } - /* make the previous node point to our next */ - if(data->prev) - data->prev->next = data->next; - else - multi->easyp = data->next; /* point to first node */ - - /* make our next point to our previous node */ - if(data->next) - data->next->prev = data->prev; - else - multi->easylp = data->prev; /* point to last node */ + /* make the previous node point to our next */ + if(data->prev) + data->prev->next = data->next; + else + multi->easyp = data->next; /* point to first node */ - /* NOTE NOTE NOTE - We do not touch the easy handle here! */ + /* make our next point to our previous node */ + if(data->next) + data->next->prev = data->prev; + else + multi->easylp = data->prev; /* point to last node */ - multi->num_easy--; /* one less to care about now */ + /* NOTE NOTE NOTE + We do not touch the easy handle here! */ + multi->num_easy--; /* one less to care about now */ - update_timer(multi); - return CURLM_OK; - } - else - return CURLM_BAD_EASY_HANDLE; /* twasn't found */ + update_timer(multi); + return CURLM_OK; } -bool Curl_multi_pipeline_enabled(const struct Curl_multi *multi) +/* Return TRUE if the application asked for a certain set of pipelining */ +bool Curl_pipeline_wanted(const struct Curl_multi *multi, int bits) { - return (multi && multi->pipelining_enabled) ? TRUE : FALSE; + return (multi && (multi->pipelining & bits)) ? TRUE : FALSE; } void Curl_multi_handlePipeBreak(struct SessionHandle *data) @@ -650,14 +636,24 @@ static int waitconnect_getsock(struct connectdata *conn, } } + return rc; +} + +static int waitproxyconnect_getsock(struct connectdata *conn, + curl_socket_t *sock, + int numsocks) +{ + if(!numsocks) + return GETSOCK_BLANK; + + sock[0] = conn->sock[FIRSTSOCKET]; + /* when we've sent a CONNECT to a proxy, we should rather wait for the socket to become readable to be able to get the response headers */ - if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) { - sock[0] = conn->sock[FIRSTSOCKET]; - rc = GETSOCK_READSOCK(0); - } + if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) + return GETSOCK_READSOCK(0); - return rc; + return GETSOCK_WRITESOCK(0); } static int domore_getsock(struct connectdata *conn, @@ -710,6 +706,7 @@ static int multi_getsock(struct SessionHandle *data, return Curl_resolver_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: + case CURLM_STATE_SENDPROTOCONNECT: return Curl_protocol_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_DO: @@ -717,6 +714,8 @@ static int multi_getsock(struct SessionHandle *data, return Curl_doing_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_WAITPROXYCONNECT: + return waitproxyconnect_getsock(data->easy_conn, socks, numsocks); + case CURLM_STATE_WAITCONNECT: return waitconnect_getsock(data->easy_conn, socks, numsocks); @@ -917,12 +916,62 @@ CURLMcode curl_multi_wait(CURLM *multi_handle, else i = 0; - Curl_safefree(ufds); + free(ufds); if(ret) *ret = i; return CURLM_OK; } +/* + * Curl_multi_connchanged() is called to tell that there is a connection in + * this multi handle that has changed state (pipelining become possible, the + * number of allowed streams changed or similar), and a subsequent use of this + * multi handle should move CONNECT_PEND handles back to CONNECT to have them + * retry. + */ +void Curl_multi_connchanged(struct Curl_multi *multi) +{ + multi->recheckstate = TRUE; +} + +/* + * multi_ischanged() is called + * + * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND + * => CONNECT action. + * + * Set 'clear' to TRUE to have it also clear the state variable. + */ +static bool multi_ischanged(struct Curl_multi *multi, bool clear) +{ + bool retval = multi->recheckstate; + if(clear) + multi->recheckstate = FALSE; + return retval; +} + +CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, + struct SessionHandle *data, + struct connectdata *conn) +{ + CURLMcode rc; + + rc = curl_multi_add_handle(multi, data); + if(!rc) { + struct SingleRequest *k = &data->req; + + /* pass in NULL for 'conn' here since we don't want to init the + connection, only this transfer */ + Curl_init_do(data, NULL); + + /* take this handle to the perform state right away */ + multistate(data, CURLM_STATE_PERFORM); + data->easy_conn = conn; + k->keepon |= KEEP_RECV; /* setup to receive! */ + } + return rc; +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct timeval now, struct SessionHandle *data) @@ -933,7 +982,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, bool protocol_connect = FALSE; bool dophase_done = FALSE; bool done = FALSE; - CURLMcode result = CURLM_OK; + CURLMcode rc; + CURLcode result = CURLE_OK; struct SingleRequest *k; long timeout_ms; int control; @@ -942,26 +992,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, return CURLM_BAD_EASY_HANDLE; do { - /* this is a single-iteration do-while loop just to allow a - break to skip to the end of it */ bool disconnect_conn = FALSE; + rc = CURLM_OK; /* Handle the case when the pipe breaks, i.e., the connection we're using gets cleaned up and we're left with nothing. */ if(data->state.pipe_broke) { - infof(data, "Pipe broke: handle 0x%p, url = %s\n", + infof(data, "Pipe broke: handle %p, url = %s\n", (void *)data, data->state.path); if(data->mstate < CURLM_STATE_COMPLETED) { /* Head back to the CONNECT state */ multistate(data, CURLM_STATE_CONNECT); - result = CURLM_CALL_MULTI_PERFORM; - data->result = CURLE_OK; + rc = CURLM_CALL_MULTI_PERFORM; + result = CURLE_OK; } data->state.pipe_broke = FALSE; data->easy_conn = NULL; - break; + continue; } if(!data->easy_conn && @@ -974,6 +1023,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, return CURLM_INTERNAL_ERROR; } + if(multi_ischanged(multi, TRUE)) { + DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n")); + Curl_multi_process_pending_handles(multi); + } + if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT && data->mstate < CURLM_STATE_COMPLETED) /* Make sure we set the connection's current owner */ @@ -1014,26 +1068,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } - /* Force the connection closed because the server could continue to - send us stuff at any time. (The disconnect_conn logic used below - doesn't work at this point). */ - connclose(data->easy_conn, "Disconnected with pending data"); - data->result = CURLE_OPERATION_TIMEDOUT; - multistate(data, CURLM_STATE_COMPLETED); - break; + /* Force connection closed if the connection has indeed been used */ + if(data->mstate > CURLM_STATE_DO) { + connclose(data->easy_conn, "Disconnected with pending data"); + disconnect_conn = TRUE; + } + result = CURLE_OPERATION_TIMEDOUT; + (void)Curl_done(&data->easy_conn, result, TRUE); + /* Skip the statemachine and go directly to error handling section. */ + goto statemachine_end; } } switch(data->mstate) { case CURLM_STATE_INIT: /* init this transfer. */ - data->result=Curl_pretransfer(data); + result=Curl_pretransfer(data); - if(CURLE_OK == data->result) { + if(!result) { /* after init, go CONNECT */ multistate(data, CURLM_STATE_CONNECT); Curl_pgrsTime(data, TIMER_STARTOP); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } break; @@ -1045,25 +1101,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_CONNECT: /* Connect. We want to get a connection identifier filled in. */ Curl_pgrsTime(data, TIMER_STARTSINGLE); - data->result = Curl_connect(data, &data->easy_conn, - &async, &protocol_connect); - if(CURLE_NO_CONNECTION_AVAILABLE == data->result) { + result = Curl_connect(data, &data->easy_conn, + &async, &protocol_connect); + if(CURLE_NO_CONNECTION_AVAILABLE == result) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ multistate(data, CURLM_STATE_CONNECT_PEND); /* add this handle to the list of connect-pending handles */ if(!Curl_llist_insert_next(multi->pending, multi->pending->tail, data)) - data->result = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else - data->result = CURLE_OK; + result = CURLE_OK; break; } - if(CURLE_OK == data->result) { + if(!result) { /* Add this handle to the send or pend pipeline */ - data->result = Curl_add_handle_to_pipeline(data, data->easy_conn); - if(CURLE_OK != data->result) + result = Curl_add_handle_to_pipeline(data, data->easy_conn); + if(result) disconnect_conn = TRUE; else { if(async) @@ -1073,10 +1129,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* after the connect has been sent off, go WAITCONNECT unless the protocol connect is already done and we can go directly to WAITDO or DO! */ - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(data, multi->pipelining_enabled? + multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)? CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP @@ -1096,31 +1152,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, { struct Curl_dns_entry *dns = NULL; struct connectdata *conn = data->easy_conn; - int stale; /* check if we have the name resolved by now */ - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - dns = Curl_fetch_addr(conn, conn->host.name, (int)conn->port, &stale); + dns = Curl_fetch_addr(conn, conn->host.name, (int)conn->port); if(dns) { - dns->inuse++; /* we use it! */ #ifdef CURLRES_ASYNCH conn->async.dns = dns; conn->async.done = TRUE; #endif - data->result = CURLRESOLV_RESOLVED; + result = CURLE_OK; infof(data, "Hostname was found in DNS cache\n"); } - if(stale) - infof(data, "Hostname in DNS cache was stale, zapped\n"); - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) - data->result = Curl_resolver_is_resolved(data->easy_conn, &dns); + result = Curl_resolver_is_resolved(data->easy_conn, &dns); /* Update sockets here, because the socket(s) may have been closed and the application thus needs to be told, even if it @@ -1133,18 +1179,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - data->result = Curl_async_resolved(data->easy_conn, - &protocol_connect); + result = Curl_async_resolved(data->easy_conn, &protocol_connect); - if(CURLE_OK != data->result) + if(result) /* if Curl_async_resolved() returns failure, the connection struct is already freed and gone */ data->easy_conn = NULL; /* no more connection */ else { /* call again please so that we get the next socket setup */ - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; if(protocol_connect) - multistate(data, multi->pipelining_enabled? + multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)? CURLM_STATE_WAITDO:CURLM_STATE_DO); else { #ifndef CURL_DISABLE_HTTP @@ -1157,7 +1202,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } - if(CURLE_OK != data->result) { + if(result) { /* failure detected */ disconnect_conn = TRUE; break; @@ -1168,109 +1213,82 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, #ifndef CURL_DISABLE_HTTP case CURLM_STATE_WAITPROXYCONNECT: /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ - data->result = Curl_http_connect(data->easy_conn, &protocol_connect); + result = Curl_http_connect(data->easy_conn, &protocol_connect); + rc = CURLM_CALL_MULTI_PERFORM; if(data->easy_conn->bits.proxy_connect_closed) { /* connect back to proxy again */ - data->result = CURLE_OK; - result = CURLM_CALL_MULTI_PERFORM; + result = CURLE_OK; + Curl_done(&data->easy_conn, CURLE_OK, FALSE); multistate(data, CURLM_STATE_CONNECT); } - else if(CURLE_OK == data->result) { + else if(!result) { if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE) - multistate(data, CURLM_STATE_WAITCONNECT); + /* initiate protocol connect phase */ + multistate(data, CURLM_STATE_SENDPROTOCONNECT); } break; #endif case CURLM_STATE_WAITCONNECT: - /* awaiting a completion of an asynch connect */ - data->result = Curl_is_connected(data->easy_conn, - FIRSTSOCKET, - &connected); - if(connected) { - - if(!data->result) - /* if everything is still fine we do the protocol-specific connect - setup */ - data->result = Curl_protocol_connect(data->easy_conn, - &protocol_connect); - } - - if(data->easy_conn->bits.proxy_connect_closed) { - /* connect back to proxy again since it was closed in a proxy CONNECT - setup */ - data->result = CURLE_OK; - result = CURLM_CALL_MULTI_PERFORM; - multistate(data, CURLM_STATE_CONNECT); - break; + /* awaiting a completion of an asynch TCP connect */ + result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected); + if(connected && !result) { + rc = CURLM_CALL_MULTI_PERFORM; + multistate(data, data->easy_conn->bits.tunnel_proxy? + CURLM_STATE_WAITPROXYCONNECT: + CURLM_STATE_SENDPROTOCONNECT); } - else if(CURLE_OK != data->result) { + else if(result) { /* failure detected */ /* Just break, the cleaning up is handled all in one place */ disconnect_conn = TRUE; break; } + break; - if(connected) { - if(!protocol_connect) { - /* We have a TCP connection, but 'protocol_connect' may be false - and then we continue to 'STATE_PROTOCONNECT'. If protocol - connect is TRUE, we move on to STATE_DO. - BUT if we are using a proxy we must change to WAITPROXYCONNECT - */ -#ifndef CURL_DISABLE_HTTP - if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) - multistate(data, CURLM_STATE_WAITPROXYCONNECT); - else -#endif - multistate(data, CURLM_STATE_PROTOCONNECT); - - } - else - /* after the connect has completed, go WAITDO or DO */ - multistate(data, multi->pipelining_enabled? - CURLM_STATE_WAITDO:CURLM_STATE_DO); - - result = CURLM_CALL_MULTI_PERFORM; + case CURLM_STATE_SENDPROTOCONNECT: + result = Curl_protocol_connect(data->easy_conn, &protocol_connect); + if(!protocol_connect) + /* switch to waiting state */ + multistate(data, CURLM_STATE_PROTOCONNECT); + else if(!result) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)? + CURLM_STATE_WAITDO:CURLM_STATE_DO); + rc = CURLM_CALL_MULTI_PERFORM; + } + else if(result) { + /* failure detected */ + Curl_posttransfer(data); + Curl_done(&data->easy_conn, result, TRUE); + disconnect_conn = TRUE; } break; case CURLM_STATE_PROTOCONNECT: /* protocol-specific connect phase */ - data->result = Curl_protocol_connecting(data->easy_conn, - &protocol_connect); - if((data->result == CURLE_OK) && protocol_connect) { + result = Curl_protocol_connecting(data->easy_conn, &protocol_connect); + if(!result && protocol_connect) { /* after the connect has completed, go WAITDO or DO */ - multistate(data, multi->pipelining_enabled? + multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)? CURLM_STATE_WAITDO:CURLM_STATE_DO); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } - else if(data->result) { + else if(result) { /* failure detected */ Curl_posttransfer(data); - Curl_done(&data->easy_conn, data->result, TRUE); + Curl_done(&data->easy_conn, result, TRUE); disconnect_conn = TRUE; } break; case CURLM_STATE_WAITDO: /* Wait for our turn to DO when we're pipelining requests */ -#ifdef DEBUGBUILD - infof(data, "WAITDO: Conn %ld send pipe %zu inuse %s athead %s\n", - data->easy_conn->connection_id, - data->easy_conn->send_pipe->size, - data->easy_conn->writechannel_inuse?"TRUE":"FALSE", - isHandleAtHead(data, - data->easy_conn->send_pipe)?"TRUE":"FALSE"); -#endif - if(!data->easy_conn->writechannel_inuse && - isHandleAtHead(data, - data->easy_conn->send_pipe)) { - /* Grab the channel */ - data->easy_conn->writechannel_inuse = TRUE; + if(Curl_pipeline_checkget_write(data, data->easy_conn)) { + /* Grabbed the channel */ multistate(data, CURLM_STATE_DO); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } break; @@ -1279,16 +1297,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* keep connection open for application to use the socket */ connkeep(data->easy_conn, "CONNECT_ONLY"); multistate(data, CURLM_STATE_DONE); - data->result = CURLE_OK; - result = CURLM_CALL_MULTI_PERFORM; + result = CURLE_OK; + rc = CURLM_CALL_MULTI_PERFORM; } else { /* Perform the protocol's DO action */ - data->result = Curl_do(&data->easy_conn, &dophase_done); + result = Curl_do(&data->easy_conn, &dophase_done); /* When Curl_do() returns failure, data->easy_conn might be NULL! */ - if(CURLE_OK == data->result) { + if(!result) { if(!dophase_done) { /* some steps needed for wildcard matching */ if(data->set.wildcardmatch) { @@ -1297,14 +1315,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* skip some states if it is important */ Curl_done(&data->easy_conn, CURLE_OK, FALSE); multistate(data, CURLM_STATE_DONE); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; break; } } /* DO was not completed in one function call, we must continue DOING... */ multistate(data, CURLM_STATE_DOING); - result = CURLM_OK; + rc = CURLM_OK; } /* after DO, go DO_DONE... or DO_MORE */ @@ -1312,15 +1330,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ multistate(data, CURLM_STATE_DO_MORE); - result = CURLM_OK; + rc = CURLM_OK; } else { /* we're done with the DO, now DO_DONE */ multistate(data, CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } } - else if((CURLE_SEND_ERROR == data->result) && + else if((CURLE_SEND_ERROR == result) && data->easy_conn->bits.reuse) { /* * In this situation, a connection that we were trying to use @@ -1335,48 +1353,49 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, drc = Curl_retry_request(data->easy_conn, &newurl); if(drc) { /* a failure here pretty much implies an out of memory */ - data->result = drc; + result = drc; disconnect_conn = TRUE; } else retry = (newurl)?TRUE:FALSE; Curl_posttransfer(data); - drc = Curl_done(&data->easy_conn, data->result, FALSE); + drc = Curl_done(&data->easy_conn, result, FALSE); /* When set to retry the connection, we must to go back to * the CONNECT state */ if(retry) { - if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) { + if(!drc || (drc == CURLE_SEND_ERROR)) { follow = FOLLOW_RETRY; drc = Curl_follow(data, newurl, follow); - if(drc == CURLE_OK) { + if(!drc) { multistate(data, CURLM_STATE_CONNECT); - result = CURLM_CALL_MULTI_PERFORM; - data->result = CURLE_OK; + rc = CURLM_CALL_MULTI_PERFORM; + result = CURLE_OK; } else { /* Follow failed */ - data->result = drc; + result = drc; free(newurl); } } else { /* done didn't return OK or SEND_ERROR */ - data->result = drc; + result = drc; free(newurl); } } else { /* Have error handler disconnect conn if we can't retry */ disconnect_conn = TRUE; + free(newurl); } } else { /* failure detected */ Curl_posttransfer(data); if(data->easy_conn) - Curl_done(&data->easy_conn, data->result, FALSE); + Curl_done(&data->easy_conn, result, FALSE); disconnect_conn = TRUE; } } @@ -1384,21 +1403,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DOING: /* we continue DOING until the DO phase is complete */ - data->result = Curl_protocol_doing(data->easy_conn, - &dophase_done); - if(CURLE_OK == data->result) { + result = Curl_protocol_doing(data->easy_conn, + &dophase_done); + if(!result) { if(dophase_done) { /* after DO, go DO_DONE or DO_MORE */ multistate(data, data->easy_conn->bits.do_more? CURLM_STATE_DO_MORE: CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } /* dophase_done */ } else { /* failure detected */ Curl_posttransfer(data); - Curl_done(&data->easy_conn, data->result, FALSE); + Curl_done(&data->easy_conn, result, FALSE); disconnect_conn = TRUE; } break; @@ -1407,27 +1426,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* * When we are connected, DO MORE and then go DO_DONE */ - data->result = Curl_do_more(data->easy_conn, &control); + result = Curl_do_more(data->easy_conn, &control); /* No need to remove this handle from the send pipeline here since that is done in Curl_done() */ - if(CURLE_OK == data->result) { + if(!result) { if(control) { /* if positive, advance to DO_DONE if negative, go back to DOING */ multistate(data, control==1? CURLM_STATE_DO_DONE: CURLM_STATE_DOING); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } else /* stay in DO_MORE */ - result = CURLM_OK; + rc = CURLM_OK; } else { /* failure detected */ Curl_posttransfer(data); - Curl_done(&data->easy_conn, data->result, FALSE); + Curl_done(&data->easy_conn, result, FALSE); disconnect_conn = TRUE; } break; @@ -1445,37 +1464,24 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(data, CURLM_STATE_WAITPERFORM); else multistate(data, CURLM_STATE_DONE); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; break; case CURLM_STATE_WAITPERFORM: /* Wait for our turn to PERFORM */ - if(!data->easy_conn->readchannel_inuse && - isHandleAtHead(data, - data->easy_conn->recv_pipe)) { - /* Grab the channel */ - data->easy_conn->readchannel_inuse = TRUE; + if(Curl_pipeline_checkget_read(data, data->easy_conn)) { + /* Grabbed the channel */ multistate(data, CURLM_STATE_PERFORM); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } -#ifdef DEBUGBUILD - else { - infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %s athead %s\n", - data->easy_conn->connection_id, - data->easy_conn->recv_pipe->size, - data->easy_conn->readchannel_inuse?"TRUE":"FALSE", - isHandleAtHead(data, - data->easy_conn->recv_pipe)?"TRUE":"FALSE"); - } -#endif break; case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */ /* if both rates are within spec, resume transfer */ if(Curl_pgrsUpdate(data->easy_conn)) - data->result = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; else - data->result = Curl_speedcheck(data, now); + result = Curl_speedcheck(data, now); if(( (data->set.max_send_speed == 0) || (data->progress.ulspeed < data->set.max_send_speed )) && @@ -1485,7 +1491,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_PERFORM: - { + { char *newurl = NULL; bool retry = FALSE; @@ -1512,7 +1518,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(data, CURLM_STATE_TOOFAST); - /* Calculate download rate-limitation timeout. */ + /* Calculate download rate-limitation timeout. */ buffersize = (int)(data->set.buffer_size ? data->set.buffer_size : BUFSIZE); timeout_ms = Curl_sleep_time(data->set.max_recv_speed, @@ -1522,21 +1528,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* read/write data if it is ready to do so */ - data->result = Curl_readwrite(data->easy_conn, &done); + result = Curl_readwrite(data->easy_conn, data, &done); k = &data->req; - if(!(k->keepon & KEEP_RECV)) { + if(!(k->keepon & KEEP_RECV)) /* We're done receiving */ - data->easy_conn->readchannel_inuse = FALSE; - } + Curl_pipeline_leave_read(data->easy_conn); - if(!(k->keepon & KEEP_SEND)) { + if(!(k->keepon & KEEP_SEND)) /* We're done sending */ - data->easy_conn->writechannel_inuse = FALSE; - } + Curl_pipeline_leave_write(data->easy_conn); - if(done || (data->result == CURLE_RECV_ERROR)) { + if(done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race * condition and the server closed the re-used connection exactly when * we wanted to use it, so figure out if that is indeed the case. @@ -1548,12 +1552,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(retry) { /* if we are to retry, set the result to OK and consider the request as done */ - data->result = CURLE_OK; + result = CURLE_OK; done = TRUE; } } - if(data->result) { + if(result) { /* * The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is because we can't possibly @@ -1566,7 +1570,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, connclose(data->easy_conn, "Transfer returned error"); Curl_posttransfer(data); - Curl_done(&data->easy_conn, data->result, FALSE); + Curl_done(&data->easy_conn, result, FALSE); } else if(done) { followtype follow=FOLLOW_NONE; @@ -1590,18 +1594,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!retry) { /* if the URL is a follow-location and not just a retried request then figure out the URL here */ + free(newurl); newurl = data->req.newurl; data->req.newurl = NULL; follow = FOLLOW_REDIR; } else follow = FOLLOW_RETRY; - data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE); - if(CURLE_OK == data->result) { - data->result = Curl_follow(data, newurl, follow); - if(CURLE_OK == data->result) { + result = Curl_done(&data->easy_conn, CURLE_OK, FALSE); + if(!result) { + result = Curl_follow(data, newurl, follow); + if(!result) { multistate(data, CURLM_STATE_CONNECT); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; newurl = NULL; /* handed over the memory ownership to Curl_follow(), make sure we don't free() it here */ @@ -1614,30 +1619,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* but first check to see if we got a location info even though we're not following redirects */ if(data->req.location) { - if(newurl) - free(newurl); + free(newurl); newurl = data->req.location; data->req.location = NULL; - data->result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(CURLE_OK == data->result) + result = Curl_follow(data, newurl, FOLLOW_FAKE); + if(!result) newurl = NULL; /* allocation was handed over Curl_follow() */ else disconnect_conn = TRUE; } multistate(data, CURLM_STATE_DONE); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } } - if(newurl) - free(newurl); + free(newurl); break; - } + } case CURLM_STATE_DONE: /* this state is highly transient, so run another loop after this */ - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; if(data->easy_conn) { CURLcode res; @@ -1648,11 +1651,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_multi_process_pending_handles(multi); /* post-transfer command */ - res = Curl_done(&data->easy_conn, data->result, FALSE); + res = Curl_done(&data->easy_conn, result, FALSE); /* allow a previously set error code take precedence */ - if(!data->result) - data->result = res; + if(!result) + result = res; /* * If there are other handles on the pipeline, Curl_done won't set @@ -1692,14 +1695,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_MSGSENT: + data->result = result; return CURLM_OK; /* do nothing */ default: return CURLM_INTERNAL_ERROR; } + statemachine_end: if(data->mstate < CURLM_STATE_COMPLETED) { - if(CURLE_OK != data->result) { + if(result) { /* * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. @@ -1710,20 +1715,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->state.pipe_broke = FALSE; + /* Check if we can move pending requests to send pipe */ + Curl_multi_process_pending_handles(multi); + if(data->easy_conn) { /* if this has a connection, unsubscribe from the pipelines */ - data->easy_conn->writechannel_inuse = FALSE; - data->easy_conn->readchannel_inuse = FALSE; - Curl_removeHandleFromPipeline(data, - data->easy_conn->send_pipe); - Curl_removeHandleFromPipeline(data, - data->easy_conn->recv_pipe); - /* Check if we can move pending requests to send pipe */ - Curl_multi_process_pending_handles(multi); + Curl_pipeline_leave_write(data->easy_conn); + Curl_pipeline_leave_read(data->easy_conn); + Curl_removeHandleFromPipeline(data, data->easy_conn->send_pipe); + Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe); if(disconnect_conn) { + /* Don't attempt to send data over a connection that timed out */ + bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; /* disconnect properly */ - Curl_disconnect(data->easy_conn, /* dead_connection */ FALSE); + Curl_disconnect(data->easy_conn, dead_connection); /* This is where we make sure that the easy_conn pointer is reset. We don't have to do this in every case block above where a @@ -1742,31 +1748,34 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) { /* aborted due to progress callback return code must close the connection */ - data->result = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; connclose(data->easy_conn, "Aborted by callback"); /* if not yet in DONE state, go there, otherwise COMPLETED */ multistate(data, (data->mstate < CURLM_STATE_DONE)? CURLM_STATE_DONE: CURLM_STATE_COMPLETED); - result = CURLM_CALL_MULTI_PERFORM; + rc = CURLM_CALL_MULTI_PERFORM; } } - } WHILE_FALSE; /* just to break out from! */ - if(CURLM_STATE_COMPLETED == data->mstate) { - /* now fill in the Curl_message with this info */ - msg = &data->msg; + if(CURLM_STATE_COMPLETED == data->mstate) { + /* now fill in the Curl_message with this info */ + msg = &data->msg; - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = data; - msg->extmsg.data.result = data->result; + msg->extmsg.msg = CURLMSG_DONE; + msg->extmsg.easy_handle = data; + msg->extmsg.data.result = result; - result = multi_addmsg(multi, msg); + rc = multi_addmsg(multi, msg); - multistate(data, CURLM_STATE_MSGSENT); - } + multistate(data, CURLM_STATE_MSGSENT); + } + } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); - return result; + data->result = result; + + + return rc; } @@ -1796,9 +1805,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) } sigpipe_ignore(data, &pipe_st); - do - result = multi_runsingle(multi, now, data); - while(CURLM_CALL_MULTI_PERFORM == result); + result = multi_runsingle(multi, now, data); sigpipe_restore(&pipe_st); if(data->set.wildcardmatch) { @@ -1843,7 +1850,7 @@ static void close_all_connections(struct Curl_multi *multi) { struct connectdata *conn; - conn = Curl_conncache_find_first_connection(multi->conn_cache); + conn = Curl_conncache_find_first_connection(&multi->conn_cache); while(conn) { SIGPIPE_VARIABLE(pipe_st); conn->data = multi->closure_handle; @@ -1853,7 +1860,7 @@ static void close_all_connections(struct Curl_multi *multi) (void)Curl_disconnect(conn, FALSE); sigpipe_restore(&pipe_st); - conn = Curl_conncache_find_first_connection(multi->conn_cache); + conn = Curl_conncache_find_first_connection(&multi->conn_cache); } } @@ -1876,15 +1883,15 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) sigpipe_ignore(multi->closure_handle, &pipe_st); restore_pipe = TRUE; - multi->closure_handle->dns.hostcache = multi->hostcache; + multi->closure_handle->dns.hostcache = &multi->hostcache; Curl_hostcache_clean(multi->closure_handle, multi->closure_handle->dns.hostcache); Curl_close(multi->closure_handle); } - Curl_hash_destroy(multi->sockhash); - Curl_conncache_destroy(multi->conn_cache); + Curl_hash_destroy(&multi->sockhash); + Curl_conncache_destroy(&multi->conn_cache); Curl_llist_destroy(multi->msglist, NULL); Curl_llist_destroy(multi->pending, NULL); @@ -1906,7 +1913,7 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle) data = nextdata; } - Curl_hash_destroy(multi->hostcache); + Curl_hash_destroy(&multi->hostcache); /* Free the blacklists by setting them to NULL */ Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl); @@ -1995,7 +2002,7 @@ static void singlesocket(struct Curl_multi *multi, s = socks[i]; /* get it from the hash */ - entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + entry = Curl_hash_pick(&multi->sockhash, (char *)&s, sizeof(s)); if(curraction & GETSOCK_READSOCK(i)) action |= CURL_POLL_IN; @@ -2010,7 +2017,7 @@ static void singlesocket(struct Curl_multi *multi, } else { /* this is a socket we didn't have before, add it! */ - entry = sh_addentry(multi->sockhash, s, data); + entry = sh_addentry(&multi->sockhash, s, data); if(!entry) /* fatal */ return; @@ -2046,7 +2053,7 @@ static void singlesocket(struct Curl_multi *multi, /* this socket has been removed. Tell the app to remove it */ remove_sock_from_hash = TRUE; - entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + entry = Curl_hash_pick(&multi->sockhash, (char *)&s, sizeof(s)); if(entry) { /* check if the socket to be removed serves a connection which has other easy-s in a pipeline. In this case the socket should not be @@ -2061,7 +2068,7 @@ static void singlesocket(struct Curl_multi *multi, for the recv_pipe, or the first (in case this particular easy isn't already) */ if(entry->easy == data) { - if(isHandleAtHead(data, easy_conn->recv_pipe)) + if(Curl_recvpipe_head(data, easy_conn)) entry->easy = easy_conn->recv_pipe->head->next->ptr; else entry->easy = easy_conn->recv_pipe->head->ptr; @@ -2075,7 +2082,7 @@ static void singlesocket(struct Curl_multi *multi, for the send_pipe, or the first (in case this particular easy isn't already) */ if(entry->easy == data) { - if(isHandleAtHead(data, easy_conn->send_pipe)) + if(Curl_sendpipe_head(data, easy_conn)) entry->easy = easy_conn->send_pipe->head->next->ptr; else entry->easy = easy_conn->send_pipe->head->ptr; @@ -2101,7 +2108,7 @@ static void singlesocket(struct Curl_multi *multi, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); - sh_delentry(multi->sockhash, s); + sh_delentry(&multi->sockhash, s); } } @@ -2115,7 +2122,7 @@ static void singlesocket(struct Curl_multi *multi, * Curl_multi_closed() * * Used by the connect code to tell the multi_socket code that one of the - * sockets we were using have just been closed. This function will then + * sockets we were using is about to be closed. This function will then * remove it from the sockethash for this handle to make the multi_socket API * behave properly, especially for the case when libcurl will create another * socket again and it gets the same file descriptor number. @@ -2128,7 +2135,7 @@ void Curl_multi_closed(struct connectdata *conn, curl_socket_t s) /* this is set if this connection is part of a handle that is added to a multi handle, and only then this is necessary */ struct Curl_sh_entry *entry = - Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + Curl_hash_pick(&multi->sockhash, (char *)&s, sizeof(s)); if(entry) { if(multi->socket_cb) @@ -2137,7 +2144,7 @@ void Curl_multi_closed(struct connectdata *conn, curl_socket_t s) entry->socketp); /* now remove it from the socket hash */ - sh_delentry(multi->sockhash, s); + sh_delentry(&multi->sockhash, s); } } } @@ -2230,7 +2237,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, else if(s != CURL_SOCKET_TIMEOUT) { struct Curl_sh_entry *entry = - Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + Curl_hash_pick(&multi->sockhash, (char *)&s, sizeof(s)); if(!entry) /* Unmatched socket, we can't act on it but we ignore this fact. In @@ -2268,9 +2275,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data->easy_conn->cselect_bits = ev_bitmask; sigpipe_ignore(data, &pipe_st); - do - result = multi_runsingle(multi, now, data); - while(CURLM_CALL_MULTI_PERFORM == result); + result = multi_runsingle(multi, now, data); sigpipe_restore(&pipe_st); if(data->easy_conn && @@ -2312,9 +2317,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, SIGPIPE_VARIABLE(pipe_st); sigpipe_ignore(data, &pipe_st); - do - result = multi_runsingle(multi, now, data); - while(CURLM_CALL_MULTI_PERFORM == result); + result = multi_runsingle(multi, now, data); sigpipe_restore(&pipe_st); if(CURLM_OK >= result) @@ -2358,8 +2361,14 @@ CURLMcode curl_multi_setopt(CURLM *multi_handle, case CURLMOPT_SOCKETDATA: multi->socket_userp = va_arg(param, void *); break; + case CURLMOPT_PUSHFUNCTION: + multi->push_cb = va_arg(param, curl_push_callback); + break; + case CURLMOPT_PUSHDATA: + multi->push_userp = va_arg(param, void *); + break; case CURLMOPT_PIPELINING: - multi->pipelining_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE; + multi->pipelining = va_arg(param, long); break; case CURLMOPT_TIMERFUNCTION: multi->timer_cb = va_arg(param, curl_multi_timer_callback); @@ -2437,7 +2446,7 @@ CURLMcode curl_multi_socket_all(CURLM *multi_handle, int *running_handles) static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms) { - static struct timeval tv_zero = {0,0}; + static struct timeval tv_zero = {0, 0}; if(multi->timetree) { /* we have a tree of expire times */ @@ -2495,7 +2504,7 @@ static int update_timer(struct Curl_multi *multi) return -1; } if(timeout_ms < 0) { - static const struct timeval none={0,0}; + static const struct timeval none={0, 0}; if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { multi->timer_lastcall = none; /* there's no timeout now but there was one previously, tell the app to @@ -2517,22 +2526,6 @@ static int update_timer(struct Curl_multi *multi) return multi->timer_cb((CURLM*)multi, timeout_ms, multi->timer_userp); } -void Curl_multi_set_easy_connection(struct SessionHandle *handle, - struct connectdata *conn) -{ - handle->easy_conn = conn; -} - -static bool isHandleAtHead(struct SessionHandle *handle, - struct curl_llist *pipeline) -{ - struct curl_llist_element *curr = pipeline->head; - if(curr) - return (curr->ptr == handle) ? TRUE : FALSE; - - return FALSE; -} - /* * multi_freetimeout() * @@ -2698,24 +2691,24 @@ void Curl_expire(struct SessionHandle *data, long milli) */ void Curl_expire_latest(struct SessionHandle *data, long milli) { - struct timeval *exp = &data->state.expiretime; + struct timeval *expire = &data->state.expiretime; struct timeval set; set = Curl_tvnow(); - set.tv_sec += milli/1000; - set.tv_usec += (milli%1000)*1000; + set.tv_sec += milli / 1000; + set.tv_usec += (milli % 1000) * 1000; if(set.tv_usec >= 1000000) { set.tv_sec++; set.tv_usec -= 1000000; } - if(exp->tv_sec || exp->tv_usec) { + if(expire->tv_sec || expire->tv_usec) { /* This means that the struct is added as a node in the splay tree. Compare if the new time is earlier, and only remove-old/add-new if it is. */ - long diff = curlx_tvdiff(set, *exp); + long diff = curlx_tvdiff(set, *expire); if(diff > 0) /* the new expire time was later than the top time, so just skip this */ return; @@ -2732,7 +2725,8 @@ CURLMcode curl_multi_assign(CURLM *multi_handle, struct Curl_multi *multi = (struct Curl_multi *)multi_handle; if(s != CURL_SOCKET_BAD) - there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t)); + there = Curl_hash_pick(&multi->sockhash, (char *)&s, + sizeof(curl_socket_t)); if(!there) return CURLM_BAD_SOCKET; @@ -2752,11 +2746,6 @@ size_t Curl_multi_max_total_connections(struct Curl_multi *multi) return multi ? multi->max_total_connections : 0; } -size_t Curl_multi_max_pipeline_length(struct Curl_multi *multi) -{ - return multi ? multi->max_pipeline_length : 0; -} - curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi) { return multi ? multi->content_length_penalty_size : 0; @@ -2816,7 +2805,7 @@ void Curl_multi_dump(const struct Curl_multi *multi_handle) for(i=0; i < data->numsocks; i++) { curl_socket_t s = data->sockets[i]; struct Curl_sh_entry *entry = - Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s)); + Curl_hash_pick(&multi->sockhash, (char *)&s, sizeof(s)); fprintf(stderr, "%d ", (int)s); if(!entry) { diff --git a/lib/multihandle.h b/lib/multihandle.h index 1a4b1d9..6c24f50 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,6 +22,8 @@ * ***************************************************************************/ +#include "conncache.h" + struct Curl_message { /* the 'CURLMsg' is the part that is visible to the external user */ struct CURLMsg extmsg; @@ -35,22 +37,23 @@ typedef enum { CURLM_STATE_CONNECT_PEND, /* 1 - no connections, waiting for one */ CURLM_STATE_CONNECT, /* 2 - resolve/connect has been sent off */ CURLM_STATE_WAITRESOLVE, /* 3 - awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* 4 - awaiting the connect to finalize */ + CURLM_STATE_WAITCONNECT, /* 4 - awaiting the TCP connect to finalize */ CURLM_STATE_WAITPROXYCONNECT, /* 5 - awaiting proxy CONNECT to finalize */ - CURLM_STATE_PROTOCONNECT, /* 6 - completing the protocol-specific connect + CURLM_STATE_SENDPROTOCONNECT, /* 6 - initiate protocol connect procedure */ + CURLM_STATE_PROTOCONNECT, /* 7 - completing the protocol-specific connect phase */ - CURLM_STATE_WAITDO, /* 7 - wait for our turn to send the request */ - CURLM_STATE_DO, /* 8 - start send off the request (part 1) */ - CURLM_STATE_DOING, /* 9 - sending off the request (part 1) */ - CURLM_STATE_DO_MORE, /* 10 - send off the request (part 2) */ - CURLM_STATE_DO_DONE, /* 11 - done sending off request */ - CURLM_STATE_WAITPERFORM, /* 12 - wait for our turn to read the response */ - CURLM_STATE_PERFORM, /* 13 - transfer data */ - CURLM_STATE_TOOFAST, /* 14 - wait because limit-rate exceeded */ - CURLM_STATE_DONE, /* 15 - post data transfer operation */ - CURLM_STATE_COMPLETED, /* 16 - operation complete */ - CURLM_STATE_MSGSENT, /* 17 - the operation complete message is sent */ - CURLM_STATE_LAST /* 18 - not a true state, never use this */ + CURLM_STATE_WAITDO, /* 8 - wait for our turn to send the request */ + CURLM_STATE_DO, /* 9 - start send off the request (part 1) */ + CURLM_STATE_DOING, /* 10 - sending off the request (part 1) */ + CURLM_STATE_DO_MORE, /* 11 - send off the request (part 2) */ + CURLM_STATE_DO_DONE, /* 12 - done sending off request */ + CURLM_STATE_WAITPERFORM, /* 13 - wait for our turn to read the response */ + CURLM_STATE_PERFORM, /* 14 - transfer data */ + CURLM_STATE_TOOFAST, /* 15 - wait because limit-rate exceeded */ + CURLM_STATE_DONE, /* 16 - post data transfer operation */ + CURLM_STATE_COMPLETED, /* 17 - operation complete */ + CURLM_STATE_MSGSENT, /* 18 - the operation complete message is sent */ + CURLM_STATE_LAST /* 19 - not a true state, never use this */ } CURLMstate; /* we support N sockets per easy handle. Set the corresponding bit to what @@ -59,6 +62,8 @@ typedef enum { #define GETSOCK_READABLE (0x00ff) #define GETSOCK_WRITABLE (0xff00) +#define CURLPIPE_ANY (CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX) + /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up @@ -82,8 +87,12 @@ struct Curl_multi { curl_socket_callback socket_cb; void *socket_userp; + /* callback function and user data pointer for server push */ + curl_push_callback push_cb; + void *push_userp; + /* Hostname cache */ - struct curl_hash *hostcache; + struct curl_hash hostcache; /* timetree points to the splay-tree of time nodes to figure out expire times of all currently set timers */ @@ -92,13 +101,15 @@ struct Curl_multi { /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note the pluralis form, there can be more than one easy handle waiting on the same actual socket) */ - struct curl_hash *sockhash; + struct curl_hash sockhash; - /* Whether pipelining is enabled for this multi handle */ - bool pipelining_enabled; + /* pipelining wanted bits (CURLPIPE*) */ + long pipelining; + + bool recheckstate; /* see Curl_multi_connchanged */ /* Shared connection cache (bundles)*/ - struct conncache *conn_cache; + struct conncache conn_cache; /* This handle will be used for closing the cached connections in curl_multi_cleanup() */ @@ -139,4 +150,3 @@ struct Curl_multi { }; #endif /* HEADER_CURL_MULTIHANDLE_H */ - diff --git a/lib/multiif.h b/lib/multiif.h index c77b3ca..e6323ad 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,8 +27,7 @@ */ void Curl_expire(struct SessionHandle *data, long milli); void Curl_expire_latest(struct SessionHandle *data, long milli); - -bool Curl_multi_pipeline_enabled(const struct Curl_multi* multi); +bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits); void Curl_multi_handlePipeBreak(struct SessionHandle *data); /* Internal version of curl_multi_init() accepts size parameters for the @@ -55,18 +54,11 @@ struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize); void Curl_multi_dump(const struct Curl_multi *multi_handle); #endif -/* Update the current connection of a One_Easy handle */ -void Curl_multi_set_easy_connection(struct SessionHandle *handle, - struct connectdata *conn); - void Curl_multi_process_pending_handles(struct Curl_multi *multi); /* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */ size_t Curl_multi_max_host_connections(struct Curl_multi *multi); -/* Return the value of the CURLMOPT_MAX_PIPELINE_LENGTH option */ -size_t Curl_multi_max_pipeline_length(struct Curl_multi *multi); - /* Return the value of the CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE option */ curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi); @@ -82,11 +74,13 @@ struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi); /* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */ size_t Curl_multi_max_total_connections(struct Curl_multi *multi); +void Curl_multi_connchanged(struct Curl_multi *multi); + /* * Curl_multi_closed() * * Used by the connect code to tell the multi_socket code that one of the - * sockets we were using have just been closed. This function will then + * sockets we were using is about to be closed. This function will then * remove it from the sockethash for this handle to make the multi_socket API * behave properly, especially for the case when libcurl will create another * socket again and it gets the same file descriptor number. @@ -94,4 +88,10 @@ size_t Curl_multi_max_total_connections(struct Curl_multi *multi); void Curl_multi_closed(struct connectdata *conn, curl_socket_t s); +/* + * Add a handle and move it into PERFORM state at once. For pushed streams. + */ +CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, + struct SessionHandle *data, + struct connectdata *conn); #endif /* HEADER_CURL_MULTIIF_H */ diff --git a/lib/netrc.c b/lib/netrc.c index 7435d94..06f8ea1 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,13 +31,11 @@ #include "strequal.h" #include "strtok.h" -#include "curl_memory.h" #include "rawstr.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* Get user and password from .netrc when given a machine name */ @@ -104,16 +102,16 @@ int Curl_parsenetrc(const char *host, netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC); if(home_alloc) - Curl_safefree(home); + free(home); if(!netrcfile) { return -1; } netrc_alloc = TRUE; } - file = fopen(netrcfile, "r"); + file = fopen(netrcfile, FOPEN_READTEXT); if(netrc_alloc) - Curl_safefree(netrcfile); + free(netrcfile); if(file) { char *tok; char *tok_buf; @@ -139,6 +137,10 @@ int Curl_parsenetrc(const char *host, 'password'. */ state=HOSTFOUND; } + else if(Curl_raw_equal("default", tok)) { + state=HOSTVALID; + retcode=0; /* we did find our host */ + } break; case HOSTFOUND: if(Curl_raw_equal(host, tok)) { diff --git a/lib/non-ascii.c b/lib/non-ascii.c index 91d6a54..6ccb449 100644 --- a/lib/non-ascii.c +++ b/lib/non-ascii.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -82,17 +82,16 @@ CURLcode Curl_convert_clone(struct SessionHandle *data, CURLcode Curl_convert_to_network(struct SessionHandle *data, char *buffer, size_t length) { - CURLcode rc; - if(data->set.convtonetwork) { /* use translation callback */ - rc = data->set.convtonetwork(buffer, length); - if(rc != CURLE_OK) { + CURLcode result = data->set.convtonetwork(buffer, length); + if(result) { failf(data, "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s", - (int)rc, curl_easy_strerror(rc)); + (int)result, curl_easy_strerror(result)); } - return rc; + + return result; } else { #ifdef HAVE_ICONV @@ -143,17 +142,16 @@ CURLcode Curl_convert_to_network(struct SessionHandle *data, CURLcode Curl_convert_from_network(struct SessionHandle *data, char *buffer, size_t length) { - CURLcode rc; - if(data->set.convfromnetwork) { /* use translation callback */ - rc = data->set.convfromnetwork(buffer, length); - if(rc != CURLE_OK) { + CURLcode result = data->set.convfromnetwork(buffer, length); + if(result) { failf(data, "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s", - (int)rc, curl_easy_strerror(rc)); + (int)result, curl_easy_strerror(result)); } - return rc; + + return result; } else { #ifdef HAVE_ICONV @@ -204,17 +202,16 @@ CURLcode Curl_convert_from_network(struct SessionHandle *data, CURLcode Curl_convert_from_utf8(struct SessionHandle *data, char *buffer, size_t length) { - CURLcode rc; - if(data->set.convfromutf8) { /* use translation callback */ - rc = data->set.convfromutf8(buffer, length); - if(rc != CURLE_OK) { + CURLcode result = data->set.convfromutf8(buffer, length); + if(result) { failf(data, "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s", - (int)rc, curl_easy_strerror(rc)); + (int)result, curl_easy_strerror(result)); } - return rc; + + return result; } else { #ifdef HAVE_ICONV @@ -319,24 +316,22 @@ void Curl_convert_close(struct SessionHandle *data) */ CURLcode Curl_convert_form(struct SessionHandle *data, struct FormData *form) { - struct FormData *next; - CURLcode rc; - - if(!form) - return CURLE_OK; + CURLcode result; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; - do { - next=form->next; /* the following form line */ + while(form) { if(form->type == FORM_DATA) { - rc = Curl_convert_to_network(data, form->line, form->length); + result = Curl_convert_to_network(data, form->line, form->length); /* Curl_convert_to_network calls failf if unsuccessful */ - if(rc != CURLE_OK) - return rc; + if(result) + return result; } - } while((form = next) != NULL); /* continue */ + + form = form->next; + } + return CURLE_OK; } diff --git a/lib/nwlib.c b/lib/nwlib.c index 252bf11..bd3f27e 100644 --- a/lib/nwlib.c +++ b/lib/nwlib.c @@ -282,9 +282,7 @@ int DisposeLibraryData( void *data ) if(data) { void *tenbytes = ((libdata_t *) data)->tenbytes; - if(tenbytes) - free(tenbytes); - + free(tenbytes); free(data); } @@ -296,9 +294,7 @@ void DisposeThreadData( void *data ) if(data) { void *twentybytes = ((libthreaddata_t *) data)->twentybytes; - if(twentybytes) - free(twentybytes); - + free(twentybytes); free(data); } } diff --git a/lib/openldap.c b/lib/openldap.c index df8d938..bee552f 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010, 2013, Howard Chu, <hyc@openldap.org> - * Copyright (C) 2011 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010, Howard Chu, <hyc@openldap.org> + * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -44,13 +44,12 @@ #include "vtls/vtls.h" #include "transfer.h" #include "curl_ldap.h" -#include "curl_memory.h" #include "curl_base64.h" #include "connect.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #ifndef _LDAP_PVT_H @@ -59,7 +58,7 @@ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); #endif -static CURLcode ldap_setup(struct connectdata *conn); +static CURLcode ldap_setup_connection(struct connectdata *conn); static CURLcode ldap_do(struct connectdata *conn, bool *done); static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool); static CURLcode ldap_connect(struct connectdata *conn, bool *done); @@ -74,7 +73,7 @@ static Curl_recv ldap_recv; const struct Curl_handler Curl_handler_ldap = { "LDAP", /* scheme */ - ldap_setup, /* setup_connection */ + ldap_setup_connection, /* setup_connection */ ldap_do, /* do_it */ ldap_done, /* done */ ZERO_NULL, /* do_more */ @@ -99,7 +98,7 @@ const struct Curl_handler Curl_handler_ldap = { const struct Curl_handler Curl_handler_ldaps = { "LDAPS", /* scheme */ - ldap_setup, /* setup_connection */ + ldap_setup_connection, /* setup_connection */ ldap_do, /* do_it */ ldap_done, /* done */ ZERO_NULL, /* do_more */ @@ -148,7 +147,7 @@ typedef struct ldapreqinfo { int nument; } ldapreqinfo; -static CURLcode ldap_setup(struct connectdata *conn) +static CURLcode ldap_setup_connection(struct connectdata *conn) { ldapconninfo *li; LDAPURLDesc *lud; @@ -190,9 +189,11 @@ static Sockbuf_IO ldapsb_tls; static CURLcode ldap_connect(struct connectdata *conn, bool *done) { ldapconninfo *li = conn->proto.generic; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; int rc, proto = LDAP_VERSION3; - char hosturl[1024], *ptr; + char hosturl[1024]; + char *ptr; + (void)done; strcpy(hosturl, "ldap"); @@ -213,10 +214,10 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done) #ifdef USE_SSL if(conn->handler->flags & PROTOPT_SSL) { - CURLcode res; - res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone); - if(res) - return res; + CURLcode result; + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &li->ssldone); + if(result) + return result; } #endif @@ -226,9 +227,9 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done) static CURLcode ldap_connecting(struct connectdata *conn, bool *done) { ldapconninfo *li = conn->proto.generic; - struct SessionHandle *data=conn->data; - LDAPMessage *result = NULL; - struct timeval tv = {0,1}, *tvp; + struct SessionHandle *data = conn->data; + LDAPMessage *msg = NULL; + struct timeval tv = {0, 1}, *tvp; int rc, err; char *info = NULL; @@ -236,11 +237,12 @@ static CURLcode ldap_connecting(struct connectdata *conn, bool *done) if(conn->handler->flags & PROTOPT_SSL) { /* Is the SSL handshake complete yet? */ if(!li->ssldone) { - CURLcode res = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, - &li->ssldone); - if(res || !li->ssldone) - return res; + CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, + &li->ssldone); + if(result || !li->ssldone) + return result; } + /* Have we installed the libcurl SSL handlers into the sockbuf yet? */ if(!li->sslinst) { Sockbuf *sb; @@ -279,7 +281,7 @@ retry: return CURLE_OK; } - rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &result); + rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg); if(rc < 0) { failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc)); return CURLE_LDAP_CANNOT_BIND; @@ -288,11 +290,13 @@ retry: /* timed out */ return CURLE_OK; } - rc = ldap_parse_result(li->ld, result, &err, NULL, &info, NULL, NULL, 1); + + rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1); if(rc) { failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc)); return CURLE_LDAP_CANNOT_BIND; } + /* Try to fallback to LDAPv2? */ if(err == LDAP_PROTOCOL_ERROR) { int proto; @@ -321,6 +325,7 @@ retry: ldap_memfree(info); conn->recv[FIRSTSOCKET] = ldap_recv; *done = TRUE; + return CURLE_OK; } @@ -375,7 +380,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done) failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); return CURLE_LDAP_SEARCH_FAILED; } - lr = calloc(1,sizeof(ldapreqinfo)); + lr = calloc(1, sizeof(ldapreqinfo)); if(!lr) return CURLE_OUT_OF_MEMORY; lr->msgid = msgid; @@ -389,6 +394,7 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res, bool premature) { ldapreqinfo *lr = conn->data->req.protop; + (void)res; (void)premature; @@ -402,6 +408,7 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res, conn->data->req.protop = NULL; free(lr); } + return CURLE_OK; } @@ -409,18 +416,19 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err) { ldapconninfo *li = conn->proto.generic; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; ldapreqinfo *lr = data->req.protop; int rc, ret; - LDAPMessage *result = NULL; + LDAPMessage *msg = NULL; LDAPMessage *ent; BerElement *ber = NULL; - struct timeval tv = {0,1}; + struct timeval tv = {0, 1}; + (void)len; (void)buf; (void)sockindex; - rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &result); + rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg); if(rc < 0) { failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); *err = CURLE_RECV_ERROR; @@ -431,10 +439,10 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, ret = -1; /* timed out */ - if(result == NULL) + if(!msg) return ret; - for(ent = ldap_first_message(li->ld, result); ent; + for(ent = ldap_first_message(li->ld, msg); ent; ent = ldap_next_message(li->ld, ent)) { struct berval bv, *bvals, **bvp = &bvals; int binary = 0, msgtype; @@ -477,9 +485,18 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, *err = CURLE_RECV_ERROR; return -1; } - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, bv.bv_len); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"DN: ", 4); + if(*err) + return -1; + + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, + bv.bv_len); + if(*err) + return -1; + + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 1); + if(*err) + return -1; data->req.bytecount += bv.bv_len + 5; for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, bvp); @@ -496,10 +513,18 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, for(i=0; bvals[i].bv_val != NULL; i++) { int binval = 0; - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, - bv.bv_len); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\t", 1); + if(*err) + return -1; + + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)bv.bv_val, + bv.bv_len); + if(*err) + return -1; + + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)":", 1); + if(*err) + return -1; data->req.bytecount += bv.bv_len + 2; if(!binary) { @@ -529,36 +554,55 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf, if(error) { ber_memfree(bvals); ber_free(ber, 0); - ldap_msgfree(result); + ldap_msgfree(msg); *err = error; return -1; } - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)": ", 2); + if(*err) + return -1; + data->req.bytecount += 2; if(val_b64_sz > 0) { - Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, val_b64_sz); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, val_b64, + val_b64_sz); + if(*err) + return -1; free(val_b64); data->req.bytecount += val_b64_sz; } } else { - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1); - Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val, - bvals[i].bv_len); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)" ", 1); + if(*err) + return -1; + + *err = Curl_client_write(conn, CLIENTWRITE_BODY, bvals[i].bv_val, + bvals[i].bv_len); + if(*err) + return -1; + data->req.bytecount += bvals[i].bv_len + 1; } - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + if(*err) + return -1; + data->req.bytecount++; } ber_memfree(bvals); - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + if(*err) + return -1; data->req.bytecount++; } - Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + *err = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)"\n", 0); + if(*err) + return -1; data->req.bytecount++; ber_free(ber, 0); } - ldap_msgfree(result); + ldap_msgfree(msg); return ret; } diff --git a/lib/parsedate.c b/lib/parsedate.c index ecb8dfb..3e168f5 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -545,7 +545,7 @@ static int parsedate(const char *date, time_t *output) time_t curl_getdate(const char *p, const time_t *now) { - time_t parsed; + time_t parsed = -1; int rc = parsedate(p, &parsed); (void)now; /* legacy argument from the past that we ignore */ diff --git a/lib/pingpong.c b/lib/pingpong.c index c7e89d0..1670792 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,9 +34,7 @@ #include "multiif.h" #include "non-ascii.h" #include "vtls/vtls.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -165,7 +163,7 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, size_t write_len; char *fmt_crlf; char *s; - CURLcode error; + CURLcode result; struct connectdata *conn = pp->conn; struct SessionHandle *data = conn->data; @@ -191,26 +189,26 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, Curl_pp_init(pp); - error = Curl_convert_to_network(data, s, write_len); + result = Curl_convert_to_network(data, s, write_len); /* Curl_convert_to_network calls failf if unsuccessful */ - if(error) { + if(result) { free(s); - return error; + return result; } #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len, + result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len, &bytes_written); #ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif - if(error) { + if(result) { free(s); - return error; + return result; } if(conn->data->set.verbose) @@ -247,15 +245,15 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, CURLcode Curl_pp_sendf(struct pingpong *pp, const char *fmt, ...) { - CURLcode res; + CURLcode result; va_list ap; va_start(ap, fmt); - res = Curl_pp_vsendf(pp, fmt, ap); + result = Curl_pp_vsendf(pp, fmt, ap); va_end(ap); - return res; + return result; } /* @@ -285,8 +283,6 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, /* number of bytes in the current line, so far */ perline = (ssize_t)(ptr-pp->linestart_resp); - keepon=TRUE; - while((pp->nread_resp<BUFSIZE) && (keepon && !result)) { if(pp->cache) { @@ -305,30 +301,28 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd, pp->cache_size = 0; /* zero the size just in case */ } else { - int res; #ifdef HAVE_GSSAPI enum protection_level prot = conn->data_prot; conn->data_prot = PROT_CLEAR; #endif DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1)); - res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp, - &gotbytes); + result = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp, + &gotbytes); #ifdef HAVE_GSSAPI DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); conn->data_prot = prot; #endif - if(res == CURLE_AGAIN) + if(result == CURLE_AGAIN) return CURLE_OK; /* return */ - if((res == CURLE_OK) && (gotbytes > 0)) + if(!result && (gotbytes > 0)) /* convert from the network encoding */ - res = Curl_convert_from_network(data, ptr, gotbytes); + result = Curl_convert_from_network(data, ptr, gotbytes); /* Curl_convert_from_network calls failf if unsuccessful */ - if(CURLE_OK != res) { - result = (CURLcode)res; /* Set outer result variable to this error. */ + if(result) + /* Set outer result variable to this error. */ keepon = FALSE; - } } if(!keepon) @@ -478,11 +472,9 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp) /* we have a piece of a command still left to send */ struct connectdata *conn = pp->conn; ssize_t written; - CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; - - result = Curl_write(conn, sock, pp->sendthis + pp->sendsize - - pp->sendleft, pp->sendleft, &written); + CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize - + pp->sendleft, pp->sendleft, &written); if(result) return result; @@ -501,10 +493,8 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp) CURLcode Curl_pp_disconnect(struct pingpong *pp) { - if(pp->cache) { - free(pp->cache); - pp->cache = NULL; - } + free(pp->cache); + pp->cache = NULL; return CURLE_OK; } diff --git a/lib/pipeline.c b/lib/pipeline.c index 8d2544b..1b38836 100644 --- a/lib/pipeline.c +++ b/lib/pipeline.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) 2013-2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2013-2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,7 +32,6 @@ #include "pipeline.h" #include "sendf.h" #include "rawstr.h" -#include "bundles.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -49,15 +48,13 @@ static void site_blacklist_llist_dtor(void *user, void *element) (void)user; Curl_safefree(entry->hostname); - Curl_safefree(entry); + free(entry); } static void server_blacklist_llist_dtor(void *user, void *element) { - char *server_name = element; (void)user; - - Curl_safefree(server_name); + free(element); } bool Curl_pipeline_penalized(struct SessionHandle *data, @@ -94,20 +91,29 @@ bool Curl_pipeline_penalized(struct SessionHandle *data, return FALSE; } +static CURLcode addHandleToPipeline(struct SessionHandle *data, + struct curl_llist *pipeline) +{ + if(!Curl_llist_insert_next(pipeline, pipeline->tail, data)) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; +} + + CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle, struct connectdata *conn) { struct curl_llist_element *sendhead = conn->send_pipe->head; struct curl_llist *pipeline; - CURLcode rc; + CURLcode result; pipeline = conn->send_pipe; - rc = Curl_addHandleToPipeline(handle, pipeline); + result = addHandleToPipeline(handle, pipeline); if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) { /* this is a new one as head, expire it */ - conn->writechannel_inuse = FALSE; /* not in use yet */ + Curl_pipeline_leave_write(conn); /* not in use yet */ Curl_expire(conn->send_pipe->head->ptr, 1); } @@ -115,7 +121,7 @@ CURLcode Curl_add_handle_to_pipeline(struct SessionHandle *handle, print_pipeline(conn); #endif - return rc; + return result; } /* Move this transfer from the sending list to the receiving list. @@ -138,7 +144,7 @@ void Curl_move_handle_from_send_to_recv_pipe(struct SessionHandle *handle, if(conn->send_pipe->head) { /* Since there's a new easy handle at the start of the send pipeline, set its timeout value to 1ms to make it trigger instantly */ - conn->writechannel_inuse = FALSE; /* not used now */ + Curl_pipeline_leave_write(conn); /* not used now */ #ifdef DEBUGBUILD infof(conn->data, "%p is at send pipe head B!\n", (void *)conn->send_pipe->head->ptr); @@ -251,7 +257,7 @@ CURLMcode Curl_pipeline_set_site_blacklist(char **sites, bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle, char *server_name) { - if(handle->multi) { + if(handle->multi && server_name) { struct curl_llist *blacklist = Curl_multi_pipelining_server_bl(handle->multi); @@ -272,7 +278,7 @@ bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle, } } - infof(handle, "Server %s is not blacklisted\n", server_name); + DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name)); } return FALSE; } @@ -314,6 +320,93 @@ CURLMcode Curl_pipeline_set_server_blacklist(char **servers, return CURLM_OK; } +static bool pipe_head(struct SessionHandle *data, + struct curl_llist *pipeline) +{ + struct curl_llist_element *curr = pipeline->head; + if(curr) + return (curr->ptr == data) ? TRUE : FALSE; + + return FALSE; +} + +/* returns TRUE if the given handle is head of the recv pipe */ +bool Curl_recvpipe_head(struct SessionHandle *data, + struct connectdata *conn) +{ + return pipe_head(data, conn->recv_pipe); +} + +/* returns TRUE if the given handle is head of the send pipe */ +bool Curl_sendpipe_head(struct SessionHandle *data, + struct connectdata *conn) +{ + return pipe_head(data, conn->send_pipe); +} + + +/* + * Check if the write channel is available and this handle as at the head, + * then grab the channel and return TRUE. + * + * If not available, return FALSE. + */ + +bool Curl_pipeline_checkget_write(struct SessionHandle *data, + struct connectdata *conn) +{ + if(conn->bits.multiplex) + /* when multiplexing, we can use it at once */ + return TRUE; + + if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) { + /* Grab the channel */ + conn->writechannel_inuse = TRUE; + return TRUE; + } + return FALSE; +} + + +/* + * Check if the read channel is available and this handle as at the head, then + * grab the channel and return TRUE. + * + * If not available, return FALSE. + */ + +bool Curl_pipeline_checkget_read(struct SessionHandle *data, + struct connectdata *conn) +{ + if(conn->bits.multiplex) + /* when multiplexing, we can use it at once */ + return TRUE; + + if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) { + /* Grab the channel */ + conn->readchannel_inuse = TRUE; + return TRUE; + } + return FALSE; +} + +/* + * The current user of the pipeline write channel gives it up. + */ +void Curl_pipeline_leave_write(struct connectdata *conn) +{ + conn->writechannel_inuse = FALSE; +} + +/* + * The current user of the pipeline read channel gives it up. + */ +void Curl_pipeline_leave_read(struct connectdata *conn) +{ + conn->readchannel_inuse = FALSE; +} + + #if 0 void print_pipeline(struct connectdata *conn) { diff --git a/lib/pipeline.h b/lib/pipeline.h index 96c4c33..bf229f1 100644 --- a/lib/pipeline.h +++ b/lib/pipeline.h @@ -7,6 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * + * Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2013 - 2014, Linus Nielsen Feltzing, <linus@haxx.se> * * This software is licensed as described in the file COPYING, which @@ -41,4 +42,15 @@ bool Curl_pipeline_server_blacklisted(struct SessionHandle *handle, CURLMcode Curl_pipeline_set_server_blacklist(char **servers, struct curl_llist **list_ptr); +bool Curl_pipeline_checkget_write(struct SessionHandle *data, + struct connectdata *conn); +bool Curl_pipeline_checkget_read(struct SessionHandle *data, + struct connectdata *conn); +void Curl_pipeline_leave_write(struct connectdata *conn); +void Curl_pipeline_leave_read(struct connectdata *conn); +bool Curl_recvpipe_head(struct SessionHandle *data, + struct connectdata *conn); +bool Curl_sendpipe_head(struct SessionHandle *data, + struct connectdata *conn); + #endif /* HEADER_CURL_PIPELINE_H */ @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -63,7 +63,6 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" -#include "if2ip.h" #include "hostip.h" #include "progress.h" #include "transfer.h" @@ -84,10 +83,7 @@ #include "curl_sasl.h" #include "curl_md5.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -107,10 +103,10 @@ static CURLcode pop3_setup_connection(struct connectdata *conn); static CURLcode pop3_parse_url_options(struct connectdata *conn); static CURLcode pop3_parse_url_path(struct connectdata *conn); static CURLcode pop3_parse_custom_request(struct connectdata *conn); -static CURLcode pop3_calc_sasl_details(struct connectdata *conn, - const char **mech, - char **initresp, size_t *len, - pop3state *state1, pop3state *state2); +static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech, + const char *initresp); +static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp); +static void pop3_get_message(char *buffer, char** outptr); /* * POP3 protocol handler. @@ -215,6 +211,17 @@ static const struct Curl_handler Curl_handler_pop3s_proxy = { #endif #endif +/* SASL parameters for the pop3 protocol */ +static const struct SASLproto saslpop3 = { + "pop", /* The service name */ + '+', /* Code received when continuation is expected */ + '+', /* Code to receive upon authentication success */ + 255 - 8, /* Maximum initial response length (no max) */ + pop3_perform_auth, /* Send authentication command */ + pop3_continue_auth, /* Send authentication continuation */ + pop3_get_message /* Get SASL response message */ +}; + #ifdef USE_SSL static void pop3_to_pop3s(struct connectdata *conn) { @@ -313,20 +320,7 @@ static void state(struct connectdata *conn, pop3state newstate) "CAPA", "STARTTLS", "UPGRADETLS", - "AUTH_PLAIN", - "AUTH_LOGIN", - "AUTH_LOGIN_PASSWD", - "AUTH_CRAMMD5", - "AUTH_DIGESTMD5", - "AUTH_DIGESTMD5_RESP", - "AUTH_NTLM", - "AUTH_NTLM_TYPE2MSG", - "AUTH_GSSAPI", - "AUTH_GSSAPI_TOKEN", - "AUTH_GSSAPI_NO_DATA", - "AUTH_XOAUTH2", - "AUTH_CANCEL", - "AUTH_FINAL", + "AUTH", "APOP", "USER", "PASS", @@ -355,9 +349,9 @@ static CURLcode pop3_perform_capa(struct connectdata *conn) CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; - pop3c->authmechs = 0; /* No known authentication mechanisms yet */ - pop3c->authused = 0; /* Clear the authentication mechanism used */ - pop3c->tls_supported = FALSE; /* Clear the TLS capability */ + pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ + pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ + pop3c->tls_supported = FALSE; /* Clear the TLS capability */ /* Send the CAPA command */ result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA"); @@ -501,25 +495,18 @@ static CURLcode pop3_perform_apop(struct connectdata *conn) */ static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech, - const char *initresp, size_t len, - pop3state state1, pop3state state2) + const char *initresp) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; - if(initresp && 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */ + if(initresp) { /* AUTH <mech> ...<crlf> */ /* Send the AUTH command with the initial response */ result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp); - - if(!result) - state(conn, state2); } else { /* Send the AUTH command */ result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech); - - if(!result) - state(conn, state1); } return result; @@ -527,6 +514,20 @@ static CURLcode pop3_perform_auth(struct connectdata *conn, /*********************************************************************** * + * pop3_continue_auth() + * + * Sends SASL continuation data or cancellation. + */ +static CURLcode pop3_continue_auth(struct connectdata *conn, + const char *resp) +{ + struct pop3_conn *pop3c = &conn->proto.pop3c; + + return Curl_pp_sendf(&pop3c->pp, "%s", resp); +} + +/*********************************************************************** + * * pop3_perform_authentication() * * Initiates the authentication sequence, with the appropriate SASL @@ -537,40 +538,32 @@ static CURLcode pop3_perform_authentication(struct connectdata *conn) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *mech = NULL; - char *initresp = NULL; - size_t len = 0; - pop3state state1 = POP3_STOP; - pop3state state2 = POP3_STOP; + saslprogress progress = SASL_IDLE; - /* Check we have a username and password to authenticate with and end the + /* Check we have enough data to authenticate with and end the connect phase if we don't */ - if(!conn->bits.user_passwd) { + if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) { state(conn, POP3_STOP); - return result; } - /* Calculate the SASL login details */ - if(pop3c->authtypes & POP3_TYPE_SASL) - result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1, - &state2); + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) { + /* Calculate the SASL login details */ + result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress); - if(!result) { - if(mech && (pop3c->preftype & POP3_TYPE_SASL)) { - /* Perform SASL based authentication */ - result = pop3_perform_auth(conn, mech, initresp, len, state1, state2); + if(!result) + if(progress == SASL_INPROGRESS) + state(conn, POP3_AUTH); + } - Curl_safefree(initresp); - } + if(!result && progress == SASL_IDLE) { #ifndef CURL_DISABLE_CRYPTO_AUTH - else if((pop3c->authtypes & POP3_TYPE_APOP) && - (pop3c->preftype & POP3_TYPE_APOP)) + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) /* Perform APOP authentication */ result = pop3_perform_apop(conn); + else #endif - else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && - (pop3c->preftype & POP3_TYPE_CLEARTEXT)) + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) /* Perform clear text authentication */ result = pop3_perform_user(conn); else { @@ -727,6 +720,9 @@ static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, /* Loop through the data line */ for(;;) { + size_t llen; + unsigned int mechbit; + while(len && (*line == ' ' || *line == '\t' || *line == '\r' || *line == '\n')) { @@ -745,22 +741,9 @@ static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, wordlen++; /* Test the word for a matching authentication mechanism */ - if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) - pop3c->authmechs |= SASL_MECH_LOGIN; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) - pop3c->authmechs |= SASL_MECH_PLAIN; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) - pop3c->authmechs |= SASL_MECH_CRAM_MD5; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) - pop3c->authmechs |= SASL_MECH_DIGEST_MD5; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) - pop3c->authmechs |= SASL_MECH_GSSAPI; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) - pop3c->authmechs |= SASL_MECH_EXTERNAL; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) - pop3c->authmechs |= SASL_MECH_NTLM; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) - pop3c->authmechs |= SASL_MECH_XOAUTH2; + if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) && + llen == wordlen) + pop3c->sasl.authmechs |= mechbit; line += wordlen; len -= wordlen; @@ -818,575 +801,42 @@ static CURLcode pop3_state_starttls_resp(struct connectdata *conn, return result; } -/* For AUTH PLAIN (without initial response) responses */ -static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *plainauth = NULL; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied. %c", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the authorisation message */ - result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, - &plainauth, &len); - if(!result && plainauth) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - } - - Curl_safefree(plainauth); - - return result; -} - -/* For AUTH LOGIN (without initial response) responses */ -static CURLcode pop3_state_auth_login_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *authuser = NULL; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the user message */ - result = Curl_sasl_create_login_message(data, conn->user, - &authuser, &len); - if(!result && authuser) { - /* Send the user */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser); - - if(!result) - state(conn, POP3_AUTH_LOGIN_PASSWD); - } - } - - Curl_safefree(authuser); - - return result; -} - -/* For AUTH LOGIN user entry responses */ -static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *authpasswd = NULL; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the password message */ - result = Curl_sasl_create_login_message(data, conn->passwd, - &authpasswd, &len); - if(!result && authpasswd) { - /* Send the password */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - } - - Curl_safefree(authpasswd); - - return result; -} - -#ifndef CURL_DISABLE_CRYPTO_AUTH -/* For AUTH CRAM-MD5 responses */ -static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlg = NULL; - char *chlg64 = NULL; - char *rplyb64 = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - return CURLE_LOGIN_DENIED; - } - - /* Get the challenge message */ - pop3_get_message(data->state.buffer, &chlg64); - - /* Decode the challenge message */ - result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len); - if(result) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); - - if(!result) - state(conn, POP3_AUTH_CANCEL); - } - else { - /* Create the response message */ - result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user, - conn->passwd, &rplyb64, &len); - if(!result && rplyb64) { - /* Send the response */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - } - - Curl_safefree(chlg); - Curl_safefree(rplyb64); - - return result; -} - -/* For AUTH DIGEST-MD5 challenge responses */ -static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlg64 = NULL; - char *rplyb64 = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - return CURLE_LOGIN_DENIED; - } - - /* Get the challenge message */ - pop3_get_message(data->state.buffer, &chlg64); - - /* Create the response message */ - result = Curl_sasl_create_digest_md5_message(data, chlg64, - conn->user, conn->passwd, - "pop", &rplyb64, &len); - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); - - if(!result) - state(conn, POP3_AUTH_CANCEL); - } - } - else { - /* Send the response */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); - - if(!result) - state(conn, POP3_AUTH_DIGESTMD5_RESP); - } - - Curl_safefree(rplyb64); - - return result; -} - -/* For AUTH DIGEST-MD5 challenge-response responses */ -static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Authentication failed: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", ""); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - - return result; -} -#endif - -#ifdef USE_NTLM -/* For AUTH NTLM (without initial response) responses */ -static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *type1msg = NULL; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the type-1 message */ - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - &type1msg, &len); - if(!result && type1msg) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg); - - if(!result) - state(conn, POP3_AUTH_NTLM_TYPE2MSG); - } - } - - Curl_safefree(type1msg); - - return result; -} - -/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ -static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *type2msg = NULL; - char *type3msg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the type-2 message */ - pop3_get_message(data->state.buffer, &type2msg); - - /* Decode the type-2 message */ - result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); - if(result) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); - - if(!result) - state(conn, POP3_AUTH_CANCEL); - } - else { - /* Create the type-3 message */ - result = Curl_sasl_create_ntlm_type3_message(data, conn->user, - conn->passwd, &conn->ntlm, - &type3msg, &len); - if(!result && type3msg) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - } - } - - Curl_safefree(type3msg); - - return result; -} -#endif - -#if defined(USE_WINDOWS_SSPI) -/* For AUTH GSSAPI (without initial response) responses */ -static CURLcode pop3_state_auth_gssapi_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct pop3_conn *pop3c = &conn->proto.pop3c; - size_t len = 0; - char *respmsg = NULL; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the initial response message */ - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, "pop", - pop3c->mutual_auth, - NULL, &conn->krb5, - &respmsg, &len); - if(!result && respmsg) { - /* Send the message */ - result = Curl_pp_sendf(&pop3c->pp, "%s", respmsg); - - if(!result) - state(conn, POP3_AUTH_GSSAPI_TOKEN); - } - } - - Curl_safefree(respmsg); - - return result; -} - -/* For AUTH GSSAPI user token responses */ -static CURLcode pop3_state_auth_gssapi_token_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct pop3_conn *pop3c = &conn->proto.pop3c; - char *chlgmsg = NULL; - char *respmsg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - pop3_get_message(data->state.buffer, &chlgmsg); - - if(pop3c->mutual_auth) - /* Decode the user token challenge and create the optional response - message */ - result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL, - pop3c->mutual_auth, - chlgmsg, &conn->krb5, - &respmsg, &len); - else - /* Decode the security challenge and create the response message */ - result = Curl_sasl_create_gssapi_security_message(data, chlgmsg, - &conn->krb5, - &respmsg, &len); - - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&pop3c->pp, "%s", "*"); - - if(!result) - state(conn, POP3_AUTH_CANCEL); - } - } - else { - /* Send the response */ - if(respmsg) - result = Curl_pp_sendf(&pop3c->pp, "%s", respmsg); - else - result = Curl_pp_sendf(&pop3c->pp, "%s", ""); - - if(!result) - state(conn, (pop3c->mutual_auth ? POP3_AUTH_GSSAPI_NO_DATA : - POP3_AUTH_FINAL)); - } - } - - Curl_safefree(respmsg); - - return result; -} - -/* For AUTH GSSAPI no data responses */ -static CURLcode pop3_state_auth_gssapi_no_data_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlgmsg = NULL; - char *respmsg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - pop3_get_message(data->state.buffer, &chlgmsg); - - /* Decode the security challenge and create the security message */ - result = Curl_sasl_create_gssapi_security_message(data, chlgmsg, - &conn->krb5, - &respmsg, &len); - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); - - if(!result) - state(conn, POP3_AUTH_CANCEL); - } - } - else { - /* Send the response */ - if(respmsg) { - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", respmsg); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - } - } - - Curl_safefree(respmsg); - - return result; -} -#endif - -/* For AUTH XOAUTH2 (without initial response) responses */ -static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn, - int pop3code, pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *xoauth = NULL; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the authorisation message */ - result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, - conn->xoauth2_bearer, - &xoauth, &len); - if(!result && xoauth) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth); - - if(!result) - state(conn, POP3_AUTH_FINAL); - } - } - - Curl_safefree(xoauth); - - return result; -} - -/* For AUTH cancellation responses */ -static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn, - int pop3code, - pop3state instate) +/* For SASL authentication responses */ +static CURLcode pop3_state_auth_resp(struct connectdata *conn, + int pop3code, + pop3state instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *mech = NULL; - char *initresp = NULL; - size_t len = 0; - pop3state state1 = POP3_STOP; - pop3state state2 = POP3_STOP; + saslprogress progress; - (void)pop3code; (void)instate; /* no use for this yet */ - /* Remove the offending mechanism from the supported list */ - pop3c->authmechs ^= pop3c->authused; - - /* Calculate alternative SASL login details */ - result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1, - &state2); - - if(!result) { - /* Do we have any mechanisms left or can we fallback to another - authentication type? */ - if(mech) { - /* Retry SASL based authentication */ - result = pop3_perform_auth(conn, mech, initresp, len, state1, state2); - - Curl_safefree(initresp); - } + result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + state(conn, POP3_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ #ifndef CURL_DISABLE_CRYPTO_AUTH - else if((pop3c->authtypes & POP3_TYPE_APOP) && - (pop3c->preftype & POP3_TYPE_APOP)) - /* Perform APOP authentication */ - result = pop3_perform_apop(conn); + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) + /* Perform APOP authentication */ + result = pop3_perform_apop(conn); + else #endif - else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && - (pop3c->preftype & POP3_TYPE_CLEARTEXT)) - /* Perform clear text authentication */ - result = pop3_perform_user(conn); - else { - failf(data, "Authentication cancelled"); - - result = CURLE_LOGIN_DENIED; + if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) + /* Perform clear text authentication */ + result = pop3_perform_user(conn); + else { + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; + } + break; + default: + break; } - } - - return result; -} - -/* For final responses in the AUTH sequence */ -static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Authentication failed: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else - /* End of connect phase */ - state(conn, POP3_STOP); return result; } @@ -1553,69 +1003,8 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); break; - case POP3_AUTH_PLAIN: - result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_LOGIN: - result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_LOGIN_PASSWD: - result = pop3_state_auth_login_password_resp(conn, pop3code, - pop3c->state); - break; - -#ifndef CURL_DISABLE_CRYPTO_AUTH - case POP3_AUTH_CRAMMD5: - result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_DIGESTMD5: - result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_DIGESTMD5_RESP: - result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state); - break; -#endif - -#ifdef USE_NTLM - case POP3_AUTH_NTLM: - result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_NTLM_TYPE2MSG: - result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code, - pop3c->state); - break; -#endif - -#if defined(USE_WINDOWS_SSPI) - case POP3_AUTH_GSSAPI: - result = pop3_state_auth_gssapi_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_GSSAPI_TOKEN: - result = pop3_state_auth_gssapi_token_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_GSSAPI_NO_DATA: - result = pop3_state_auth_gssapi_no_data_resp(conn, pop3code, - pop3c->state); - break; -#endif - - case POP3_AUTH_XOAUTH2: - result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_CANCEL: - result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state); - break; - - case POP3_AUTH_FINAL: - result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state); + case POP3_AUTH: + result = pop3_state_auth_resp(conn, pop3code, pop3c->state); break; #ifndef CURL_DISABLE_CRYPTO_AUTH @@ -1728,7 +1117,7 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done) /* Set the default preferred authentication type and mechanism */ pop3c->preftype = POP3_TYPE_ANY; - pop3c->prefmech = SASL_AUTH_ANY; + Curl_sasl_init(&pop3c->sasl, &saslpop3); /* Initialise the pingpong layer */ Curl_pp_init(pp); @@ -1880,7 +1269,7 @@ static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection) Curl_pp_disconnect(&pop3c->pp); /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, pop3c->authused); + Curl_sasl_cleanup(conn, pop3c->sasl.authused); /* Cleanup our connection based variables */ Curl_safefree(pop3c->apoptimestamp); @@ -1995,75 +1384,52 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *options = conn->options; - const char *ptr = options; - bool reset = TRUE; + const char *ptr = conn->options; + + pop3c->sasl.resetprefs = TRUE; - while(ptr && *ptr) { + while(!result && ptr && *ptr) { const char *key = ptr; + const char *value; while(*ptr && *ptr != '=') ptr++; - if(strnequal(key, "AUTH", 4)) { - size_t len = 0; - const char *value = ++ptr; + value = ptr + 1; - if(reset) { - reset = FALSE; - pop3c->preftype = POP3_TYPE_NONE; - pop3c->prefmech = SASL_AUTH_NONE; - } + while(*ptr && *ptr != ';') + ptr++; - while(*ptr && *ptr != ';') { - ptr++; - len++; - } + if(strnequal(key, "AUTH=", 5)) { + result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, + value, ptr - value); - if(strnequal(value, "*", len)) { - pop3c->preftype = POP3_TYPE_ANY; - pop3c->prefmech = SASL_AUTH_ANY; - } - else if(strnequal(value, "+APOP", len)) { + if(result && strnequal(value, "+APOP", ptr - value)) { pop3c->preftype = POP3_TYPE_APOP; - pop3c->prefmech = SASL_AUTH_NONE; - } - else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_LOGIN; - } - else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_PLAIN; - } - else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_CRAM_MD5; - } - else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_DIGEST_MD5; - } - else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_GSSAPI; - } - else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_NTLM; - } - else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) { - pop3c->preftype = POP3_TYPE_SASL; - pop3c->prefmech |= SASL_MECH_XOAUTH2; + pop3c->sasl.prefmech = SASL_AUTH_NONE; + result = CURLE_OK; } - - if(*ptr == ';') - ptr++; } else result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; } + if(pop3c->preftype != POP3_TYPE_APOP) + switch(pop3c->sasl.prefmech) { + case SASL_AUTH_NONE: + pop3c->preftype = POP3_TYPE_NONE; + break; + case SASL_AUTH_DEFAULT: + pop3c->preftype = POP3_TYPE_ANY; + break; + default: + pop3c->preftype = POP3_TYPE_SASL; + break; + } + return result; } @@ -2106,110 +1472,6 @@ static CURLcode pop3_parse_custom_request(struct connectdata *conn) /*********************************************************************** * - * pop3_calc_sasl_details() - * - * Calculate the required login details for SASL authentication. - */ -static CURLcode pop3_calc_sasl_details(struct connectdata *conn, - const char **mech, - char **initresp, size_t *len, - pop3state *state1, pop3state *state2) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct pop3_conn *pop3c = &conn->proto.pop3c; - - /* Calculate the supported authentication mechanism, by decreasing order of - security, as well as the initial response where appropriate */ -#if defined(USE_WINDOWS_SSPI) - if((pop3c->authmechs & SASL_MECH_GSSAPI) && - (pop3c->prefmech & SASL_MECH_GSSAPI)) { - pop3c->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ - - *mech = SASL_MECH_STRING_GSSAPI; - *state1 = POP3_AUTH_GSSAPI; - *state2 = POP3_AUTH_GSSAPI_TOKEN; - pop3c->authused = SASL_MECH_GSSAPI; - - if(data->set.sasl_ir) - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, "pop", - pop3c->mutual_auth, - NULL, &conn->krb5, - initresp, len); - } - else -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) && - (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) { - *mech = SASL_MECH_STRING_DIGEST_MD5; - *state1 = POP3_AUTH_DIGESTMD5; - pop3c->authused = SASL_MECH_DIGEST_MD5; - } - else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) && - (pop3c->prefmech & SASL_MECH_CRAM_MD5)) { - *mech = SASL_MECH_STRING_CRAM_MD5; - *state1 = POP3_AUTH_CRAMMD5; - pop3c->authused = SASL_MECH_CRAM_MD5; - } - else -#endif -#ifdef USE_NTLM - if((pop3c->authmechs & SASL_MECH_NTLM) && - (pop3c->prefmech & SASL_MECH_NTLM)) { - *mech = SASL_MECH_STRING_NTLM; - *state1 = POP3_AUTH_NTLM; - *state2 = POP3_AUTH_NTLM_TYPE2MSG; - pop3c->authused = SASL_MECH_NTLM; - - if(data->set.sasl_ir) - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - initresp, len); - } - else -#endif - if(((pop3c->authmechs & SASL_MECH_XOAUTH2) && - (pop3c->prefmech & SASL_MECH_XOAUTH2) && - (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { - *mech = SASL_MECH_STRING_XOAUTH2; - *state1 = POP3_AUTH_XOAUTH2; - *state2 = POP3_AUTH_FINAL; - pop3c->authused = SASL_MECH_XOAUTH2; - - if(data->set.sasl_ir) - result = Curl_sasl_create_xoauth2_message(data, conn->user, - conn->xoauth2_bearer, - initresp, len); - } - else if((pop3c->authmechs & SASL_MECH_LOGIN) && - (pop3c->prefmech & SASL_MECH_LOGIN)) { - *mech = SASL_MECH_STRING_LOGIN; - *state1 = POP3_AUTH_LOGIN; - *state2 = POP3_AUTH_LOGIN_PASSWD; - pop3c->authused = SASL_MECH_LOGIN; - - if(data->set.sasl_ir) - result = Curl_sasl_create_login_message(data, conn->user, initresp, len); - } - else if((pop3c->authmechs & SASL_MECH_PLAIN) && - (pop3c->prefmech & SASL_MECH_PLAIN)) { - *mech = SASL_MECH_STRING_PLAIN; - *state1 = POP3_AUTH_PLAIN; - *state2 = POP3_AUTH_FINAL; - pop3c->authused = SASL_MECH_PLAIN; - - if(data->set.sasl_ir) - result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, - initresp, len); - } - - return result; -} - -/*********************************************************************** - * * Curl_pop3_write() * * This function scans the body after the end-of-body and writes everything @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2009 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,6 +23,7 @@ ***************************************************************************/ #include "pingpong.h" +#include "curl_sasl.h" /**************************************************************************** * POP3 unique setup @@ -35,20 +36,7 @@ typedef enum { POP3_STARTTLS, POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS (multi mode only) */ - POP3_AUTH_PLAIN, - POP3_AUTH_LOGIN, - POP3_AUTH_LOGIN_PASSWD, - POP3_AUTH_CRAMMD5, - POP3_AUTH_DIGESTMD5, - POP3_AUTH_DIGESTMD5_RESP, - POP3_AUTH_NTLM, - POP3_AUTH_NTLM_TYPE2MSG, - POP3_AUTH_GSSAPI, - POP3_AUTH_GSSAPI_TOKEN, - POP3_AUTH_GSSAPI_NO_DATA, - POP3_AUTH_XOAUTH2, - POP3_AUTH_CANCEL, - POP3_AUTH_FINAL, + POP3_AUTH, POP3_APOP, POP3_USER, POP3_PASS, @@ -77,14 +65,11 @@ struct pop3_conn { have been received so far */ size_t strip; /* Number of bytes from the start to ignore as non-body */ + struct SASL sasl; /* SASL-related storage */ unsigned int authtypes; /* Accepted authentication types */ - unsigned int authmechs; /* Accepted SASL authentication mechanisms */ unsigned int preftype; /* Preferred authentication type */ - unsigned int prefmech; /* Preferred SASL authentication mechanism */ - unsigned int authused; /* SASL auth mechanism used for the connection */ char *apoptimestamp; /* APOP timestamp from the server greeting */ bool tls_supported; /* StartTLS capability supported by server */ - bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ }; extern const struct Curl_handler Curl_handler_pop3; diff --git a/lib/progress.c b/lib/progress.c index f147ce7..b46e274 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,9 +25,7 @@ #include "urldata.h" #include "sendf.h" #include "progress.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" /* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero byte) */ @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,14 +34,12 @@ #include "progress.h" #include "rtsp.h" #include "rawstr.h" -#include "curl_memory.h" #include "select.h" #include "connect.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* @@ -265,11 +263,10 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. **/ - DEBUGASSERT((rtspreq > RTSPREQ_NONE && rtspreq < RTSPREQ_LAST)); data->set.opt_no_body = TRUE; /* most requests don't contain a body */ switch(rtspreq) { - case RTSPREQ_NONE: - failf(data, "Got invalid RTSP request: RTSPREQ_NONE"); + default: + failf(data, "Got invalid RTSP request"); return CURLE_BAD_FUNCTION_ARGUMENT; case RTSPREQ_OPTIONS: p_request = "OPTIONS"; @@ -325,7 +322,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(!p_session_id && (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", - p_request ? p_request : ""); + p_request); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -443,8 +440,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) Curl_add_bufferf(req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ "CSeq: %ld\r\n", /* CSeq */ - (p_request ? p_request : ""), p_stream_uri, - rtsp->CSeq_sent); + p_request, p_stream_uri, rtsp->CSeq_sent); if(result) return result; @@ -498,8 +494,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } else { - postsize = (data->set.postfieldsize != -1)? - data->set.postfieldsize: + postsize = (data->state.infilesize != -1)? + data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); data->set.httpreq = HTTPREQ_POST; } diff --git a/lib/security.c b/lib/security.c index 508c7b4..014bbf1 100644 --- a/lib/security.c +++ b/lib/security.c @@ -7,10 +7,10 @@ * rewrite to work around the paragraph 2 in the BSD licenses as explained * below. * - * Copyright (c) 1998, 1999, 2013 Kungliga Tekniska Högskolan + * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * - * Copyright (C) 2001 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2001 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * All rights reserved. * @@ -109,19 +109,12 @@ static char level_to_char(int level) { return 'P'; } -static const struct Curl_sec_client_mech * const mechs[] = { -#ifdef HAVE_GSSAPI - &Curl_krb5_client_mech, -#endif - NULL -}; - /* Send an FTP command defined by |message| and the optional arguments. The function returns the ftp_code. If an error occurs, -1 is returned. */ static int ftp_send_command(struct connectdata *conn, const char *message, ...) { int ftp_code; - ssize_t nread; + ssize_t nread=0; va_list args; char print_buffer[50]; @@ -129,11 +122,11 @@ static int ftp_send_command(struct connectdata *conn, const char *message, ...) vsnprintf(print_buffer, sizeof(print_buffer), message, args); va_end(args); - if(Curl_ftpsendf(conn, print_buffer) != CURLE_OK) { + if(Curl_ftpsendf(conn, print_buffer)) { ftp_code = -1; } else { - if(Curl_GetFTPResponse(&nread, conn, &ftp_code) != CURLE_OK) + if(Curl_GetFTPResponse(&nread, conn, &ftp_code)) ftp_code = -1; } @@ -147,20 +140,20 @@ static CURLcode socket_read(curl_socket_t fd, void *to, size_t len) { char *to_p = to; - CURLcode code; + CURLcode result; ssize_t nread; while(len > 0) { - code = Curl_read_plain(fd, to_p, len, &nread); - if(code == CURLE_OK) { + result = Curl_read_plain(fd, to_p, len, &nread); + if(!result) { len -= nread; to_p += nread; } else { /* FIXME: We are doing a busy wait */ - if(code == CURLE_AGAIN) + if(result == CURLE_AGAIN) continue; - return code; + return result; } } return CURLE_OK; @@ -175,20 +168,20 @@ socket_write(struct connectdata *conn, curl_socket_t fd, const void *to, size_t len) { const char *to_p = to; - CURLcode code; + CURLcode result; ssize_t written; while(len > 0) { - code = Curl_write_plain(conn, fd, to_p, len, &written); - if(code == CURLE_OK) { + result = Curl_write_plain(conn, fd, to_p, len, &written); + if(!result) { len -= written; to_p += written; } else { /* FIXME: We are doing a busy wait */ - if(code == CURLE_AGAIN) + if(result == CURLE_AGAIN) continue; - return code; + return result; } } return CURLE_OK; @@ -200,11 +193,11 @@ static CURLcode read_data(struct connectdata *conn, { int len; void* tmp; - CURLcode ret; + CURLcode result; - ret = socket_read(fd, &len, sizeof(len)); - if(ret != CURLE_OK) - return ret; + result = socket_read(fd, &len, sizeof(len)); + if(result) + return result; len = ntohl(len); tmp = realloc(buf->data, len); @@ -212,9 +205,9 @@ static CURLcode read_data(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; buf->data = tmp; - ret = socket_read(fd, buf->data, len); - if(ret != CURLE_OK) - return ret; + result = socket_read(fd, buf->data, len); + if(result) + return result; buf->size = conn->mech->decode(conn->app_data, buf->data, len, conn->data_prot, conn); buf->index = 0; @@ -256,7 +249,7 @@ static ssize_t sec_recv(struct connectdata *conn, int sockindex, buffer += bytes_read; while(len > 0) { - if(read_data(conn, fd, &conn->in_buffer) != CURLE_OK) + if(read_data(conn, fd, &conn->in_buffer)) return -1; if(conn->in_buffer.size == 0) { if(bytes_read > 0) @@ -295,7 +288,7 @@ static void do_sec_send(struct connectdata *conn, curl_socket_t fd, prot_level = conn->command_prot; } bytes = conn->mech->encode(conn->app_data, from, length, prot_level, - (void**)&buffer, conn); + (void**)&buffer); if(!buffer || bytes <= 0) return; /* error */ @@ -332,7 +325,6 @@ static void do_sec_send(struct connectdata *conn, curl_socket_t fd, static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd, const char *buffer, size_t length) { - /* FIXME: Check for overflow */ ssize_t tx = 0, len = conn->buffer_size; len -= conn->mech->overhead(conn->app_data, conn->data_prot, @@ -340,10 +332,9 @@ static ssize_t sec_write(struct connectdata *conn, curl_socket_t fd, if(len <= 0) len = length; while(length) { - if(len >= 0 || length < (size_t)len) { - /* FIXME: Check for overflow. */ + if(length < (size_t)len) len = length; - } + do_sec_send(conn, fd, buffer, curlx_sztosi(len)); length -= len; buffer += len; @@ -368,7 +359,7 @@ int Curl_sec_read_msg(struct connectdata *conn, char *buffer, int */ int decoded_len; char *buf; - int ret_code; + int ret_code = 0; size_t decoded_sz = 0; CURLcode error; @@ -397,13 +388,13 @@ int Curl_sec_read_msg(struct connectdata *conn, char *buffer, } buf[decoded_len] = '\0'; - DEBUGASSERT(decoded_len > 3); - if(buf[3] == '-') - ret_code = 0; - else { - /* Check for error? */ - sscanf(buf, "%d", &ret_code); - } + if(decoded_len <= 3) + /* suspiciously short */ + return 0; + + if(buf[3] != '-') + /* safe to ignore return code */ + (void)sscanf(buf, "%d", &ret_code); if(buf[decoded_len - 1] == '\n') buf[decoded_len - 1] = '\0'; @@ -446,8 +437,8 @@ static int sec_set_protection_level(struct connectdata *conn) pbsz = strstr(conn->data->state.buffer, "PBSZ="); if(pbsz) { - /* FIXME: Checks for errors in sscanf? */ - sscanf(pbsz, "PBSZ=%u", &buffer_size); + /* ignore return code, use default value if it fails */ + (void)sscanf(pbsz, "PBSZ=%u", &buffer_size); if(buffer_size < conn->buffer_size) conn->buffer_size = buffer_size; } @@ -486,72 +477,63 @@ static CURLcode choose_mech(struct connectdata *conn) { int ret; struct SessionHandle *data = conn->data; - const struct Curl_sec_client_mech * const *mech; void *tmp_allocation; - const char *mech_name; - - for(mech = mechs; (*mech); ++mech) { - mech_name = (*mech)->name; - /* We have no mechanism with a NULL name but keep this check */ - DEBUGASSERT(mech_name != NULL); - if(mech_name == NULL) { - infof(data, "Skipping mechanism with empty name (%p)\n", (void *)mech); - continue; - } - tmp_allocation = realloc(conn->app_data, (*mech)->size); - if(tmp_allocation == NULL) { - failf(data, "Failed realloc of size %u", (*mech)->size); - mech = NULL; - return CURLE_OUT_OF_MEMORY; - } - conn->app_data = tmp_allocation; + const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech; - if((*mech)->init) { - ret = (*mech)->init(conn->app_data); - if(ret != 0) { - infof(data, "Failed initialization for %s. Skipping it.\n", mech_name); - continue; - } + tmp_allocation = realloc(conn->app_data, mech->size); + if(tmp_allocation == NULL) { + failf(data, "Failed realloc of size %u", mech->size); + mech = NULL; + return CURLE_OUT_OF_MEMORY; + } + conn->app_data = tmp_allocation; + + if(mech->init) { + ret = mech->init(conn->app_data); + if(ret) { + infof(data, "Failed initialization for %s. Skipping it.\n", + mech->name); + return CURLE_FAILED_INIT; } + } - infof(data, "Trying mechanism %s...\n", mech_name); - ret = ftp_send_command(conn, "AUTH %s", mech_name); - if(ret < 0) - /* FIXME: This error is too generic but it is OK for now. */ - return CURLE_COULDNT_CONNECT; - - if(ret/100 != 3) { - switch(ret) { - case 504: - infof(data, "Mechanism %s is not supported by the server (server " - "returned ftp code: 504).\n", mech_name); - break; - case 534: - infof(data, "Mechanism %s was rejected by the server (server returned " - "ftp code: 534).\n", mech_name); - break; - default: - if(ret/100 == 5) { - infof(data, "server does not support the security extensions\n"); - return CURLE_USE_SSL_FAILED; - } - break; + infof(data, "Trying mechanism %s...\n", mech->name); + ret = ftp_send_command(conn, "AUTH %s", mech->name); + if(ret < 0) + /* FIXME: This error is too generic but it is OK for now. */ + return CURLE_COULDNT_CONNECT; + + if(ret/100 != 3) { + switch(ret) { + case 504: + infof(data, "Mechanism %s is not supported by the server (server " + "returned ftp code: 504).\n", mech->name); + break; + case 534: + infof(data, "Mechanism %s was rejected by the server (server returned " + "ftp code: 534).\n", mech->name); + break; + default: + if(ret/100 == 5) { + infof(data, "server does not support the security extensions\n"); + return CURLE_USE_SSL_FAILED; } - continue; + break; } + return CURLE_LOGIN_DENIED; + } - /* Authenticate */ - ret = (*mech)->auth(conn->app_data, conn); + /* Authenticate */ + ret = mech->auth(conn->app_data, conn); - if(ret == AUTH_CONTINUE) - continue; - else if(ret != AUTH_OK) { + if(ret != AUTH_CONTINUE) { + if(ret != AUTH_OK) { /* Mechanism has dumped the error to stderr, don't error here. */ return -1; } DEBUGASSERT(ret == AUTH_OK); - conn->mech = *mech; + conn->mech = mech; conn->sec_complete = 1; conn->recv[FIRSTSOCKET] = sec_recv; conn->send[FIRSTSOCKET] = sec_send; @@ -561,10 +543,9 @@ static CURLcode choose_mech(struct connectdata *conn) /* Set the requested protection level */ /* BLOCKING */ (void)sec_set_protection_level(conn); - break; } - return mech != NULL ? CURLE_OK : CURLE_FAILED_INIT; + return CURLE_OK; } CURLcode @@ -579,10 +560,8 @@ Curl_sec_end(struct connectdata *conn) { if(conn->mech != NULL && conn->mech->end) conn->mech->end(conn->app_data); - if(conn->app_data) { - free(conn->app_data); - conn->app_data = NULL; - } + free(conn->app_data); + conn->app_data = NULL; if(conn->in_buffer.data) { free(conn->in_buffer.data); conn->in_buffer.data = NULL; diff --git a/lib/select.c b/lib/select.c index bb9b8b0..24dc5fd 100644 --- a/lib/select.c +++ b/lib/select.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -39,6 +39,10 @@ #include <dos.h> /* delay() */ #endif +#ifdef __VXWORKS__ +#include <strings.h> /* bzero() in FD_SET */ +#endif + #include <curl/curl.h> #include "urldata.h" @@ -155,7 +159,7 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ fd_set fds_err; curl_socket_t maxfd; #endif - struct timeval initial_tv = {0,0}; + struct timeval initial_tv = {0, 0}; int pending_ms = 0; int error; int r; @@ -389,7 +393,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) fd_set fds_err; curl_socket_t maxfd; #endif - struct timeval initial_tv = {0,0}; + struct timeval initial_tv = {0, 0}; bool fds_none = TRUE; unsigned int i; int pending_ms = 0; @@ -569,6 +573,6 @@ int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv); tpf_process_signals(); - return(rc); + return rc; } #endif /* TPF */ diff --git a/lib/sendf.c b/lib/sendf.c index 4a87c79..5f39d1f 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,14 +31,11 @@ #include "ssh.h" #include "multiif.h" #include "non-ascii.h" - -#define _MPRINTF_REPLACE /* use the internal *printf() functions */ -#include <curl/mprintf.h> - -#include "curl_memory.h" +#include "curl_printf.h" #include "strerror.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #ifdef CURL_DO_LINEEND_CONV @@ -55,7 +52,7 @@ static size_t convert_lineends(struct SessionHandle *data, /* sanity check */ if((startPtr == NULL) || (size < 1)) { - return(size); + return size; } if(data->state.prev_block_had_trailing_cr) { @@ -117,9 +114,9 @@ static size_t convert_lineends(struct SessionHandle *data, /* tidy up by null terminating the now shorter data */ *outPtr = '\0'; - return(outPtr - startPtr); + return (outPtr - startPtr); } - return(size); + return size; } #endif /* CURL_DO_LINEEND_CONV */ @@ -174,7 +171,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, struct SessionHandle *data = conn->data; ssize_t bytes_written; size_t write_len; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; char *s; char *sptr; va_list ap; @@ -190,9 +187,9 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, for(;;) { /* Write the buffer to the socket */ - res = Curl_write(conn, sockfd, sptr, write_len, &bytes_written); + result = Curl_write(conn, sockfd, sptr, write_len, &bytes_written); - if(CURLE_OK != res) + if(result) break; if(data->set.verbose) @@ -210,7 +207,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, free(s); /* free the output string */ - return res; + return result; } /* @@ -227,10 +224,10 @@ CURLcode Curl_write(struct connectdata *conn, ssize_t *written) { ssize_t bytes_written; - CURLcode curlcode = CURLE_OK; + CURLcode result = CURLE_OK; int num = (sockfd == conn->sock[SECONDARYSOCKET]); - bytes_written = conn->send[num](conn, num, mem, len, &curlcode); + bytes_written = conn->send[num](conn, num, mem, len, &result); *written = bytes_written; if(bytes_written >= 0) @@ -238,7 +235,7 @@ CURLcode Curl_write(struct connectdata *conn, return CURLE_OK; /* handle CURLE_AGAIN or a send failure */ - switch(curlcode) { + switch(result) { case CURLE_AGAIN: *written = 0; return CURLE_OK; @@ -249,7 +246,7 @@ CURLcode Curl_write(struct connectdata *conn, default: /* we got a specific curlcode, forward it */ - return curlcode; + return result; } } @@ -300,14 +297,14 @@ CURLcode Curl_write_plain(struct connectdata *conn, ssize_t *written) { ssize_t bytes_written; - CURLcode retcode; + CURLcode result; int num = (sockfd == conn->sock[SECONDARYSOCKET]); - bytes_written = Curl_send_plain(conn, num, mem, len, &retcode); + bytes_written = Curl_send_plain(conn, num, mem, len, &result); *written = bytes_written; - return retcode; + return result; } ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf, @@ -374,25 +371,21 @@ static CURLcode pausewrite(struct SessionHandle *data, } -/* Curl_client_write() sends data to the write callback(s) - - The bit pattern defines to what "streams" to write to. Body and/or header. - The defines are in sendf.h of course. - - If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the - local character encoding. This is a problem and should be changed in - the future to leave the original data alone. +/* Curl_client_chop_write() writes chunks of data not larger than + * CURL_MAX_WRITE_SIZE via client write callback(s) and + * takes care of pause requests from the callbacks. */ -CURLcode Curl_client_write(struct connectdata *conn, - int type, - char *ptr, - size_t len) +CURLcode Curl_client_chop_write(struct connectdata *conn, + int type, + char * ptr, + size_t len) { struct SessionHandle *data = conn->data; - size_t wrote; + curl_write_callback writeheader = NULL; + curl_write_callback writebody = NULL; - if(0 == len) - len = strlen(ptr); + if(!len) + return CURLE_OK; /* If reading is actually paused, we're forced to append this chunk of data to the already held data, but only if it is the same type as otherwise it @@ -417,78 +410,107 @@ CURLcode Curl_client_write(struct connectdata *conn, /* update the pointer and the size */ data->state.tempwrite = newptr; data->state.tempwritesize = newlen; - return CURLE_OK; } - if(type & CLIENTWRITE_BODY) { - if((conn->handler->protocol&PROTO_FAMILY_FTP) && - conn->proto.ftpc.transfertype == 'A') { - /* convert from the network encoding */ - CURLcode rc = Curl_convert_from_network(data, ptr, len); - /* Curl_convert_from_network calls failf if unsuccessful */ - if(rc) - return rc; + /* Determine the callback(s) to use. */ + if(type & CLIENTWRITE_BODY) + writebody = data->set.fwrite_func; + if((type & CLIENTWRITE_HEADER) && + (data->set.fwrite_header || data->set.writeheader)) { + /* + * Write headers to the same callback or to the especially setup + * header callback function (added after version 7.7.1). + */ + writeheader = + data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func; + } -#ifdef CURL_DO_LINEEND_CONV - /* convert end-of-line markers */ - len = convert_lineends(data, ptr, len); -#endif /* CURL_DO_LINEEND_CONV */ - } - /* If the previous block of data ended with CR and this block of data is - just a NL, then the length might be zero */ - if(len) { - wrote = data->set.fwrite_func(ptr, 1, len, data->set.out); - } - else { - wrote = len; - } + /* Chop data, write chunks. */ + while(len) { + size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE; + + if(writebody) { + size_t wrote = writebody(ptr, 1, chunklen, data->set.out); - if(CURL_WRITEFUNC_PAUSE == wrote) { - if(conn->handler->flags & PROTOPT_NONETWORK) { - /* 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!"); + if(CURL_WRITEFUNC_PAUSE == wrote) { + if(conn->handler->flags & PROTOPT_NONETWORK) { + /* 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!"); + return CURLE_WRITE_ERROR; + } + else + return pausewrite(data, type, ptr, len); + } + else if(wrote != chunklen) { + failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen); return CURLE_WRITE_ERROR; } - else - return pausewrite(data, type, ptr, len); } - else if(wrote != len) { - failf(data, "Failed writing body (%zu != %zu)", wrote, len); - return CURLE_WRITE_ERROR; - } - } - if((type & CLIENTWRITE_HEADER) && - (data->set.fwrite_header || data->set.writeheader) ) { - /* - * Write headers to the same callback or to the especially setup - * header callback function (added after version 7.7.1). - */ - curl_write_callback writeit= - data->set.fwrite_header?data->set.fwrite_header:data->set.fwrite_func; - - /* Note: The header is in the host encoding - regardless of the ftp transfer mode (ASCII/Image) */ - - wrote = writeit(ptr, 1, len, data->set.writeheader); - 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); - - if(wrote != len) { - failf (data, "Failed writing header"); - return CURLE_WRITE_ERROR; + if(writeheader) { + size_t wrote = writeheader(ptr, 1, chunklen, data->set.writeheader); + + 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); + + if(wrote != chunklen) { + failf (data, "Failed writing header"); + return CURLE_WRITE_ERROR; + } } + + ptr += chunklen; + len -= chunklen; } return CURLE_OK; } + +/* Curl_client_write() sends data to the write callback(s) + + The bit pattern defines to what "streams" to write to. Body and/or header. + The defines are in sendf.h of course. + + If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the + local character encoding. This is a problem and should be changed in + the future to leave the original data alone. + */ +CURLcode Curl_client_write(struct connectdata *conn, + int type, + char *ptr, + size_t len) +{ + struct SessionHandle *data = conn->data; + + if(0 == len) + len = strlen(ptr); + + /* 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; + +#ifdef CURL_DO_LINEEND_CONV + /* convert end-of-line markers */ + len = convert_lineends(data, ptr, len); +#endif /* CURL_DO_LINEEND_CONV */ + } + + return Curl_client_chop_write(conn, type, ptr, len); +} + CURLcode Curl_read_plain(curl_socket_t sockfd, char *buf, size_t bytesfromsocket, @@ -525,11 +547,11 @@ CURLcode Curl_read(struct connectdata *conn, /* connection data */ size_t sizerequested, /* max amount to read */ ssize_t *n) /* amount bytes read */ { - CURLcode curlcode = CURLE_RECV_ERROR; + CURLcode result = CURLE_RECV_ERROR; ssize_t nread = 0; size_t bytesfromsocket = 0; char *buffertofill = NULL; - bool pipelining = Curl_multi_pipeline_enabled(conn->data->multi); + bool pipelining = Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1); /* Set 'num' to 0 or 1, depending on which socket that has been sent here. If it is the second socket, we set num to 1. Otherwise to 0. This lets @@ -564,9 +586,9 @@ CURLcode Curl_read(struct connectdata *conn, /* connection data */ buffertofill = buf; } - nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &curlcode); + nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &result); if(nread < 0) - return curlcode; + return result; if(pipelining) { memcpy(buf, conn->master_buffer, nread); @@ -661,11 +683,13 @@ int Curl_debug(struct SessionHandle *data, curl_infotype type, switch (type) { case CURLINFO_HEADER_IN: w = "Header"; + /* FALLTHROUGH */ case CURLINFO_DATA_IN: t = "from"; break; case CURLINFO_HEADER_OUT: w = "Header"; + /* FALLTHROUGH */ case CURLINFO_DATA_OUT: t = "to"; break; diff --git a/lib/sendf.h b/lib/sendf.h index 39489e4..86f06cf 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -51,8 +51,10 @@ void Curl_failf(struct SessionHandle *, const char *fmt, ...); #define CLIENTWRITE_HEADER (1<<1) #define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) +CURLcode Curl_client_chop_write(struct connectdata *conn, int type, char *ptr, + size_t len) WARN_UNUSED_RESULT; CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, - size_t len); + size_t len) WARN_UNUSED_RESULT; /* internal read-function, does plain socket only */ CURLcode Curl_read_plain(curl_socket_t sockfd, diff --git a/lib/setup-os400.h b/lib/setup-os400.h index 0331464..fae8567 100644 --- a/lib/setup-os400.h +++ b/lib/setup-os400.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -37,7 +37,6 @@ typedef unsigned long u_int32_t; #include <sys/socket.h> #include <netdb.h> -#include <qsossl.h> #include <gskssl.h> #include <qsoasync.h> #include <gssapi.h> @@ -57,21 +56,6 @@ extern int Curl_getnameinfo_a(const struct sockaddr * sa, #define getnameinfo Curl_getnameinfo_a -/* SSL wrappers. */ - -extern int Curl_SSL_Init_Application_a(SSLInitApp * init_app); -#define SSL_Init_Application Curl_SSL_Init_Application_a - - -extern int Curl_SSL_Init_a(SSLInit * init); -#define SSL_Init Curl_SSL_Init_a - - -extern char * Curl_SSL_Strerror_a(int sslreturnvalue, - SSLErrorMsg * serrmsgp); -#define SSL_Strerror Curl_SSL_Strerror_a - - /* GSKit wrappers. */ extern int Curl_gsk_environment_open(gsk_handle * my_env_handle); diff --git a/lib/setup-vms.h b/lib/setup-vms.h index f5eedf7..520a35d 100644 --- a/lib/setup-vms.h +++ b/lib/setup-vms.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -203,6 +203,19 @@ char * unix_path; #define CRYPTO_cleanup_all_ex_data CRYPTO_CLEANUP_ALL_EX_DATA #define CRYPTO_free CRYPTO_FREE #define CRYPTO_malloc CRYPTO_MALLOC +#define CONF_modules_load_file CONF_MODULES_LOAD_FILE +#ifdef __VAX +# ifdef VMS_OLD_SSL + /* Ancient OpenSSL on VAX/VMS missing this constant */ +# define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10 +# undef CONF_modules_load_file + static int CONF_modules_load_file(const char *filename, + const char *appname, + unsigned long flags) { + return 1; + } +# endif +#endif #define DES_ecb_encrypt DES_ECB_ENCRYPT #define DES_set_key DES_SET_KEY #define DES_set_odd_parity DES_SET_ODD_PARITY @@ -228,6 +241,7 @@ char * unix_path; #define EVP_PKEY_free EVP_PKEY_FREE #define EVP_cleanup EVP_CLEANUP #define GENERAL_NAMES_free GENERAL_NAMES_FREE +#define i2d_X509_PUBKEY I2D_X509_PUBKEY #define MD4_Final MD4_FINAL #define MD4_Init MD4_INIT #define MD4_Update MD4_UPDATE @@ -235,6 +249,9 @@ char * unix_path; #define MD5_Init MD5_INIT #define MD5_Update MD5_UPDATE #define OPENSSL_add_all_algo_noconf OPENSSL_ADD_ALL_ALGO_NOCONF +#ifndef __VAX +#define OPENSSL_load_builtin_modules OPENSSL_LOAD_BUILTIN_MODULES +#endif #define PEM_read_X509 PEM_READ_X509 #define PEM_write_bio_X509 PEM_WRITE_BIO_X509 #define PKCS12_PBE_add PKCS12_PBE_ADD @@ -258,6 +275,7 @@ char * unix_path; #define SSL_CTX_set_cipher_list SSL_CTX_SET_CIPHER_LIST #define SSL_CTX_set_def_passwd_cb_ud SSL_CTX_SET_DEF_PASSWD_CB_UD #define SSL_CTX_set_default_passwd_cb SSL_CTX_SET_DEFAULT_PASSWD_CB +#define SSL_CTX_set_msg_callback SSL_CTX_SET_MSG_CALLBACK #define SSL_CTX_set_verify SSL_CTX_SET_VERIFY #define SSL_CTX_use_PrivateKey SSL_CTX_USE_PRIVATEKEY #define SSL_CTX_use_PrivateKey_file SSL_CTX_USE_PRIVATEKEY_FILE @@ -274,6 +292,7 @@ char * unix_path; #define SSL_get_peer_cert_chain SSL_GET_PEER_CERT_CHAIN #define SSL_get_peer_certificate SSL_GET_PEER_CERTIFICATE #define SSL_get_privatekey SSL_GET_PRIVATEKEY +#define SSL_get_session SSL_GET_SESSION #define SSL_get_shutdown SSL_GET_SHUTDOWN #define SSL_get_verify_result SSL_GET_VERIFY_RESULT #define SSL_library_init SSL_LIBRARY_INIT @@ -286,14 +305,32 @@ char * unix_path; #define SSL_set_fd SSL_SET_FD #define SSL_set_session SSL_SET_SESSION #define SSL_shutdown SSL_SHUTDOWN +#define SSL_version SSL_VERSION #define SSL_write SSL_WRITE #define SSLeay SSLEAY #define SSLv23_client_method SSLV23_CLIENT_METHOD #define SSLv3_client_method SSLV3_CLIENT_METHOD #define TLSv1_client_method TLSV1_CLIENT_METHOD +#define UI_create_method UI_CREATE_METHOD +#define UI_destroy_method UI_DESTROY_METHOD +#define UI_get0_user_data UI_GET0_USER_DATA +#define UI_get_input_flags UI_GET_INPUT_FLAGS +#define UI_get_string_type UI_GET_STRING_TYPE +#define UI_create_method UI_CREATE_METHOD +#define UI_destroy_method UI_DESTROY_METHOD +#define UI_method_get_closer UI_METHOD_GET_CLOSER +#define UI_method_get_opener UI_METHOD_GET_OPENER +#define UI_method_get_reader UI_METHOD_GET_READER +#define UI_method_get_writer UI_METHOD_GET_WRITER +#define UI_method_set_closer UI_METHOD_SET_CLOSER +#define UI_method_set_opener UI_METHOD_SET_OPENER +#define UI_method_set_reader UI_METHOD_SET_READER +#define UI_method_set_writer UI_METHOD_SET_WRITER #define UI_OpenSSL UI_OPENSSL +#define UI_set_result UI_SET_RESULT #define X509V3_EXT_print X509V3_EXT_PRINT #define X509_EXTENSION_get_critical X509_EXTENSION_GET_CRITICAL +#define X509_EXTENSION_get_data X509_EXTENSION_GET_DATA #define X509_EXTENSION_get_object X509_EXTENSION_GET_OBJECT #define X509_LOOKUP_file X509_LOOKUP_FILE #define X509_NAME_ENTRY_get_data X509_NAME_ENTRY_GET_DATA @@ -318,6 +355,12 @@ char * unix_path; #define sk_pop SK_POP #define sk_pop_free SK_POP_FREE #define sk_value SK_VALUE +#ifdef __VAX +#define OPENSSL_NO_SHA256 +#endif +#define SHA256_Final SHA256_FINAL +#define SHA256_Init SHA256_INIT +#define SHA256_Update SHA256_UPDATE #define USE_UPPERCASE_GSSAPI 1 #define gss_seal GSS_SEAL @@ -383,7 +426,7 @@ char * unix_path; static void des_ecb_encrypt(const_des_cblock *input, des_cblock *output, - des_key_schedule ks,int enc) { + des_key_schedule ks, int enc) { DES_ECB_ENCRYPT(input, output, ks, enc); } #endif diff --git a/lib/share.c b/lib/share.c index b8b6bee..1720248 100644 --- a/lib/share.c +++ b/lib/share.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,9 +35,15 @@ CURLSH * curl_share_init(void) { struct Curl_share *share = calloc(1, sizeof(struct Curl_share)); - if(share) + if(share) { share->specifier |= (1<<CURL_LOCK_DATA_SHARE); + if(Curl_mk_dnscache(&share->hostcache)) { + free(share); + return NULL; + } + } + return share; } @@ -67,11 +73,6 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) share->specifier |= (1<<type); switch( type ) { case CURL_LOCK_DATA_DNS: - if(!share->hostcache) { - share->hostcache = Curl_mk_dnscache(); - if(!share->hostcache) - res = CURLSHE_NOMEM; - } break; case CURL_LOCK_DATA_COOKIE: @@ -115,10 +116,6 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) share->specifier &= ~(1<<type); switch( type ) { case CURL_LOCK_DATA_DNS: - if(share->hostcache) { - Curl_hash_destroy(share->hostcache); - share->hostcache = NULL; - } break; case CURL_LOCK_DATA_COOKIE: @@ -192,14 +189,10 @@ curl_share_cleanup(CURLSH *sh) return CURLSHE_IN_USE; } - if(share->hostcache) { - Curl_hash_destroy(share->hostcache); - share->hostcache = NULL; - } + Curl_hash_destroy(&share->hostcache); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(share->cookies) - Curl_cookie_cleanup(share->cookies); + Curl_cookie_cleanup(share->cookies); #endif #ifdef USE_SSL diff --git a/lib/share.h b/lib/share.h index 9a5128e..8e6629b 100644 --- a/lib/share.h +++ b/lib/share.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -44,7 +44,7 @@ struct Curl_share { curl_unlock_function unlockfunc; void *clientdata; - struct curl_hash *hostcache; + struct curl_hash hostcache; #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) struct CookieInfo *cookies; #endif diff --git a/lib/slist.c b/lib/slist.c index 3cac6ca..9c0b2a5 100644 --- a/lib/slist.c +++ b/lib/slist.c @@ -22,10 +22,10 @@ #include "curl_setup.h" -#include "curl_memory.h" #include "slist.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* returns last node in linked list */ diff --git a/lib/smb.c b/lib/smb.c new file mode 100644 index 0000000..d461a71 --- /dev/null +++ b/lib/smb.c @@ -0,0 +1,976 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.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_SMB) && defined(USE_NTLM) && \ + (CURL_SIZEOF_CURL_OFF_T > 4) + +#if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO) + +#define BUILDING_CURL_SMB_C + +#ifdef HAVE_PROCESS_H +#include <process.h> +#define getpid _getpid +#endif + +#include "smb.h" +#include "urldata.h" +#include "sendf.h" +#include "multiif.h" +#include "connect.h" +#include "progress.h" +#include "transfer.h" +#include "vtls/vtls.h" +#include "curl_ntlm_core.h" +#include "escape.h" +#include "curl_endian.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* Local API functions */ +static CURLcode smb_setup_connection(struct connectdata *conn); +static CURLcode smb_connect(struct connectdata *conn, bool *done); +static CURLcode smb_connection_state(struct connectdata *conn, bool *done); +static CURLcode smb_request_state(struct connectdata *conn, bool *done); +static CURLcode smb_done(struct connectdata *conn, CURLcode status, + bool premature); +static CURLcode smb_disconnect(struct connectdata *conn, bool dead); +static int smb_getsock(struct connectdata *conn, curl_socket_t *socks, + int numsocks); +static CURLcode smb_parse_url_path(struct connectdata *conn); + +/* + * SMB handler interface + */ +const struct Curl_handler Curl_handler_smb = { + "SMB", /* scheme */ + smb_setup_connection, /* setup_connection */ + ZERO_NULL, /* do_it */ + smb_done, /* done */ + ZERO_NULL, /* do_more */ + smb_connect, /* connect_it */ + smb_connection_state, /* connecting */ + smb_request_state, /* doing */ + smb_getsock, /* proto_getsock */ + smb_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + smb_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + PORT_SMB, /* defport */ + CURLPROTO_SMB, /* protocol */ + PROTOPT_NONE /* flags */ +}; + +#ifdef USE_SSL +/* + * SMBS handler interface + */ +const struct Curl_handler Curl_handler_smbs = { + "SMBS", /* scheme */ + smb_setup_connection, /* setup_connection */ + ZERO_NULL, /* do_it */ + smb_done, /* done */ + ZERO_NULL, /* do_more */ + smb_connect, /* connect_it */ + smb_connection_state, /* connecting */ + smb_request_state, /* doing */ + smb_getsock, /* proto_getsock */ + smb_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + smb_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + PORT_SMBS, /* defport */ + CURLPROTO_SMBS, /* protocol */ + PROTOPT_SSL /* flags */ +}; +#endif + +#define MAX_PAYLOAD_SIZE 0x8000 +#define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) +#define CLIENTNAME "curl" +#define SERVICENAME "?????" + +/* Append a string to an SMB message */ +#define MSGCAT(str) \ + strcpy(p, (str)); \ + p += strlen(str); + +/* Append a null-terminated string to an SMB message */ +#define MSGCATNULL(str) \ + strcpy(p, (str)); \ + p += strlen(str) + 1; + +/* SMB is mostly little endian */ +#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ + defined(__OS400__) +static unsigned short smb_swap16(unsigned short x) +{ + return (x << 8) | ((x >> 8) & 0xff); +} + +static unsigned int smb_swap32(unsigned int x) +{ + return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) | + ((x >> 24) & 0xff); +} + +#ifdef HAVE_LONGLONG +static unsigned long long smb_swap64(unsigned long long x) +{ + return ((unsigned long long)smb_swap32(x) << 32) | smb_swap32(x >> 32); +} +#else +static unsigned __int64 smb_swap64(unsigned __int64 x) +{ + return ((unsigned __int64)smb_swap32(x) << 32) | smb_swap32(x >> 32); +} +#endif +#else +# define smb_swap16(x) (x) +# define smb_swap32(x) (x) +# define smb_swap64(x) (x) +#endif + +/* SMB request state */ +enum smb_req_state { + SMB_REQUESTING, + SMB_TREE_CONNECT, + SMB_OPEN, + SMB_DOWNLOAD, + SMB_UPLOAD, + SMB_CLOSE, + SMB_TREE_DISCONNECT, + SMB_DONE +}; + +/* SMB request data */ +struct smb_request { + enum smb_req_state state; + char *share; + char *path; + unsigned short tid; /* Even if we connect to the same tree as another */ + unsigned short fid; /* request, the tid will be different */ + CURLcode result; +}; + +static void conn_state(struct connectdata *conn, enum smb_conn_state newstate) +{ + struct smb_conn *smb = &conn->proto.smbc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* For debug purposes */ + static const char * const names[] = { + "SMB_NOT_CONNECTED", + "SMB_CONNECTING", + "SMB_NEGOTIATE", + "SMB_SETUP", + "SMB_CONNECTED", + /* LAST */ + }; + + if(smb->state != newstate) + infof(conn->data, "SMB conn %p state change from %s to %s\n", + (void *)smb, names[smb->state], names[newstate]); +#endif + + smb->state = newstate; +} + +static void request_state(struct connectdata *conn, + enum smb_req_state newstate) +{ + struct smb_request *req = conn->data->req.protop; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* For debug purposes */ + static const char * const names[] = { + "SMB_REQUESTING", + "SMB_TREE_CONNECT", + "SMB_OPEN", + "SMB_DOWNLOAD", + "SMB_UPLOAD", + "SMB_CLOSE", + "SMB_TREE_DISCONNECT", + "SMB_DONE", + /* LAST */ + }; + + if(req->state != newstate) + infof(conn->data, "SMB request %p state change from %s to %s\n", + (void *)req, names[req->state], names[newstate]); +#endif + + req->state = newstate; +} + +static CURLcode smb_setup_connection(struct connectdata *conn) +{ + struct smb_request *req; + + /* Initialize the request state */ + conn->data->req.protop = req = calloc(1, sizeof(struct smb_request)); + if(!req) + return CURLE_OUT_OF_MEMORY; + + /* Parse the URL path */ + return smb_parse_url_path(conn); +} + +static CURLcode smb_connect(struct connectdata *conn, bool *done) +{ + struct smb_conn *smbc = &conn->proto.smbc; + char *slash; + + (void) done; + + /* Check we have a username and password to authenticate with */ + if(!conn->bits.user_passwd) + return CURLE_LOGIN_DENIED; + + /* Initialize the connection state */ + memset(smbc, 0, sizeof(*smbc)); + smbc->state = SMB_CONNECTING; + smbc->recv_buf = malloc(MAX_MESSAGE_SIZE); + if(!smbc->recv_buf) + return CURLE_OUT_OF_MEMORY; + + /* Multiple requests are allowed with this connection */ + connkeep(conn, "SMB default"); + + /* Parse the username, domain, and password */ + slash = strchr(conn->user, '/'); + if(!slash) + slash = strchr(conn->user, '\\'); + + if(slash) { + smbc->user = slash + 1; + smbc->domain = strdup(conn->user); + if(!smbc->domain) + return CURLE_OUT_OF_MEMORY; + smbc->domain[slash - conn->user] = 0; + } + else { + smbc->user = conn->user; + smbc->domain = strdup(conn->host.name); + if(!smbc->domain) + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +static CURLcode smb_recv_message(struct connectdata *conn, void **msg) +{ + struct smb_conn *smbc = &conn->proto.smbc; + char *buf = smbc->recv_buf; + ssize_t bytes_read; + size_t nbt_size; + size_t msg_size; + size_t len = MAX_MESSAGE_SIZE - smbc->got; + CURLcode result; + + result = Curl_read(conn, FIRSTSOCKET, buf + smbc->got, len, &bytes_read); + if(result) + return result; + + if(!bytes_read) + return CURLE_OK; + + smbc->got += bytes_read; + + /* Check for a 32-bit nbt header */ + if(smbc->got < sizeof(unsigned int)) + return CURLE_OK; + + nbt_size = Curl_read16_be((unsigned char *)(buf + sizeof(unsigned short))) + + sizeof(unsigned int); + if(smbc->got < nbt_size) + return CURLE_OK; + + msg_size = sizeof(struct smb_header); + if(nbt_size >= msg_size + 1) { + /* Add the word count */ + msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short); + if(nbt_size >= msg_size + sizeof(unsigned short)) { + /* Add the byte count */ + msg_size += sizeof(unsigned short) + + Curl_read16_le((unsigned char *)&buf[msg_size]); + if(nbt_size < msg_size) + return CURLE_READ_ERROR; + } + } + + *msg = buf; + + return CURLE_OK; +} + +static void smb_pop_message(struct connectdata *conn) +{ + struct smb_conn *smbc = &conn->proto.smbc; + + smbc->got = 0; +} + +static void smb_format_message(struct connectdata *conn, struct smb_header *h, + unsigned char cmd, size_t len) +{ + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_request *req = conn->data->req.protop; + unsigned int pid; + + memset(h, 0, sizeof(*h)); + h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) + + len)); + memcpy((char *)h->magic, "\xffSMB", 4); + h->command = cmd; + h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES; + h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME); + h->uid = smb_swap16(smbc->uid); + h->tid = smb_swap16(req->tid); + pid = getpid(); + h->pid_high = smb_swap16((unsigned short)(pid >> 16)); + h->pid = smb_swap16((unsigned short) pid); +} + +static CURLcode smb_send(struct connectdata *conn, ssize_t len, + size_t upload_size) +{ + struct smb_conn *smbc = &conn->proto.smbc; + ssize_t bytes_written; + CURLcode result; + + result = Curl_write(conn, FIRSTSOCKET, conn->data->state.uploadbuffer, + len, &bytes_written); + if(result) + return result; + + if(bytes_written != len) { + smbc->send_size = len; + smbc->sent = bytes_written; + } + + smbc->upload_size = upload_size; + + return CURLE_OK; +} + +static CURLcode smb_flush(struct connectdata *conn) +{ + struct smb_conn *smbc = &conn->proto.smbc; + ssize_t bytes_written; + ssize_t len = smbc->send_size - smbc->sent; + CURLcode result; + + if(!smbc->send_size) + return CURLE_OK; + + result = Curl_write(conn, FIRSTSOCKET, + conn->data->state.uploadbuffer + smbc->sent, + len, &bytes_written); + if(result) + return result; + + if(bytes_written != len) + smbc->sent += bytes_written; + else + smbc->send_size = 0; + + return CURLE_OK; +} + +static CURLcode smb_send_message(struct connectdata *conn, unsigned char cmd, + const void *msg, size_t msg_len) +{ + smb_format_message(conn, (struct smb_header *)conn->data->state.uploadbuffer, + cmd, msg_len); + memcpy(conn->data->state.uploadbuffer + sizeof(struct smb_header), + msg, msg_len); + + return smb_send(conn, sizeof(struct smb_header) + msg_len, 0); +} + +static CURLcode smb_send_negotiate(struct connectdata *conn) +{ + const char *msg = "\x00\x0c\x00\x02NT LM 0.12"; + + return smb_send_message(conn, SMB_COM_NEGOTIATE, msg, 15); +} + +static CURLcode smb_send_setup(struct connectdata *conn) +{ + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_setup msg; + char *p = msg.bytes; + unsigned char lm_hash[21]; + unsigned char lm[24]; + unsigned char nt_hash[21]; + unsigned char nt[24]; + + size_t byte_count = sizeof(lm) + sizeof(nt); + byte_count += strlen(smbc->user) + strlen(smbc->domain); + byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ + if(byte_count > sizeof(msg.bytes)) + return CURLE_FILESIZE_EXCEEDED; + + Curl_ntlm_core_mk_lm_hash(conn->data, conn->passwd, lm_hash); + Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); +#if USE_NTRESPONSES + Curl_ntlm_core_mk_nt_hash(conn->data, 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; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE); + msg.max_mpx_count = smb_swap16(1); + msg.vc_number = smb_swap16(1); + msg.session_key = smb_swap32(smbc->session_key); + msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES); + msg.lengths[0] = smb_swap16(sizeof(lm)); + msg.lengths[1] = smb_swap16(sizeof(nt)); + memcpy(p, lm, sizeof(lm)); + p += sizeof(lm); + memcpy(p, nt, sizeof(nt)); + p += sizeof(nt); + MSGCATNULL(smbc->user); + MSGCATNULL(smbc->domain); + MSGCATNULL(OS); + MSGCATNULL(CLIENTNAME); + byte_count = p - msg.bytes; + msg.byte_count = smb_swap16((unsigned short)byte_count); + + return smb_send_message(conn, SMB_COM_SETUP_ANDX, &msg, + sizeof(msg) - sizeof(msg.bytes) + byte_count); +} + +static CURLcode smb_send_tree_connect(struct connectdata *conn) +{ + struct smb_request *req = conn->data->req.protop; + struct smb_tree_connect msg; + char *p = msg.bytes; + + size_t byte_count = strlen(conn->host.name) + strlen(req->share); + byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ + if(byte_count > sizeof(msg.bytes)) + return CURLE_FILESIZE_EXCEEDED; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_TREE_CONNECT_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + msg.pw_len = 0; + MSGCAT("\\\\"); + MSGCAT(conn->host.name); + MSGCAT("\\"); + MSGCATNULL(req->share); + MSGCATNULL(SERVICENAME); /* Match any type of service */ + byte_count = p - msg.bytes; + msg.byte_count = smb_swap16((unsigned short)byte_count); + + return smb_send_message(conn, SMB_COM_TREE_CONNECT_ANDX, &msg, + sizeof(msg) - sizeof(msg.bytes) + byte_count); +} + +static CURLcode smb_send_open(struct connectdata *conn) +{ + struct smb_request *req = conn->data->req.protop; + struct smb_nt_create msg; + size_t byte_count; + + if((strlen(req->path) + 1) > sizeof(msg.bytes)) + return CURLE_FILESIZE_EXCEEDED; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_NT_CREATE_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + byte_count = strlen(req->path); + msg.name_length = smb_swap16((unsigned short)byte_count); + msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL); + if(conn->data->set.upload) { + msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE); + msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF); + } + else { + msg.access = smb_swap32(SMB_GENERIC_READ); + msg.create_disposition = smb_swap32(SMB_FILE_OPEN); + } + msg.byte_count = smb_swap16((unsigned short) ++byte_count); + strcpy(msg.bytes, req->path); + + return smb_send_message(conn, SMB_COM_NT_CREATE_ANDX, &msg, + sizeof(msg) - sizeof(msg.bytes) + byte_count); +} + +static CURLcode smb_send_close(struct connectdata *conn) +{ + struct smb_request *req = conn->data->req.protop; + struct smb_close msg; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_CLOSE; + msg.fid = smb_swap16(req->fid); + + return smb_send_message(conn, SMB_COM_CLOSE, &msg, sizeof(msg)); +} + +static CURLcode smb_send_tree_disconnect(struct connectdata *conn) +{ + struct smb_tree_disconnect msg; + + memset(&msg, 0, sizeof(msg)); + + return smb_send_message(conn, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg)); +} + +static CURLcode smb_send_read(struct connectdata *conn) +{ + struct smb_request *req = conn->data->req.protop; + curl_off_t offset = conn->data->req.offset; + struct smb_read msg; + + memset(&msg, 0, sizeof(msg)); + msg.word_count = SMB_WC_READ_ANDX; + msg.andx.command = SMB_COM_NO_ANDX_COMMAND; + msg.fid = smb_swap16(req->fid); + msg.offset = smb_swap32((unsigned int) offset); + msg.offset_high = smb_swap32((unsigned int) (offset >> 32)); + msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE); + msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE); + + return smb_send_message(conn, SMB_COM_READ_ANDX, &msg, sizeof(msg)); +} + +static CURLcode smb_send_write(struct connectdata *conn) +{ + struct smb_write *msg = (struct smb_write *)conn->data->state.uploadbuffer; + struct smb_request *req = conn->data->req.protop; + curl_off_t offset = conn->data->req.offset; + + curl_off_t upload_size = conn->data->req.size - conn->data->req.bytecount; + if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */ + upload_size = MAX_PAYLOAD_SIZE - 1; + + memset(msg, 0, sizeof(*msg)); + msg->word_count = SMB_WC_WRITE_ANDX; + msg->andx.command = SMB_COM_NO_ANDX_COMMAND; + msg->fid = smb_swap16(req->fid); + msg->offset = smb_swap32((unsigned int) offset); + msg->offset_high = smb_swap32((unsigned int) (offset >> 32)); + msg->data_length = smb_swap16((unsigned short) upload_size); + msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int)); + msg->byte_count = smb_swap16((unsigned short) (upload_size + 1)); + + smb_format_message(conn, &msg->h, SMB_COM_WRITE_ANDX, + sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size); + + return smb_send(conn, sizeof(*msg), (size_t) upload_size); +} + +static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg) +{ + struct smb_conn *smbc = &conn->proto.smbc; + CURLcode result; + + /* Check if there is data in the transfer buffer */ + if(!smbc->send_size && smbc->upload_size) { + int nread = smbc->upload_size > BUFSIZE ? BUFSIZE : + (int) smbc->upload_size; + conn->data->req.upload_fromhere = conn->data->state.uploadbuffer; + result = Curl_fillreadbuffer(conn, nread, &nread); + if(result && result != CURLE_AGAIN) + return result; + if(!nread) + return CURLE_OK; + + smbc->upload_size -= nread; + smbc->send_size = nread; + smbc->sent = 0; + } + + /* Check if there is data to send */ + if(smbc->send_size) { + result = smb_flush(conn); + if(result) + return result; + } + + /* Check if there is still data to be sent */ + if(smbc->send_size || smbc->upload_size) + return CURLE_AGAIN; + + return smb_recv_message(conn, msg); +} + +static CURLcode smb_connection_state(struct connectdata *conn, bool *done) +{ + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_negotiate_response *nrsp; + struct smb_header *h; + CURLcode result; + void *msg = NULL; + + if(smbc->state == SMB_CONNECTING) { +#ifdef USE_SSL + if((conn->handler->flags & PROTOPT_SSL)) { + bool ssl_done; + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &ssl_done); + if(result && result != CURLE_AGAIN) + return result; + if(!ssl_done) + return CURLE_OK; + } +#endif + + result = smb_send_negotiate(conn); + if(result) { + connclose(conn, "SMB: failed to send negotiate message"); + return result; + } + + conn_state(conn, SMB_NEGOTIATE); + } + + /* Send the previous message and check for a response */ + result = smb_send_and_recv(conn, &msg); + if(result && result != CURLE_AGAIN) { + connclose(conn, "SMB: failed to communicate"); + return result; + } + + if(!msg) + return CURLE_OK; + + h = msg; + + switch(smbc->state) { + case SMB_NEGOTIATE: + if(h->status) { + connclose(conn, "SMB: negotiation failed"); + return CURLE_COULDNT_CONNECT; + } + nrsp = msg; + memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge)); + smbc->session_key = smb_swap32(nrsp->session_key); + result = smb_send_setup(conn); + if(result) { + connclose(conn, "SMB: failed to send setup message"); + return result; + } + conn_state(conn, SMB_SETUP); + break; + + case SMB_SETUP: + if(h->status) { + connclose(conn, "SMB: authentication failed"); + return CURLE_LOGIN_DENIED; + } + smbc->uid = smb_swap16(h->uid); + conn_state(conn, SMB_CONNECTED); + *done = true; + break; + + default: + smb_pop_message(conn); + return CURLE_OK; /* ignore */ + } + + smb_pop_message(conn); + + return CURLE_OK; +} + +static CURLcode smb_request_state(struct connectdata *conn, bool *done) +{ + struct smb_request *req = conn->data->req.protop; + struct smb_header *h; + enum smb_req_state next_state = SMB_DONE; + unsigned short len; + unsigned short off; + CURLcode result; + void *msg = NULL; + + /* Start the request */ + if(req->state == SMB_REQUESTING) { + result = smb_send_tree_connect(conn); + if(result) { + connclose(conn, "SMB: failed to send tree connect message"); + return result; + } + + request_state(conn, SMB_TREE_CONNECT); + } + + /* Send the previous message and check for a response */ + result = smb_send_and_recv(conn, &msg); + if(result && result != CURLE_AGAIN) { + connclose(conn, "SMB: failed to communicate"); + return result; + } + + if(!msg) + return CURLE_OK; + + h = msg; + + switch(req->state) { + case SMB_TREE_CONNECT: + if(h->status) { + req->result = CURLE_REMOTE_FILE_NOT_FOUND; + if(h->status == smb_swap32(SMB_ERR_NOACCESS)) + req->result = CURLE_REMOTE_ACCESS_DENIED; + break; + } + req->tid = smb_swap16(h->tid); + next_state = SMB_OPEN; + break; + + case SMB_OPEN: + if(h->status) { + req->result = CURLE_REMOTE_FILE_NOT_FOUND; + next_state = SMB_TREE_DISCONNECT; + break; + } + req->fid = smb_swap16(((struct smb_nt_create_response *)msg)->fid); + conn->data->req.offset = 0; + if(conn->data->set.upload) { + conn->data->req.size = conn->data->state.infilesize; + Curl_pgrsSetUploadSize(conn->data, conn->data->req.size); + next_state = SMB_UPLOAD; + } + else { + conn->data->req.size = + smb_swap64(((struct smb_nt_create_response *)msg)->end_of_file); + Curl_pgrsSetDownloadSize(conn->data, conn->data->req.size); + next_state = SMB_DOWNLOAD; + } + break; + + case SMB_DOWNLOAD: + if(h->status) { + req->result = CURLE_RECV_ERROR; + next_state = SMB_CLOSE; + break; + } + len = Curl_read16_le(((unsigned char *) msg) + + sizeof(struct smb_header) + 11); + off = Curl_read16_le(((unsigned char *) msg) + + sizeof(struct smb_header) + 13); + if(len > 0) { + struct smb_conn *smbc = &conn->proto.smbc; + if(off + sizeof(unsigned int) + len > smbc->got) { + failf(conn->data, "Invalid input packet"); + result = CURLE_RECV_ERROR; + } + else + result = Curl_client_write(conn, CLIENTWRITE_BODY, + (char *)msg + off + sizeof(unsigned int), + len); + if(result) { + req->result = result; + next_state = SMB_CLOSE; + break; + } + } + conn->data->req.bytecount += len; + conn->data->req.offset += len; + Curl_pgrsSetDownloadCounter(conn->data, conn->data->req.bytecount); + next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD; + break; + + case SMB_UPLOAD: + if(h->status) { + req->result = CURLE_UPLOAD_FAILED; + next_state = SMB_CLOSE; + break; + } + len = Curl_read16_le(((unsigned char *) msg) + + sizeof(struct smb_header) + 5); + conn->data->req.bytecount += len; + conn->data->req.offset += len; + Curl_pgrsSetUploadCounter(conn->data, conn->data->req.bytecount); + if(conn->data->req.bytecount >= conn->data->req.size) + next_state = SMB_CLOSE; + else + next_state = SMB_UPLOAD; + break; + + case SMB_CLOSE: + /* We don't care if the close failed, proceed to tree disconnect anyway */ + next_state = SMB_TREE_DISCONNECT; + break; + + case SMB_TREE_DISCONNECT: + next_state = SMB_DONE; + break; + + default: + smb_pop_message(conn); + return CURLE_OK; /* ignore */ + } + + smb_pop_message(conn); + + switch(next_state) { + case SMB_OPEN: + result = smb_send_open(conn); + break; + + case SMB_DOWNLOAD: + result = smb_send_read(conn); + break; + + case SMB_UPLOAD: + result = smb_send_write(conn); + break; + + case SMB_CLOSE: + result = smb_send_close(conn); + break; + + case SMB_TREE_DISCONNECT: + result = smb_send_tree_disconnect(conn); + break; + + case SMB_DONE: + result = req->result; + *done = true; + break; + + default: + break; + } + + if(result) { + connclose(conn, "SMB: failed to send message"); + return result; + } + + request_state(conn, next_state); + + return CURLE_OK; +} + +static CURLcode smb_done(struct connectdata *conn, CURLcode status, + bool premature) +{ + struct smb_request *req = conn->data->req.protop; + + (void) premature; + + Curl_safefree(req->share); + Curl_safefree(conn->data->req.protop); + + return status; +} + +static CURLcode smb_disconnect(struct connectdata *conn, bool dead) +{ + struct smb_conn *smbc = &conn->proto.smbc; + struct smb_request *req = conn->data->req.protop; + + (void) dead; + + Curl_safefree(smbc->domain); + Curl_safefree(smbc->recv_buf); + + /* smb_done is not always called, so cleanup the request */ + if(req) { + Curl_safefree(req->share); + Curl_safefree(conn->data->req.protop); + } + + return CURLE_OK; +} + +static int smb_getsock(struct connectdata *conn, curl_socket_t *socks, + int numsocks) +{ + struct smb_conn *smbc = &conn->proto.smbc; + + if(!numsocks) + return GETSOCK_BLANK; + + socks[0] = conn->sock[FIRSTSOCKET]; + + if(smbc->send_size || smbc->upload_size) + return GETSOCK_WRITESOCK(0); + + return GETSOCK_READSOCK(0); +} + +static CURLcode smb_parse_url_path(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct smb_request *req = data->req.protop; + char *path; + char *slash; + + /* URL decode the path */ + result = Curl_urldecode(data, data->state.path, 0, &path, NULL, TRUE); + if(result) + return result; + + /* Parse the path for the share */ + req->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path); + if(!req->share) { + free(path); + + return CURLE_OUT_OF_MEMORY; + } + + slash = strchr(req->share, '/'); + if(!slash) + slash = strchr(req->share, '\\'); + + /* The share must be present */ + if(!slash) { + free(path); + + return CURLE_URL_MALFORMAT; + } + + /* Parse the path for the file path converting any forward slashes into + backslashes */ + *slash++ = 0; + req->path = slash; + for(; *slash; slash++) { + if(*slash == '/') + *slash = '\\'; + } + + free(path); + + return CURLE_OK; +} + +#endif /* !USE_WINDOWS_SSPI || USE_WIN32_CRYPTO */ + +#endif /* CURL_DISABLE_SMB && USE_NTLM && CURL_SIZEOF_CURL_OFF_T > 4 */ diff --git a/lib/smb.h b/lib/smb.h new file mode 100644 index 0000000..7852fa1 --- /dev/null +++ b/lib/smb.h @@ -0,0 +1,271 @@ +#ifndef HEADER_CURL_SMB_H +#define HEADER_CURL_SMB_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * + * 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 http://curl.haxx.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. + * + ***************************************************************************/ + +enum smb_conn_state { + SMB_NOT_CONNECTED = 0, + SMB_CONNECTING, + SMB_NEGOTIATE, + SMB_SETUP, + SMB_CONNECTED +}; + +struct smb_conn { + enum smb_conn_state state; + char *user; + char *domain; + unsigned char challenge[8]; + unsigned int session_key; + unsigned short uid; + char *recv_buf; + size_t upload_size; + size_t send_size; + size_t sent; + size_t got; +}; + +/* + * Definitions for SMB protocol data structures + */ +#ifdef BUILDING_CURL_SMB_C + +#if defined(_MSC_VER) || defined(__ILEC400__) +# define PACK +# pragma pack(push) +# pragma pack(1) +#elif defined(__GNUC__) +# define PACK __attribute__((packed)) +#else +# define PACK +#endif + +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_READ_ANDX 0x2e +#define SMB_COM_WRITE_ANDX 0x2f +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SETUP_ANDX 0x73 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_CREATE_ANDX 0xa2 +#define SMB_COM_NO_ANDX_COMMAND 0xff + +#define SMB_WC_CLOSE 0x03 +#define SMB_WC_READ_ANDX 0x0c +#define SMB_WC_WRITE_ANDX 0x0e +#define SMB_WC_SETUP_ANDX 0x0d +#define SMB_WC_TREE_CONNECT_ANDX 0x04 +#define SMB_WC_NT_CREATE_ANDX 0x18 + +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 +#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 + +#define SMB_CAP_LARGE_FILES 0x08 +#define SMB_GENERIC_WRITE 0x40000000 +#define SMB_GENERIC_READ 0x80000000 +#define SMB_FILE_SHARE_ALL 0x07 +#define SMB_FILE_OPEN 0x01 +#define SMB_FILE_OVERWRITE_IF 0x05 + +#define SMB_ERR_NOACCESS 0x00050001 + +struct smb_header { + unsigned char nbt_type; + unsigned char nbt_flags; + unsigned short nbt_length; + unsigned char magic[4]; + unsigned char command; + unsigned int status; + unsigned char flags; + unsigned short flags2; + unsigned short pid_high; + unsigned char signature[8]; + unsigned short pad; + unsigned short tid; + unsigned short pid; + unsigned short uid; + unsigned short mid; +} PACK; + +struct smb_negotiate_response { + struct smb_header h; + unsigned char word_count; + unsigned short dialect_index; + unsigned char security_mode; + unsigned short max_mpx_count; + unsigned short max_number_vcs; + unsigned int max_buffer_size; + unsigned int max_raw_size; + unsigned int session_key; + unsigned int capabilities; + unsigned int system_time_low; + unsigned int system_time_high; + unsigned short server_time_zone; + unsigned char encryption_key_length; + unsigned short byte_count; + char bytes[1]; +} PACK; + +struct andx { + unsigned char command; + unsigned char pad; + unsigned short offset; +} PACK; + +struct smb_setup { + unsigned char word_count; + struct andx andx; + unsigned short max_buffer_size; + unsigned short max_mpx_count; + unsigned short vc_number; + unsigned int session_key; + unsigned short lengths[2]; + unsigned int pad; + unsigned int capabilities; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_tree_connect { + unsigned char word_count; + struct andx andx; + unsigned short flags; + unsigned short pw_len; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create { + unsigned char word_count; + struct andx andx; + unsigned char pad; + unsigned short name_length; + unsigned int flags; + unsigned int root_fid; + unsigned int access; +#ifdef HAVE_LONGLONG + unsigned long long allocation_size; +#else + unsigned __int64 allocation_size; +#endif + unsigned int ext_file_attributes; + unsigned int share_access; + unsigned int create_disposition; + unsigned int create_options; + unsigned int impersonation_level; + unsigned char security_flags; + unsigned short byte_count; + char bytes[1024]; +} PACK; + +struct smb_nt_create_response { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned char op_lock_level; + unsigned short fid; + unsigned int create_disposition; +#ifdef HAVE_LONGLONG + unsigned long long create_time; + unsigned long long last_access_time; + unsigned long long last_write_time; + unsigned long long last_change_time; +#else + unsigned __int64 create_time; + unsigned __int64 last_access_time; + unsigned __int64 last_write_time; + unsigned __int64 last_change_time; +#endif + unsigned int ext_file_attributes; +#ifdef HAVE_LONGLONG + unsigned long long allocation_size; + unsigned long long end_of_file; +#else + unsigned __int64 allocation_size; + unsigned __int64 end_of_file; +#endif +} PACK; + +struct smb_read { + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned short max_bytes; + unsigned short min_bytes; + unsigned int timeout; + unsigned short remaining; + unsigned int offset_high; + unsigned short byte_count; +} PACK; + +struct smb_write { + struct smb_header h; + unsigned char word_count; + struct andx andx; + unsigned short fid; + unsigned int offset; + unsigned int timeout; + unsigned short write_mode; + unsigned short remaining; + unsigned short pad; + unsigned short data_length; + unsigned short data_offset; + unsigned int offset_high; + unsigned short byte_count; + unsigned char pad2; +} PACK; + +struct smb_close { + unsigned char word_count; + unsigned short fid; + unsigned int last_mtime; + unsigned short byte_count; +} PACK; + +struct smb_tree_disconnect { + unsigned char word_count; + unsigned short byte_count; +} PACK; + +#if defined(_MSC_VER) || defined(__ILEC400__) +# pragma pack(pop) +#endif + +#endif /* BUILDING_CURL_SMB_C */ + +#if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \ + (CURL_SIZEOF_CURL_OFF_T > 4) + +#if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO) + +extern const struct Curl_handler Curl_handler_smb; +extern const struct Curl_handler Curl_handler_smbs; + +#endif /* !USE_WINDOWS_SSPI || USE_WIN32_CRYPTO */ + +#endif /* CURL_DISABLE_SMB && USE_NTLM && CURL_SIZEOF_CURL_OFF_T > 4 */ + +#endif /* HEADER_CURL_SMB_H */ @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -62,7 +62,6 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" -#include "if2ip.h" #include "hostip.h" #include "progress.h" #include "transfer.h" @@ -83,10 +82,7 @@ #include "curl_gethostname.h" #include "curl_sasl.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -106,10 +102,10 @@ static CURLcode smtp_setup_connection(struct connectdata *conn); static CURLcode smtp_parse_url_options(struct connectdata *conn); static CURLcode smtp_parse_url_path(struct connectdata *conn); static CURLcode smtp_parse_custom_request(struct connectdata *conn); -static CURLcode smtp_calc_sasl_details(struct connectdata *conn, - const char **mech, - char **initresp, size_t *len, - smtpstate *state1, smtpstate *state2); +static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, + const char *initresp); +static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp); +static void smtp_get_message(char *buffer, char** outptr); /* * SMTP protocol handler. @@ -214,6 +210,17 @@ static const struct Curl_handler Curl_handler_smtps_proxy = { #endif #endif +/* SASL parameters for the smtp protocol */ +static const struct SASLproto saslsmtp = { + "smtp", /* The service name */ + 334, /* Code received when continuation is expected */ + 235, /* Code to receive upon authentication success */ + 512 - 8, /* Maximum initial response length (no max) */ + smtp_perform_auth, /* Send authentication command */ + smtp_continue_auth, /* Send authentication continuation */ + smtp_get_message /* Get SASL response message */ +}; + #ifdef USE_SSL static void smtp_to_smtps(struct connectdata *conn) { @@ -310,20 +317,7 @@ static void state(struct connectdata *conn, smtpstate newstate) "HELO", "STARTTLS", "UPGRADETLS", - "AUTH_PLAIN", - "AUTH_LOGIN", - "AUTH_LOGIN_PASSWD", - "AUTH_CRAMMD5", - "AUTH_DIGESTMD5", - "AUTH_DIGESTMD5_RESP", - "AUTH_NTLM", - "AUTH_NTLM_TYPE2MSG", - "AUTH_GSSAPI", - "AUTH_GSSAPI_TOKEN", - "AUTH_GSSAPI_NO_DATA", - "AUTH_XOAUTH2", - "AUTH_CANCEL", - "AUTH_FINAL", + "AUTH", "COMMAND", "MAIL", "RCPT", @@ -353,11 +347,11 @@ static CURLcode smtp_perform_ehlo(struct connectdata *conn) CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - smtpc->authmechs = 0; /* No known authentication mechanisms yet */ - smtpc->authused = 0; /* Clear the authentication mechanism used - for esmtp connections */ - smtpc->tls_supported = FALSE; /* Clear the TLS capability */ - smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ + smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ + smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism + used for esmtp connections */ + smtpc->tls_supported = FALSE; /* Clear the TLS capability */ + smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ /* Send the EHLO command */ result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain); @@ -379,8 +373,8 @@ static CURLcode smtp_perform_helo(struct connectdata *conn) CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - smtpc->authused = 0; /* No authentication mechanism used in smtp - connections */ + smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used + in smtp connections */ /* Send the HELO command */ result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain); @@ -446,25 +440,18 @@ static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) */ static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, - const char *initresp, size_t len, - smtpstate state1, smtpstate state2) + const char *initresp) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - if(initresp && 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */ + if(initresp) { /* AUTH <mech> ...<crlf> */ /* Send the AUTH command with the initial response */ result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); - - if(!result) - state(conn, state2); } else { /* Send the AUTH command */ result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); - - if(!result) - state(conn, state1); } return result; @@ -472,6 +459,19 @@ static CURLcode smtp_perform_auth(struct connectdata *conn, /*********************************************************************** * + * smtp_continue_auth() + * + * Sends SASL continuation data or cancellation. + */ +static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp) +{ + struct smtp_conn *smtpc = &conn->proto.smtpc; + + return Curl_pp_sendf(&smtpc->pp, "%s", resp); +} + +/*********************************************************************** + * * smtp_perform_authentication() * * Initiates the authentication sequence, with the appropriate SASL @@ -481,31 +481,22 @@ static CURLcode smtp_perform_authentication(struct connectdata *conn) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *mech = NULL; - char *initresp = NULL; - size_t len = 0; - smtpstate state1 = SMTP_STOP; - smtpstate state2 = SMTP_STOP; + saslprogress progress; - /* Check we have a username and password to authenticate with, and the + /* Check we have enough data to authenticate with, and the server supports authentiation, and end the connect phase if not */ - if(!conn->bits.user_passwd || !smtpc->auth_supported) { + if(!smtpc->auth_supported || + !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { state(conn, SMTP_STOP); - return result; } /* Calculate the SASL login details */ - result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1, - &state2); + result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress); if(!result) { - if(mech) { - /* Perform SASL based authentication */ - result = smtp_perform_auth(conn, mech, initresp, len, state1, state2); - - Curl_safefree(initresp); - } + if(progress == SASL_INPROGRESS) + state(conn, SMTP_AUTH); else { /* Other mechanisms not supported */ infof(conn->data, "No known authentication mechanisms supported!\n"); @@ -572,7 +563,7 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) return CURLE_OUT_OF_MEMORY; /* Calculate the optional AUTH parameter */ - if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) { + if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { if(data->set.str[STRING_MAIL_AUTH][0] != '\0') auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); else @@ -580,7 +571,7 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) auth = strdup("<>"); if(!auth) { - Curl_safefree(from); + free(from); return CURLE_OUT_OF_MEMORY; } @@ -591,8 +582,8 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); if(!size) { - Curl_safefree(from); - Curl_safefree(auth); + free(from); + free(auth); return CURLE_OUT_OF_MEMORY; } @@ -612,9 +603,9 @@ static CURLcode smtp_perform_mail(struct connectdata *conn) result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s SIZE=%s", from, size); - Curl_safefree(from); - Curl_safefree(auth); - Curl_safefree(size); + free(from); + free(auth); + free(size); if(!result) state(conn, SMTP_MAIL); @@ -754,6 +745,9 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, /* Loop through the data line */ for(;;) { + size_t llen; + unsigned int mechbit; + while(len && (*line == ' ' || *line == '\t' || *line == '\r' || *line == '\n')) { @@ -772,22 +766,9 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, wordlen++; /* Test the word for a matching authentication mechanism */ - if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) - smtpc->authmechs |= SASL_MECH_LOGIN; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) - smtpc->authmechs |= SASL_MECH_PLAIN; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) - smtpc->authmechs |= SASL_MECH_CRAM_MD5; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) - smtpc->authmechs |= SASL_MECH_DIGEST_MD5; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) - smtpc->authmechs |= SASL_MECH_GSSAPI; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) - smtpc->authmechs |= SASL_MECH_EXTERNAL; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) - smtpc->authmechs |= SASL_MECH_NTLM; - else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) - smtpc->authmechs |= SASL_MECH_XOAUTH2; + if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) && + llen == wordlen) + smtpc->sasl.authmechs |= mechbit; line += wordlen; len -= wordlen; @@ -836,565 +817,31 @@ static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode, return result; } -/* For AUTH PLAIN (without initial response) responses */ -static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *plainauth = NULL; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the authorisation message */ - result = Curl_sasl_create_plain_message(conn->data, conn->user, - conn->passwd, &plainauth, &len); - if(!result && plainauth) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - } - - Curl_safefree(plainauth); - - return result; -} - -/* For AUTH LOGIN (without initial response) responses */ -static CURLcode smtp_state_auth_login_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *authuser = NULL; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the user message */ - result = Curl_sasl_create_login_message(conn->data, conn->user, - &authuser, &len); - if(!result && authuser) { - /* Send the user */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser); - - if(!result) - state(conn, SMTP_AUTH_LOGIN_PASSWD); - } - } - - Curl_safefree(authuser); - - return result; -} - -/* For AUTH LOGIN user entry responses */ -static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *authpasswd = NULL; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the password message */ - result = Curl_sasl_create_login_message(conn->data, conn->passwd, - &authpasswd, &len); - if(!result && authpasswd) { - /* Send the password */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - } - - Curl_safefree(authpasswd); - - return result; -} - -#ifndef CURL_DISABLE_CRYPTO_AUTH -/* For AUTH CRAM-MD5 responses */ -static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlg = NULL; - char *chlg64 = NULL; - char *rplyb64 = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - return CURLE_LOGIN_DENIED; - } - - /* Get the challenge message */ - smtp_get_message(data->state.buffer, &chlg64); - - /* Decode the challenge message */ - result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len); - if(result) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); - - if(!result) - state(conn, SMTP_AUTH_CANCEL); - } - else { - /* Create the response message */ - result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user, - conn->passwd, &rplyb64, &len); - if(!result && rplyb64) { - /* Send the response */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - } - - Curl_safefree(chlg); - Curl_safefree(rplyb64); - - return result; -} - -/* For AUTH DIGEST-MD5 challenge responses */ -static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlg64 = NULL; - char *rplyb64 = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - return CURLE_LOGIN_DENIED; - } - - /* Get the challenge message */ - smtp_get_message(data->state.buffer, &chlg64); - - /* Create the response message */ - result = Curl_sasl_create_digest_md5_message(data, chlg64, - conn->user, conn->passwd, - "smtp", &rplyb64, &len); - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); - - if(!result) - state(conn, SMTP_AUTH_CANCEL); - } - } - else { - /* Send the response */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); - - if(!result) - state(conn, SMTP_AUTH_DIGESTMD5_RESP); - } - - Curl_safefree(rplyb64); - - return result; -} - -/* For AUTH DIGEST-MD5 challenge-response responses */ -static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Authentication failed: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", ""); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - - return result; -} - -#endif - -#ifdef USE_NTLM -/* For AUTH NTLM (without initial response) responses */ -static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *type1msg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the type-1 message */ - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - &type1msg, &len); - if(!result && type1msg) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg); - - if(!result) - state(conn, SMTP_AUTH_NTLM_TYPE2MSG); - } - } - - Curl_safefree(type1msg); - - return result; -} - -/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ -static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *type2msg = NULL; - char *type3msg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the type-2 message */ - smtp_get_message(data->state.buffer, &type2msg); - - /* Decode the type-2 message */ - result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); - if(result) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); - - if(!result) - state(conn, SMTP_AUTH_CANCEL); - } - else { - /* Create the type-3 message */ - result = Curl_sasl_create_ntlm_type3_message(data, conn->user, - conn->passwd, &conn->ntlm, - &type3msg, &len); - if(!result && type3msg) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - } - } - - Curl_safefree(type3msg); - - return result; -} -#endif - -#if defined(USE_WINDOWS_SSPI) -/* For AUTH GSSAPI (without initial response) responses */ -static CURLcode smtp_state_auth_gssapi_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct smtp_conn *smtpc = &conn->proto.smtpc; - char *respmsg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the initial response message */ - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, "smtp", - smtpc->mutual_auth, NULL, - &conn->krb5, - &respmsg, &len); - if(!result && respmsg) { - /* Send the message */ - result = Curl_pp_sendf(&smtpc->pp, "%s", respmsg); - - if(!result) - state(conn, SMTP_AUTH_GSSAPI_TOKEN); - } - } - - Curl_safefree(respmsg); - - return result; -} - -/* For AUTH GSSAPI user token responses */ -static CURLcode smtp_state_auth_gssapi_token_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct smtp_conn *smtpc = &conn->proto.smtpc; - char *chlgmsg = NULL; - char *respmsg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - smtp_get_message(data->state.buffer, &chlgmsg); - - if(smtpc->mutual_auth) - /* Decode the user token challenge and create the optional response - message */ - result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL, - smtpc->mutual_auth, - chlgmsg, &conn->krb5, - &respmsg, &len); - else - /* Decode the security challenge and create the response message */ - result = Curl_sasl_create_gssapi_security_message(data, chlgmsg, - &conn->krb5, - &respmsg, &len); - - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&smtpc->pp, "%s", "*"); - - if(!result) - state(conn, SMTP_AUTH_CANCEL); - } - } - else { - /* Send the response */ - if(respmsg) - result = Curl_pp_sendf(&smtpc->pp, "%s", respmsg); - else - result = Curl_pp_sendf(&smtpc->pp, "%s", ""); - - if(!result) - state(conn, (smtpc->mutual_auth ? SMTP_AUTH_GSSAPI_NO_DATA : - SMTP_AUTH_FINAL)); - } - } - - Curl_safefree(respmsg); - - return result; -} - -/* For AUTH GSSAPI no data responses */ -static CURLcode smtp_state_auth_gssapi_no_data_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - char *chlgmsg = NULL; - char *respmsg = NULL; - size_t len = 0; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Get the challenge message */ - smtp_get_message(data->state.buffer, &chlgmsg); - - /* Decode the security challenge and create the response message */ - result = Curl_sasl_create_gssapi_security_message(data, chlgmsg, - &conn->krb5, - &respmsg, &len); - if(result) { - if(result == CURLE_BAD_CONTENT_ENCODING) { - /* Send the cancellation */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); - - if(!result) - state(conn, SMTP_AUTH_CANCEL); - } - } - else { - /* Send the response */ - if(respmsg) { - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", respmsg); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - } - } - - Curl_safefree(respmsg); - - return result; -} -#endif - -/* For AUTH XOAUTH2 (without initial response) responses */ -static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn, - int smtpcode, smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - size_t len = 0; - char *xoauth = NULL; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* Create the authorisation message */ - result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, - conn->xoauth2_bearer, - &xoauth, &len); - if(!result && xoauth) { - /* Send the message */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth); - - if(!result) - state(conn, SMTP_AUTH_FINAL); - } - } - - Curl_safefree(xoauth); - - return result; -} - -/* For AUTH cancellation responses */ -static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/* For SASL authentication responses */ +static CURLcode smtp_state_auth_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *mech = NULL; - char *initresp = NULL; - size_t len = 0; - smtpstate state1 = SMTP_STOP; - smtpstate state2 = SMTP_STOP; + saslprogress progress; - (void)smtpcode; (void)instate; /* no use for this yet */ - /* Remove the offending mechanism from the supported list */ - smtpc->authmechs ^= smtpc->authused; - - /* Calculate alternative SASL login details */ - result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1, - &state2); - - if(!result) { - /* Do we have any mechanisms left? */ - if(mech) { - /* Retry SASL based authentication */ - result = smtp_perform_auth(conn, mech, initresp, len, state1, state2); - - Curl_safefree(initresp); - } - else { + result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + state(conn, SMTP_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ failf(data, "Authentication cancelled"); - result = CURLE_LOGIN_DENIED; + break; + default: + break; } - } - - return result; -} - -/* For final responses in the AUTH sequence */ -static CURLcode smtp_state_auth_final_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 235) { - failf(data, "Authentication failed: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else - /* End of connect phase */ - state(conn, SMTP_STOP); return result; } @@ -1592,69 +1039,8 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state); break; - case SMTP_AUTH_PLAIN: - result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_LOGIN: - result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_LOGIN_PASSWD: - result = smtp_state_auth_login_password_resp(conn, smtpcode, - smtpc->state); - break; - -#ifndef CURL_DISABLE_CRYPTO_AUTH - case SMTP_AUTH_CRAMMD5: - result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_DIGESTMD5: - result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_DIGESTMD5_RESP: - result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state); - break; -#endif - -#ifdef USE_NTLM - case SMTP_AUTH_NTLM: - result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_NTLM_TYPE2MSG: - result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode, - smtpc->state); - break; -#endif - -#if defined(USE_WINDOWS_SSPI) - case SMTP_AUTH_GSSAPI: - result = smtp_state_auth_gssapi_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_GSSAPI_TOKEN: - result = smtp_state_auth_gssapi_token_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_GSSAPI_NO_DATA: - result = smtp_state_auth_gssapi_no_data_resp(conn, smtpcode, - smtpc->state); - break; -#endif - - case SMTP_AUTH_XOAUTH2: - result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_CANCEL: - result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTH_FINAL: - result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state); + case SMTP_AUTH: + result = smtp_state_auth_resp(conn, smtpcode, smtpc->state); break; case SMTP_COMMAND: @@ -1767,8 +1153,8 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done) pp->endofresp = smtp_endofresp; pp->conn = conn; - /* Set the default preferred authentication mechanism */ - smtpc->prefmech = SASL_AUTH_ANY; + /* Initialize the SASL storage */ + Curl_sasl_init(&smtpc->sasl, &saslsmtp); /* Initialise the pingpong layer */ Curl_pp_init(pp); @@ -1807,7 +1193,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, struct SessionHandle *data = conn->data; struct SMTP *smtp = data->req.protop; struct pingpong *pp = &conn->proto.smtpc.pp; - const char *eob; + char *eob; ssize_t len; ssize_t bytes_written; @@ -1827,30 +1213,45 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) { /* Calculate the EOB taking into account any terminating CRLF from the previous line of the email or the CRLF of the DATA command when there - is "no mail data". RFC-5321, sect. 4.1.1.4. */ - eob = SMTP_EOB; - len = SMTP_EOB_LEN; + is "no mail data". RFC-5321, sect. 4.1.1.4. + + Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to + fail when using a different pointer following a previous write, that + returned CURLE_AGAIN, we duplicate the EOB now rather than when the + bytes written doesn't equal len. */ if(smtp->trailing_crlf || !conn->data->state.infilesize) { - eob += 2; - len -= 2; + eob = strdup(SMTP_EOB + 2); + len = SMTP_EOB_LEN - 2; + } + else { + eob = strdup(SMTP_EOB); + len = SMTP_EOB_LEN; } + if(!eob) + return CURLE_OUT_OF_MEMORY; + /* Send the end of block data */ result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); - if(result) + if(result) { + free(eob); return result; + } if(bytes_written != len) { /* The whole chunk was not sent so keep it around and adjust the pingpong structure accordingly */ - pp->sendthis = strdup(eob); + pp->sendthis = eob; pp->sendsize = len; pp->sendleft = len - bytes_written; } - else + else { /* Successfully sent so adjust the response timeout relative to now */ pp->response = Curl_tvnow(); + free(eob); + } + state(conn, SMTP_POSTDATA); /* Run the state-machine @@ -1971,7 +1372,7 @@ static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection) Curl_pp_disconnect(&smtpc->pp); /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, smtpc->authused); + Curl_sasl_cleanup(conn, smtpc->sasl.authused); /* Cleanup our connection based variables */ Curl_safefree(smtpc->domain); @@ -2092,52 +1493,30 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *options = conn->options; - const char *ptr = options; - bool reset = TRUE; + const char *ptr = conn->options; - while(ptr && *ptr) { + smtpc->sasl.resetprefs = TRUE; + + while(!result && ptr && *ptr) { const char *key = ptr; + const char *value; while(*ptr && *ptr != '=') ptr++; - if(strnequal(key, "AUTH", 4)) { - size_t len = 0; - const char *value = ++ptr; - - if(reset) { - reset = FALSE; - smtpc->prefmech = SASL_AUTH_NONE; - } + value = ptr + 1; - while(*ptr && *ptr != ';') { - ptr++; - len++; - } + while(*ptr && *ptr != ';') + ptr++; - if(strnequal(value, "*", len)) - smtpc->prefmech = SASL_AUTH_ANY; - else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) - smtpc->prefmech |= SASL_MECH_LOGIN; - else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) - smtpc->prefmech |= SASL_MECH_PLAIN; - else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) - smtpc->prefmech |= SASL_MECH_CRAM_MD5; - else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) - smtpc->prefmech |= SASL_MECH_DIGEST_MD5; - else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) - smtpc->prefmech |= SASL_MECH_GSSAPI; - else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) - smtpc->prefmech |= SASL_MECH_NTLM; - else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) - smtpc->prefmech |= SASL_MECH_XOAUTH2; - - if(*ptr == ';') - ptr++; - } + if(strnequal(key, "AUTH=", 5)) + result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, + value, ptr - value); else result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; } return result; @@ -2189,111 +1568,7 @@ static CURLcode smtp_parse_custom_request(struct connectdata *conn) return result; } -/*********************************************************************** - * - * smtp_calc_sasl_details() - * - * Calculate the required login details for SASL authentication. - */ -static CURLcode smtp_calc_sasl_details(struct connectdata *conn, - const char **mech, - char **initresp, size_t *len, - smtpstate *state1, smtpstate *state2) -{ - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - struct smtp_conn *smtpc = &conn->proto.smtpc; - - /* Calculate the supported authentication mechanism, by decreasing order of - security, as well as the initial response where appropriate */ -#if defined(USE_WINDOWS_SSPI) - if((smtpc->authmechs & SASL_MECH_GSSAPI) && - (smtpc->prefmech & SASL_MECH_GSSAPI)) { - smtpc->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ - - *mech = SASL_MECH_STRING_GSSAPI; - *state1 = SMTP_AUTH_GSSAPI; - *state2 = SMTP_AUTH_GSSAPI_TOKEN; - smtpc->authused = SASL_MECH_GSSAPI; - - if(data->set.sasl_ir) - result = Curl_sasl_create_gssapi_user_message(data, conn->user, - conn->passwd, "smtp", - smtpc->mutual_auth, - NULL, &conn->krb5, - initresp, len); - } - else -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) && - (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) { - *mech = SASL_MECH_STRING_DIGEST_MD5; - *state1 = SMTP_AUTH_DIGESTMD5; - smtpc->authused = SASL_MECH_DIGEST_MD5; - } - else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) && - (smtpc->prefmech & SASL_MECH_CRAM_MD5)) { - *mech = SASL_MECH_STRING_CRAM_MD5; - *state1 = SMTP_AUTH_CRAMMD5; - smtpc->authused = SASL_MECH_CRAM_MD5; - } - else -#endif -#ifdef USE_NTLM - if((smtpc->authmechs & SASL_MECH_NTLM) && - (smtpc->prefmech & SASL_MECH_NTLM)) { - *mech = SASL_MECH_STRING_NTLM; - *state1 = SMTP_AUTH_NTLM; - *state2 = SMTP_AUTH_NTLM_TYPE2MSG; - smtpc->authused = SASL_MECH_NTLM; - - if(data->set.sasl_ir) - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - initresp, len); - } - else -#endif - if(((smtpc->authmechs & SASL_MECH_XOAUTH2) && - (smtpc->prefmech & SASL_MECH_XOAUTH2) && - (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { - *mech = SASL_MECH_STRING_XOAUTH2; - *state1 = SMTP_AUTH_XOAUTH2; - *state2 = SMTP_AUTH_FINAL; - smtpc->authused = SASL_MECH_XOAUTH2; - - if(data->set.sasl_ir) - result = Curl_sasl_create_xoauth2_message(data, conn->user, - conn->xoauth2_bearer, - initresp, len); - } - else if((smtpc->authmechs & SASL_MECH_LOGIN) && - (smtpc->prefmech & SASL_MECH_LOGIN)) { - *mech = SASL_MECH_STRING_LOGIN; - *state1 = SMTP_AUTH_LOGIN; - *state2 = SMTP_AUTH_LOGIN_PASSWD; - smtpc->authused = SASL_MECH_LOGIN; - - if(data->set.sasl_ir) - result = Curl_sasl_create_login_message(data, conn->user, initresp, len); - } - else if((smtpc->authmechs & SASL_MECH_PLAIN) && - (smtpc->prefmech & SASL_MECH_PLAIN)) { - *mech = SASL_MECH_STRING_PLAIN; - *state1 = SMTP_AUTH_PLAIN; - *state2 = SMTP_AUTH_FINAL; - smtpc->authused = SASL_MECH_PLAIN; - - if(data->set.sasl_ir) - result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, - initresp, len); - } - - return result; -} - -CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) +CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread) { /* When sending a SMTP payload we must detect CRLF. sequences making sure they are sent as CRLF.. instead, as a . on the beginning of a line will @@ -2305,17 +1580,26 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) ssize_t si; struct SessionHandle *data = conn->data; struct SMTP *smtp = data->req.protop; + char *scratch = data->state.scratch; + char *newscratch = NULL; + char *oldscratch = NULL; + size_t eob_sent; + + /* Do we need to allocate a scratch buffer? */ + if(!scratch || data->set.crlf) { + oldscratch = scratch; - /* Do we need to allocate the scatch buffer? */ - if(!data->state.scratch) { - data->state.scratch = malloc(2 * BUFSIZE); + scratch = newscratch = malloc(2 * BUFSIZE); + if(!newscratch) { + failf(data, "Failed to alloc scratch buffer!"); - if(!data->state.scratch) { - failf (data, "Failed to alloc scratch buffer!"); return CURLE_OUT_OF_MEMORY; } } + /* Have we already sent part of the EOB? */ + eob_sent = smtp->eob; + /* This loop can be improved by some kind of Boyer-Moore style of approach but that is saved for later... */ for(i = 0, si = 0; i < nread; i++) { @@ -2330,8 +1614,8 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) } else if(smtp->eob) { /* A previous substring matched so output that first */ - memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); - si += smtp->eob; + memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); + si += smtp->eob - eob_sent; /* Then compare the first byte */ if(SMTP_EOB[0] == data->req.upload_fromhere[i]) @@ -2339,6 +1623,8 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) else smtp->eob = 0; + eob_sent = 0; + /* Reset the trailing CRLF flag as there was more data */ smtp->trailing_crlf = FALSE; } @@ -2346,31 +1632,38 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ if(SMTP_EOB_FIND_LEN == smtp->eob) { /* Copy the replacement data to the target buffer */ - memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN); - si += SMTP_EOB_REPL_LEN; + memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent], + SMTP_EOB_REPL_LEN - eob_sent); + si += SMTP_EOB_REPL_LEN - eob_sent; smtp->eob = 0; + eob_sent = 0; } else if(!smtp->eob) - data->state.scratch[si++] = data->req.upload_fromhere[i]; + scratch[si++] = data->req.upload_fromhere[i]; } - if(smtp->eob) { + if(smtp->eob - eob_sent) { /* A substring matched before processing ended so output that now */ - memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); - si += smtp->eob; - smtp->eob = 0; + memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); + si += smtp->eob - eob_sent; } + /* Only use the new buffer if we replaced something */ if(si != nread) { - /* Only use the new buffer if we replaced something */ - nread = si; - /* Upload from the new (replaced) buffer instead */ - data->req.upload_fromhere = data->state.scratch; + data->req.upload_fromhere = scratch; + + /* Save the buffer so it can be freed later */ + data->state.scratch = scratch; + + /* Free the old scratch buffer */ + free(oldscratch); /* Set the new amount too */ - data->req.upload_present = nread; + data->req.upload_present = si; } + else + free(newscratch); return CURLE_OK; } @@ -23,6 +23,7 @@ ***************************************************************************/ #include "pingpong.h" +#include "curl_sasl.h" /**************************************************************************** * SMTP unique setup @@ -36,20 +37,7 @@ typedef enum { SMTP_STARTTLS, SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS (multi mode only) */ - SMTP_AUTH_PLAIN, - SMTP_AUTH_LOGIN, - SMTP_AUTH_LOGIN_PASSWD, - SMTP_AUTH_CRAMMD5, - SMTP_AUTH_DIGESTMD5, - SMTP_AUTH_DIGESTMD5_RESP, - SMTP_AUTH_NTLM, - SMTP_AUTH_NTLM_TYPE2MSG, - SMTP_AUTH_GSSAPI, - SMTP_AUTH_GSSAPI_TOKEN, - SMTP_AUTH_GSSAPI_NO_DATA, - SMTP_AUTH_XOAUTH2, - SMTP_AUTH_CANCEL, - SMTP_AUTH_FINAL, + SMTP_AUTH, SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */ SMTP_MAIL, /* MAIL FROM */ SMTP_RCPT, /* RCPT TO */ @@ -79,14 +67,11 @@ struct smtp_conn { smtpstate state; /* Always use smtp.c:state() to change state! */ bool ssldone; /* Is connect() over SSL done? */ char *domain; /* Client address/name to send in the EHLO */ - unsigned int authmechs; /* Accepted authentication mechanisms */ - unsigned int prefmech; /* Preferred authentication mechanism */ - unsigned int authused; /* Auth mechanism used for the connection */ + struct SASL sasl; /* SASL-related storage */ bool tls_supported; /* StartTLS capability supported by server */ bool size_supported; /* If server supports SIZE extension according to RFC 1870 */ bool auth_supported; /* AUTH capability supported by server */ - bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ }; extern const struct Curl_handler Curl_handler_smtp; @@ -101,6 +86,6 @@ extern const struct Curl_handler Curl_handler_smtps; #define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e" #define SMTP_EOB_REPL_LEN 4 -CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread); +CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread); #endif /* HEADER_CURL_SMTP_H */ diff --git a/lib/socks.c b/lib/socks.c index 028475c..7d3f782 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -127,7 +127,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name, return CURLE_OPERATION_TIMEDOUT; } - curlx_nonblock(sock, FALSE); + (void)curlx_nonblock(sock, FALSE); infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port); @@ -238,7 +238,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name, code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize + hostnamelen, &written); - if((code != CURLE_OK) || (written != packetsize + hostnamelen)) { + if(code || (written != packetsize + hostnamelen)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } @@ -247,7 +247,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name, hostnamelen = (ssize_t)strlen(hostname) + 1; code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen, &written); - if((code != CURLE_OK) || (written != hostnamelen)) { + if(code || (written != hostnamelen)) { failf(data, "Failed to send SOCKS4 connect request."); return CURLE_COULDNT_CONNECT; } @@ -258,7 +258,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name, /* Receive response */ result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize, &actualread); - if((result != CURLE_OK) || (actualread != packetsize)) { + if(result || (actualread != packetsize)) { failf(data, "Failed to receive SOCKS4 connect request ack."); return CURLE_COULDNT_CONNECT; } @@ -335,7 +335,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name, } } - curlx_nonblock(sock, TRUE); + (void)curlx_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ } @@ -382,7 +382,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ if(!socks5_resolve_local && hostname_len > 255) { - infof(conn->data,"SOCKS5: server resolving disabled for hostnames of " + infof(conn->data, "SOCKS5: server resolving disabled for hostnames of " "length > 255 [actual len=%zu]\n", hostname_len); socks5_resolve_local = TRUE; } @@ -396,7 +396,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, return CURLE_OPERATION_TIMEDOUT; } - curlx_nonblock(sock, TRUE); + (void)curlx_nonblock(sock, TRUE); /* wait until socket gets connected */ result = Curl_socket_ready(CURL_SOCKET_BAD, sock, timeout); @@ -427,16 +427,16 @@ CURLcode Curl_SOCKS5(const char *proxy_name, socksreq[3] = 2; /* username/password */ #endif - curlx_nonblock(sock, FALSE); + (void)curlx_nonblock(sock, FALSE); code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), &written); - if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { + if(code || (written != (2 + (int)socksreq[1]))) { failf(data, "Unable to send initial SOCKS5 request."); return CURLE_COULDNT_CONNECT; } - curlx_nonblock(sock, TRUE); + (void)curlx_nonblock(sock, TRUE); result = Curl_socket_ready(sock, CURL_SOCKET_BAD, timeout); @@ -454,10 +454,10 @@ CURLcode Curl_SOCKS5(const char *proxy_name, return CURLE_RECV_ERROR; } - curlx_nonblock(sock, FALSE); + (void)curlx_nonblock(sock, FALSE); result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread); - if((result != CURLE_OK) || (actualread != 2)) { + if(result || (actualread != 2)) { failf(data, "Unable to receive initial SOCKS5 response."); return CURLE_COULDNT_CONNECT; } @@ -473,7 +473,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else if(socksreq[1] == 1) { code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn); - if(code != CURLE_OK) { + if(code) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLE_COULDNT_CONNECT; } @@ -510,13 +510,13 @@ CURLcode Curl_SOCKS5(const char *proxy_name, len += proxy_password_len; code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); - if((code != CURLE_OK) || (len != written)) { + if(code || (len != written)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); return CURLE_COULDNT_CONNECT; } result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread); - if((result != CURLE_OK) || (actualread != 2)) { + if(result || (actualread != 2)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); return CURLE_COULDNT_CONNECT; } @@ -583,7 +583,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, if(rc == CURLRESOLV_PENDING) { /* this requires that we're in "wait for resolve" state */ code = Curl_resolver_wait_resolv(conn, &dns); - if(code != CURLE_OK) + if(code) return code; } @@ -642,7 +642,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, #endif code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written); - if((code != CURLE_OK) || (len != written)) { + if(code || (len != written)) { failf(data, "Failed to send SOCKS5 connect request."); return CURLE_COULDNT_CONNECT; } @@ -658,7 +658,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, result = Curl_blockread_all(conn, sock, (char *)socksreq, len, &actualread); - if((result != CURLE_OK) || (len != actualread)) { + if(result || (len != actualread)) { failf(data, "Failed to receive SOCKS5 connect request ack."); return CURLE_COULDNT_CONNECT; } @@ -738,7 +738,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, len -= 10; result = Curl_blockread_all(conn, sock, (char *)&socksreq[10], len, &actualread); - if((result != CURLE_OK) || (len != actualread)) { + if(result || (len != actualread)) { failf(data, "Failed to receive SOCKS5 connect request ack."); return CURLE_COULDNT_CONNECT; } @@ -747,7 +747,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, } #endif - curlx_nonblock(sock, TRUE); + (void)curlx_nonblock(sock, TRUE); return CURLE_OK; /* Proxy was successful! */ } diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 0eaa74c..8e575c2 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com> - * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,16 +23,7 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_PROXY - -#ifdef HAVE_GSSAPI -#ifdef HAVE_OLD_GSSMIT -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#define NCOMPAT 1 -#endif -#ifndef gss_nt_service_name -#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE -#endif +#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY) #include "curl_gssapi.h" #include "urldata.h" @@ -41,10 +32,7 @@ #include "timeval.h" #include "socks.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -60,7 +48,7 @@ static int check_gss_err(struct SessionHandle *data, const char* function) { if(GSS_ERROR(major_status)) { - OM_uint32 maj_stat,min_stat; + OM_uint32 maj_stat, min_stat; OM_uint32 msg_ctx = 0; gss_buffer_desc status_string; char buf[1024]; @@ -104,10 +92,10 @@ static int check_gss_err(struct SessionHandle *data, gss_release_buffer(&min_stat, &status_string); } failf(data, "GSS-API error: %s failed:\n%s", function, buf); - return(1); + return 1; } - return(0); + return 0; } CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, @@ -143,7 +131,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, */ /* prepare service name */ - if(strchr(serviceptr,'/')) { + if(strchr(serviceptr, '/')) { service.value = malloc(strlen(serviceptr)); if(!service.value) return CURLE_OUT_OF_MEMORY; @@ -162,13 +150,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, serviceptr, conn->proxy.name); gss_major_status = gss_import_name(&gss_minor_status, &service, - gss_nt_service_name, &server); + GSS_C_NT_HOSTBASED_SERVICE, &server); } gss_release_buffer(&gss_status, &service); /* clear allocated memory */ - if(check_gss_err(data,gss_major_status, - gss_minor_status,"gss_import_name()")) { + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_import_name()")) { failf(data, "Failed to create service name."); gss_release_name(&gss_status, &server); return CURLE_COULDNT_CONNECT; @@ -185,12 +173,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, NULL, gss_token, &gss_send_token, + TRUE, &gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) gss_release_buffer(&gss_status, &gss_recv_token); - if(check_gss_err(data,gss_major_status, - gss_minor_status,"gss_init_sec_context")) { + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_init_sec_context")) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); @@ -203,10 +192,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 1; /* authentication message type */ us_length = htons((short)gss_send_token.length); - memcpy(socksreq+2,&us_length,sizeof(short)); + memcpy(socksreq+2, &us_length, sizeof(short)); code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); - if((code != CURLE_OK) || (4 != written)) { + if(code || (4 != written)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -218,7 +207,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, code = Curl_write_plain(conn, sock, (char *)gss_send_token.value, gss_send_token.length, &written); - if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) { + if(code || ((ssize_t)gss_send_token.length != written)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -244,7 +233,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, */ result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); - if(result != CURLE_OK || actualread != 4) { + if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -285,7 +274,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); - if(result != CURLE_OK || actualread != us_length) { + if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -302,8 +291,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_major_status = gss_inquire_context (&gss_minor_status, gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); - if(check_gss_err(data,gss_major_status, - gss_minor_status,"gss_inquire_context")) { + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_inquire_context")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine user name."); @@ -311,8 +300,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, &gss_send_token, NULL); - if(check_gss_err(data,gss_major_status, - gss_minor_status,"gss_display_name")) { + if(check_gss_err(data, gss_major_status, + gss_minor_status, "gss_display_name")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); @@ -383,7 +372,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, */ if(data->set.socks5_gssapi_nec) { us_length = htons((short)1); - memcpy(socksreq+2,&us_length,sizeof(short)); + memcpy(socksreq+2, &us_length, sizeof(short)); } else { gss_send_token.length = 1; @@ -398,7 +387,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, GSS_C_QOP_DEFAULT, &gss_send_token, &gss_conf_state, &gss_w_token); - if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) { + if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -408,11 +397,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_release_buffer(&gss_status, &gss_send_token); us_length = htons((short)gss_w_token.length); - memcpy(socksreq+2,&us_length,sizeof(short)); + memcpy(socksreq+2, &us_length, sizeof(short)); } code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); - if((code != CURLE_OK) || (4 != written)) { + if(code || (4 != written)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -422,7 +411,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); code = Curl_write_plain(conn, sock, socksreq, 1, &written); - if((code != CURLE_OK) || ( 1 != written)) { + if(code || ( 1 != written)) { failf(data, "Failed to send GSS-API encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; @@ -431,7 +420,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, else { code = Curl_write_plain(conn, sock, (char *)gss_w_token.value, gss_w_token.length, &written); - if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) { + if(code || ((ssize_t)gss_w_token.length != written)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -441,7 +430,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); - if(result != CURLE_OK || actualread != 4) { + if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; @@ -474,7 +463,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); - if(result != CURLE_OK || actualread != us_length) { + if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API encryptrion type."); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -486,7 +475,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, &gss_recv_token, &gss_w_token, 0, GSS_C_QOP_DEFAULT); - if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) { + if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -503,7 +492,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - memcpy(socksreq,gss_w_token.value,gss_w_token.length); + memcpy(socksreq, gss_w_token.value, gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); } else { @@ -515,7 +504,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - memcpy(socksreq,gss_recv_token.value,gss_recv_token.length); + memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); } @@ -529,6 +518,5 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_OK; } -#endif -#endif /* CURL_DISABLE_PROXY */ +#endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */ diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index 82684e0..a7708b2 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com> - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,10 +34,7 @@ #include "curl_sspi.h" #include "curl_multibyte.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use the internal *printf() functions */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -107,8 +104,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, service_name = malloc(strlen(service) + strlen(conn->proxy.name) + 2); if(!service_name) return CURLE_OUT_OF_MEMORY; - snprintf(service_name,strlen(service) +strlen(conn->proxy.name)+2,"%s/%s", - service,conn->proxy.name); + snprintf(service_name, strlen(service) +strlen(conn->proxy.name)+2, + "%s/%s", service, conn->proxy.name); } input_desc.cBuffers = 1; @@ -146,7 +143,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) { failf(data, "Failed to acquire credentials."); - Curl_safefree(service_name); + free(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); return CURLE_COULDNT_CONNECT; } @@ -185,7 +182,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } if(check_sspi_err(conn, status, "InitializeSecurityContext")) { - Curl_safefree(service_name); + free(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); if(sspi_recv_token.pvBuffer) @@ -201,9 +198,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, memcpy(socksreq+2, &us_length, sizeof(short)); code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); - if((code != CURLE_OK) || (4 != written)) { + if(code || (4 != written)) { failf(data, "Failed to send SSPI authentication request."); - Curl_safefree(service_name); + free(service_name); if(sspi_send_token.pvBuffer) s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(sspi_recv_token.pvBuffer) @@ -215,9 +212,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer, sspi_send_token.cbBuffer, &written); - if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) { + if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI authentication token."); - Curl_safefree(service_name); + free(service_name); if(sspi_send_token.pvBuffer) s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(sspi_recv_token.pvBuffer) @@ -255,9 +252,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, */ result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); - if(result != CURLE_OK || actualread != 4) { + if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); - Curl_safefree(service_name); + free(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; @@ -267,7 +264,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - Curl_safefree(service_name); + free(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; @@ -276,7 +273,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(socksreq[1] != 1) { /* status / messgae type */ failf(data, "Invalid SSPI authentication response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - Curl_safefree(service_name); + free(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; @@ -289,7 +286,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, sspi_recv_token.pvBuffer = malloc(us_length); if(!sspi_recv_token.pvBuffer) { - Curl_safefree(service_name); + free(service_name); s_pSecFn->FreeCredentialsHandle(&cred_handle); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; @@ -297,9 +294,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer, sspi_recv_token.cbBuffer, &actualread); - if(result != CURLE_OK || actualread != us_length) { + if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI authentication token."); - Curl_safefree(service_name); + free(service_name); if(sspi_recv_token.pvBuffer) s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); s_pSecFn->FreeCredentialsHandle(&cred_handle); @@ -310,7 +307,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, context_handle = &sspi_context; } - Curl_safefree(service_name); + free(service_name); /* Everything is good so far, user was authenticated! */ status = s_pSecFn->QueryCredentialsAttributes(&cred_handle, @@ -405,7 +402,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_OUT_OF_MEMORY; } - memcpy(sspi_w_token[1].pvBuffer,&gss_enc,1); + memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); sspi_w_token[2].BufferType = SECBUFFER_PADDING; sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize); @@ -459,11 +456,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, sspi_w_token[2].cbBuffer = 0; us_length = htons((short)sspi_send_token.cbBuffer); - memcpy(socksreq+2,&us_length,sizeof(short)); + memcpy(socksreq+2, &us_length, sizeof(short)); } code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); - if((code != CURLE_OK) || (4 != written)) { + if(code || (4 != written)) { failf(data, "Failed to send SSPI encryption request."); if(sspi_send_token.pvBuffer) s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); @@ -472,9 +469,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } if(data->set.socks5_gssapi_nec) { - memcpy(socksreq,&gss_enc,1); + memcpy(socksreq, &gss_enc, 1); code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written); - if((code != CURLE_OK) || (1 != written)) { + if(code || (1 != written)) { failf(data, "Failed to send SSPI encryption type."); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; @@ -483,7 +480,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, else { code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer, sspi_send_token.cbBuffer, &written); - if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) { + if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI encryption type."); if(sspi_send_token.pvBuffer) s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); @@ -495,7 +492,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); - if(result != CURLE_OK || actualread != 4) { + if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; @@ -529,7 +526,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer, &actualread); - if(result != CURLE_OK || actualread != us_length) { + if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI encryption type."); s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -570,7 +567,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer); + memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); } @@ -582,7 +579,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } - memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer); + memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); } diff --git a/lib/splay.c b/lib/splay.c index 5bb7065..b87b6cf 100644 --- a/lib/splay.c +++ b/lib/splay.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1997 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1997 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -101,13 +101,13 @@ struct Curl_tree *Curl_splayinsert(struct timeval i, struct Curl_tree *t, struct Curl_tree *node) { - static const struct timeval KEY_NOTUSED = {-1,-1}; /* will *NEVER* appear */ + static const struct timeval KEY_NOTUSED = {-1, -1}; /* will *NEVER* appear */ if(node == NULL) return t; if(t != NULL) { - t = Curl_splay(i,t); + t = Curl_splay(i, t); if(compare(i, t->key)==0) { /* There already exists a node in the tree with the very same key. Build a linked list of nodes. We make the new 'node' struct the new master @@ -162,7 +162,7 @@ struct Curl_tree *Curl_splaygetbest(struct timeval i, return NULL; } - t = Curl_splay(i,t); + t = Curl_splay(i, t); if(compare(i, t->key) < 0) { /* too big node, try the smaller chain */ if(t->smaller) @@ -223,7 +223,7 @@ int Curl_splayremovebyaddr(struct Curl_tree *t, struct Curl_tree *removenode, struct Curl_tree **newroot) { - static const struct timeval KEY_NOTUSED = {-1,-1}; /* will *NEVER* appear */ + static const struct timeval KEY_NOTUSED = {-1, -1}; /* will *NEVER* appear */ struct Curl_tree *x; if(!t || !removenode) @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -83,10 +83,7 @@ #include "multiif.h" #include "select.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -94,6 +91,9 @@ #ifdef WIN32 # undef PATH_MAX # define PATH_MAX MAX_PATH +# ifndef R_OK +# define R_OK 4 +# endif #endif #ifndef PATH_MAX @@ -543,6 +543,17 @@ static CURLcode ssh_knownhost(struct connectdata *conn) keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS; +#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->host.name, + (conn->remote_port != PORT_SSH)? + conn->remote_port:-1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); +#else keycheck = libssh2_knownhost_check(sshc->kh, conn->host.name, remotekey, keylen, @@ -550,6 +561,7 @@ static CURLcode ssh_knownhost(struct connectdata *conn) LIBSSH2_KNOWNHOST_KEYENC_RAW| keybit, &host); +#endif infof(data, "SSH host check: %d, key: %s\n", keycheck, (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? @@ -588,8 +600,10 @@ static CURLcode ssh_knownhost(struct connectdata *conn) switch(rc) { default: /* unknown return codes will equal reject */ + /* FALLTHROUGH */ case CURLKHSTAT_REJECT: state(conn, SSH_SESSION_FREE); + /* FALLTHROUGH */ case CURLKHSTAT_DEFER: /* DEFER means bail out but keep the SSH_HOSTKEY state */ result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; @@ -732,7 +746,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) * whatever) is up to us. */ result = ssh_check_fingerprint(conn); - if(result == CURLE_OK) + if(!result) state(conn, SSH_AUTHLIST); /* ssh_check_fingerprint sets state appropriately on error */ break; @@ -786,7 +800,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && (strstr(sshc->authlist, "publickey") != NULL)) { char *home = NULL; - bool rsa_pub_empty_but_ok = FALSE; + bool out_of_memory = FALSE; sshc->rsa_pub = sshc->rsa = NULL; @@ -794,34 +808,55 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) HOME environment variable etc? */ home = curl_getenv("HOME"); - if(data->set.str[STRING_SSH_PUBLIC_KEY] && - !*data->set.str[STRING_SSH_PUBLIC_KEY]) - rsa_pub_empty_but_ok = true; - else if(data->set.str[STRING_SSH_PUBLIC_KEY]) - sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]); - else if(home) - sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home); - else - /* as a final resort, try current dir! */ - sshc->rsa_pub = strdup("id_dsa.pub"); - - if(!rsa_pub_empty_but_ok && (sshc->rsa_pub == NULL)) { - Curl_safefree(home); - state(conn, SSH_SESSION_FREE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; + if(data->set.str[STRING_SSH_PRIVATE_KEY]) + sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + else { + /* If no private key file is specified, try some common paths. */ + if(home) { + /* Try ~/.ssh first. */ + sshc->rsa = aprintf("%s/.ssh/id_rsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + sshc->rsa = aprintf("%s/.ssh/id_dsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + } + } + } + if(!out_of_memory && !sshc->rsa) { + /* Nothing found; try the current dir. */ + sshc->rsa = strdup("id_rsa"); + if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + sshc->rsa = strdup("id_dsa"); + if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + /* Out of guesses. Set to the empty string to avoid + * surprising info messages. */ + sshc->rsa = strdup(""); + } + } + } } - if(data->set.str[STRING_SSH_PRIVATE_KEY]) - sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]); - else if(home) - sshc->rsa = aprintf("%s/.ssh/id_dsa", home); - else - /* as a final resort, try current dir! */ - sshc->rsa = strdup("id_dsa"); + /* + * Unless the user explicitly specifies a public key file, let + * libssh2 extract the public key from the private key file. + * This is done by simply passing sshc->rsa_pub = NULL. + */ + if(data->set.str[STRING_SSH_PUBLIC_KEY]) { + sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + if(!sshc->rsa_pub) + out_of_memory = TRUE; + } - if(sshc->rsa == NULL) { - Curl_safefree(home); + if(out_of_memory || sshc->rsa == NULL) { + free(home); + Curl_safefree(sshc->rsa); Curl_safefree(sshc->rsa_pub); state(conn, SSH_SESSION_FREE); sshc->actualcode = CURLE_OUT_OF_MEMORY; @@ -832,10 +867,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(!sshc->passphrase) sshc->passphrase = ""; - Curl_safefree(home); + free(home); - infof(data, "Using ssh public key file %s\n", sshc->rsa_pub); - infof(data, "Using ssh private key file %s\n", sshc->rsa); + infof(data, "Using SSH public key file '%s'\n", sshc->rsa_pub); + infof(data, "Using SSH private key file '%s'\n", sshc->rsa); state(conn, SSH_AUTH_PKEY); } @@ -900,6 +935,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } else { state(conn, SSH_AUTH_HOST_INIT); + rc = 0; /* clear rc and continue */ } break; @@ -984,11 +1020,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->sshagent_identity); if(rc < 0) { - if(rc != LIBSSH2_ERROR_EAGAIN) { + if(rc != LIBSSH2_ERROR_EAGAIN) /* tried and failed? go to next identity */ sshc->sshagent_prev_identity = sshc->sshagent_identity; - } - break; + else + break; } } @@ -1002,8 +1038,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) infof(data, "Agent based authentication successful\n"); state(conn, SSH_AUTH_DONE); } - else + else { state(conn, SSH_AUTH_KEY_INIT); + rc = 0; /* clear rc and continue */ + } #endif break; @@ -1702,8 +1740,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) BUFSIZE : curlx_sotouz(data->state.resume_from - passed); size_t actuallyread = - conn->fread_func(data->state.buffer, 1, readthisamountnow, - conn->fread_in); + data->set.fread_func(data->state.buffer, 1, readthisamountnow, + data->set.in); passed += actuallyread; if((actuallyread == 0) || (actuallyread > readthisamountnow)) { @@ -1770,7 +1808,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; case SSH_SFTP_CREATE_DIRS: - if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) { + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { *sshc->slash_pos = 0; infof(data, "Creating directory '%s'\n", sftp_scp->path); @@ -1882,7 +1921,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } result = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, sshc->readdir_len+1); - Curl_safefree(tmpLine); + free(tmpLine); if(result) { state(conn, SSH_STOP); @@ -1998,7 +2037,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->readdir_line, sshc->readdir_currLen); - if(result == CURLE_OK) { + if(!result) { /* output debug output if that is requested */ if(data->set.verbose) { @@ -2068,10 +2107,14 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc) { + else if(rc || + !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || + (attrs.filesize == 0)) { /* * libssh2_sftp_open() didn't return an error, so maybe the server * just doesn't support stat() + * OR the server doesn't return a file size with a stat() + * OR file size is 0 */ data->req.size = -1; data->req.maxdownload = -1; @@ -2101,7 +2144,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* from is relative to end of file */ from += size; } - if(from >= size) { + if(from > size) { failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize); @@ -2202,7 +2245,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) DEBUGF(infof(data, "SFTP DONE done\n")); /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT - After nextstate is executed,the control should come back to + After nextstate is executed, the control should come back to SSH_SFTP_CLOSE to pass the correct result back */ if(sshc->nextstate != SSH_NO_STATE && sshc->nextstate != SSH_SFTP_CLOSE) { @@ -2691,7 +2734,7 @@ static CURLcode ssh_block_statemach(struct connectdata *conn, } #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION - if((CURLE_OK == result) && block) { + if(!result && block) { int dir = libssh2_session_block_directions(sshc->ssh_session); curl_socket_t sock = conn->sock[FIRSTSOCKET]; curl_socket_t fd_read = CURL_SOCKET_BAD; @@ -2863,7 +2906,7 @@ static CURLcode scp_doing(struct connectdata *conn, static CURLcode ssh_do(struct connectdata *conn, bool *done) { - CURLcode res; + CURLcode result; bool connected = 0; struct SessionHandle *data = conn->data; struct ssh_conn *sshc = &conn->proto.sshc; @@ -2882,11 +2925,11 @@ static CURLcode ssh_do(struct connectdata *conn, bool *done) Curl_pgrsSetDownloadSize(data, -1); if(conn->handler->protocol & CURLPROTO_SCP) - res = scp_perform(conn, &connected, done); + result = scp_perform(conn, &connected, done); else - res = sftp_perform(conn, &connected, done); + result = sftp_perform(conn, &connected, done); - return res; + return result; } /* BLOCKING, but the function is using the state machine so the only reason @@ -2918,7 +2961,7 @@ static CURLcode ssh_done(struct connectdata *conn, CURLcode status) CURLcode result = CURLE_OK; struct SSHPROTO *sftp_scp = conn->data->req.protop; - if(status == CURLE_OK) { + if(!status) { /* run the state-machine TODO: when the multi interface is used, this _really_ should be using @@ -2946,7 +2989,7 @@ static CURLcode scp_done(struct connectdata *conn, CURLcode status, { (void)premature; /* not used */ - if(status == CURLE_OK) + if(!status) state(conn, SSH_SCP_DONE); return ssh_done(conn, status); @@ -3044,8 +3087,7 @@ CURLcode sftp_perform(struct connectdata *conn, static CURLcode sftp_doing(struct connectdata *conn, bool *dophase_done) { - CURLcode result; - result = ssh_multi_statemach(conn, dophase_done); + CURLcode result = ssh_multi_statemach(conn, dophase_done); if(*dophase_done) { DEBUGF(infof(conn->data, "DO phase is complete\n")); @@ -3082,7 +3124,7 @@ static CURLcode sftp_done(struct connectdata *conn, CURLcode status, { struct ssh_conn *sshc = &conn->proto.sshc; - if(status == CURLE_OK) { + if(!status) { /* Post quote commands are executed after the SFTP_CLOSE state to avoid errors that could happen due to open file handles during POSTQUOTE operation */ @@ -3228,8 +3270,8 @@ get_pathname(const char **cpp, char **path) return CURLE_OK; fail: - Curl_safefree(*path); - return CURLE_QUOTE_ERROR; + Curl_safefree(*path); + return CURLE_QUOTE_ERROR; } @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -44,9 +44,9 @@ typedef enum { SSH_AUTH_PKEY, SSH_AUTH_PASS_INIT, SSH_AUTH_PASS, - SSH_AUTH_AGENT_INIT,/* initialize then wait for connection to agent */ - SSH_AUTH_AGENT_LIST,/* ask for list then wait for entire list to come */ - SSH_AUTH_AGENT, /* attempt one key at a time */ + SSH_AUTH_AGENT_INIT, /* initialize then wait for connection to agent */ + SSH_AUTH_AGENT_LIST, /* ask for list then wait for entire list to come */ + SSH_AUTH_AGENT, /* attempt one key at a time */ SSH_AUTH_HOST_INIT, SSH_AUTH_HOST, SSH_AUTH_KEY_INIT, @@ -158,22 +158,34 @@ struct ssh_conn { #ifdef USE_LIBSSH2 +/* Feature detection based on version numbers to better work with + non-configure platforms */ + #if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000) # error "SCP/SFTP protocols require libssh2 0.16 or later" #endif -#if defined(LIBSSH2_VERSION_NUM) && (LIBSSH2_VERSION_NUM >= 0x010000) -# define HAVE_LIBSSH2_SFTP_SEEK64 1 -#else -# undef HAVE_LIBSSH2_SFTP_SEEK64 +#if LIBSSH2_VERSION_NUM >= 0x010000 +#define HAVE_LIBSSH2_SFTP_SEEK64 1 +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010100 +#define HAVE_LIBSSH2_VERSION 1 #endif -#if defined(LIBSSH2_VERSION_NUM) && (LIBSSH2_VERSION_NUM >= 0x010206) -# define HAVE_LIBSSH2_SCP_SEND64 1 -#else -# undef HAVE_LIBSSH2_SCP_SEND64 +#if LIBSSH2_VERSION_NUM >= 0x010205 +#define HAVE_LIBSSH2_INIT 1 +#define HAVE_LIBSSH2_EXIT 1 #endif +#if LIBSSH2_VERSION_NUM >= 0x010206 +#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1 +#define HAVE_LIBSSH2_SCP_SEND64 1 +#endif + +#if LIBSSH2_VERSION_NUM >= 0x010208 +#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1 +#endif extern const struct Curl_handler Curl_handler_scp; extern const struct Curl_handler Curl_handler_sftp; diff --git a/lib/strdup.c b/lib/strdup.c index 3b776b1..5685b81 100644 --- a/lib/strdup.c +++ b/lib/strdup.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -19,12 +19,12 @@ * KIND, either express or implied. * ***************************************************************************/ -/* - * This file is 'mem-include-scan' clean. See test 1132. - */ #include "curl_setup.h" - #include "strdup.h" +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" #ifndef HAVE_STRDUP char *curlx_strdup(const char *str) @@ -44,9 +44,30 @@ char *curlx_strdup(const char *str) if(!newstr) return (char *)NULL; - memcpy(newstr,str,(len+1)*sizeof(char)); + memcpy(newstr, str, (len+1)*sizeof(char)); return newstr; } #endif + +/*************************************************************************** + * + * Curl_memdup(source, length) + * + * Copies the 'source' data to a newly allocated buffer (that is + * returned). Copies 'length' bytes. + * + * Returns the new pointer or NULL on failure. + * + ***************************************************************************/ +char *Curl_memdup(const char *src, size_t length) +{ + char *buffer = malloc(length); + if(!buffer) + return NULL; /* fail */ + + memcpy(buffer, src, length); + + return buffer; +} diff --git a/lib/strdup.h b/lib/strdup.h index 49af911..23a71f8 100644 --- a/lib/strdup.h +++ b/lib/strdup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,5 +26,6 @@ #ifndef HAVE_STRDUP extern char *curlx_strdup(const char *str); #endif +char *Curl_memdup(const char *src, size_t buffer_length); #endif /* HEADER_CURL_STRDUP_H */ diff --git a/lib/strerror.c b/lib/strerror.c index 66033f2..5657141 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2004 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2004 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,10 +40,7 @@ #endif #include "strerror.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -298,6 +295,12 @@ curl_easy_strerror(CURLcode error) case CURLE_NO_CONNECTION_AVAILABLE: return "The max connection limit is reached"; + case CURLE_SSL_PINNEDPUBKEYNOTMATCH: + return "SSL public key does not match pinned public key"; + + case CURLE_SSL_INVALIDCERTSTATUS: + return "SSL server certificate status verification FAILED"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE20: case CURLE_OBSOLETE24: @@ -327,7 +330,7 @@ curl_easy_strerror(CURLcode error) */ return "Unknown error"; #else - if(error == CURLE_OK) + if(!error) return "No error"; else return "Error"; @@ -594,7 +597,7 @@ get_winsock_error (int err, char *buf, size_t len) return NULL; } #else - if(err == CURLE_OK) + if(!err) return NULL; else p = "error"; @@ -638,7 +641,7 @@ const char *Curl_strerror(struct connectdata *conn, int err) FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL); - wcstombs(buf,wbuf,max); + wcstombs(buf, wbuf, max); } #else /* 'sys_nerr' is the maximum errno number, it is not widely portable */ @@ -705,9 +708,9 @@ const char *Curl_strerror(struct connectdata *conn, int err) buf[max] = '\0'; /* make sure the string is zero terminated */ /* strip trailing '\r\n' or '\n'. */ - if((p = strrchr(buf,'\n')) != NULL && (p - buf) >= 2) + if((p = strrchr(buf, '\n')) != NULL && (p - buf) >= 2) *p = '\0'; - if((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1) + if((p = strrchr(buf, '\r')) != NULL && (p - buf) >= 1) *p = '\0'; if(old_errno != ERRNO) @@ -821,6 +824,9 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err) case SEC_E_OK: txt = "No error"; break; + case CRYPT_E_REVOKED: + txt = "CRYPT_E_REVOKED"; + break; case SEC_E_ALGORITHM_MISMATCH: txt = "SEC_E_ALGORITHM_MISMATCH"; break; @@ -1064,6 +1070,12 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err) if(err == SEC_E_OK) strncpy(outbuf, txt, outmax); + else if(err == SEC_E_ILLEGAL_MESSAGE) + snprintf(outbuf, outmax, + "SEC_E_ILLEGAL_MESSAGE (0x%04X%04X) - This error usually occurs " + "when a fatal SSL/TLS alert is received (e.g. handshake failed). " + "More detail may be available in the Windows System event log.", + (err >> 16) & 0xffff, err & 0xffff); else { str = txtbuf; snprintf(txtbuf, sizeof(txtbuf), "%s (0x%04X%04X)", @@ -1079,7 +1091,7 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err) FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) { - wcstombs(msgbuf,wbuf,sizeof(msgbuf)-1); + wcstombs(msgbuf, wbuf, sizeof(msgbuf)-1); msg_formatted = TRUE; } } @@ -1094,9 +1106,9 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err) if(msg_formatted) { msgbuf[sizeof(msgbuf)-1] = '\0'; /* strip trailing '\r\n' or '\n' */ - if((p = strrchr(msgbuf,'\n')) != NULL && (p - msgbuf) >= 2) + if((p = strrchr(msgbuf, '\n')) != NULL && (p - msgbuf) >= 2) *p = '\0'; - if((p = strrchr(msgbuf,'\r')) != NULL && (p - msgbuf) >= 1) + if((p = strrchr(msgbuf, '\r')) != NULL && (p - msgbuf) >= 1) *p = '\0'; msg = msgbuf; } diff --git a/lib/strtoofft.h b/lib/strtoofft.h index b812a67..75c73d4 100644 --- a/lib/strtoofft.h +++ b/lib/strtoofft.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -45,7 +45,14 @@ # define curlx_strtoofft strtoll # else # if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64) - _CRTIMP __int64 __cdecl _strtoi64(const char *, char **, int); +# if defined(_SAL_VERSION) + _Check_return_ _CRTIMP __int64 __cdecl _strtoi64( + _In_z_ const char *_String, + _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix); +# else + _CRTIMP __int64 __cdecl _strtoi64(const char *_String, + char **_EndPtr, int _Radix); +# endif # define curlx_strtoofft _strtoi64 # else curl_off_t curlx_strtoll(const char *nptr, char **endptr, int base); diff --git a/lib/telnet.c b/lib/telnet.c index 1f03a00..aabf99d 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -51,21 +51,19 @@ #include "telnet.h" #include "connect.h" #include "progress.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #define TELOPTS #define TELCMDS #include "arpa_telnet.h" -#include "curl_memory.h" #include "select.h" #include "strequal.h" #include "rawstr.h" #include "warnless.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #define SUBBUFSIZE 512 @@ -228,9 +226,9 @@ check_wsock2 ( struct SessionHandle *data ) if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) { /* Our version isn't supported */ - failf(data,"insufficient winsock version to support " - "telnet"); - return CURLE_FAILED_INIT; + failf(data, "insufficient winsock version to support " + "telnet"); + return CURLE_FAILED_INIT; } /* Our version is supported */ @@ -710,7 +708,6 @@ static void printsub(struct SessionHandle *data, size_t length) /* length of suboption data */ { unsigned int i = 0; - unsigned short *pval; if(data->set.verbose) { if(direction) { @@ -763,9 +760,9 @@ static void printsub(struct SessionHandle *data, switch(pointer[0]) { case CURL_TELOPT_NAWS: - pval = (unsigned short*)(pointer+1); - infof(data, "Width: %hu ; Height: %hu", - ntohs(pval[0]), ntohs(pval[1])); + if(length > 4) + infof(data, "Width: %hu ; Height: %hu", (pointer[1]<<8) | pointer[2], + (pointer[3]<<8) | pointer[4]); break; default: switch(pointer[1]) { @@ -1076,7 +1073,7 @@ CURLcode telrcv(struct connectdata *conn, CLIENTWRITE_BODY, \ (char *)&inbuf[startwrite], \ in-startwrite); \ - if(result != CURLE_OK) \ + if(result) \ return result; \ } \ startwrite = -1 @@ -1177,7 +1174,7 @@ CURLcode telrcv(struct connectdata *conn, if(c == CURL_IAC) tn->telrcv_state = CURL_TS_SE; else - CURL_SB_ACCUM(tn,c); + CURL_SB_ACCUM(tn, c); break; case CURL_TS_SE: @@ -1202,7 +1199,7 @@ CURLcode telrcv(struct connectdata *conn, tn->telrcv_state = CURL_TS_IAC; goto process_iac; } - CURL_SB_ACCUM(tn,c); + CURL_SB_ACCUM(tn, c); tn->telrcv_state = CURL_TS_SB; } else @@ -1230,9 +1227,9 @@ static CURLcode send_telnet_data(struct connectdata *conn, unsigned char outbuf[2]; ssize_t bytes_written, total_written; int out_count; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; - while(rc == CURLE_OK && nread--) { + while(!result && nread--) { outbuf[0] = *buffer++; out_count = 1; if(outbuf[0] == CURL_IAC) @@ -1247,19 +1244,20 @@ static CURLcode send_telnet_data(struct connectdata *conn, switch (Curl_poll(pfd, 1, -1)) { case -1: /* error, abort writing */ case 0: /* timeout (will never happen) */ - rc = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; break; default: /* write! */ bytes_written = 0; - rc = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf+total_written, - out_count-total_written, &bytes_written); + result = Curl_write(conn, conn->sock[FIRSTSOCKET], + outbuf+total_written, out_count-total_written, + &bytes_written); total_written += bytes_written; break; } - /* handle partial write */ - } while(rc == CURLE_OK && total_written < out_count); + /* handle partial write */ + } while(!result && total_written < out_count); } - return rc; + return result; } static CURLcode telnet_done(struct connectdata *conn, @@ -1282,7 +1280,7 @@ static CURLcode telnet_done(struct connectdata *conn, static CURLcode telnet_do(struct connectdata *conn, bool *done) { - CURLcode code; + CURLcode result; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; #ifdef USE_WINSOCK @@ -1315,65 +1313,61 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) *done = TRUE; /* unconditionally */ - code = init_telnet(conn); - if(code) - return code; + result = init_telnet(conn); + if(result) + return result; tn = (struct TELNET *)data->req.protop; - code = check_telnet_options(conn); - if(code) - return code; + result = check_telnet_options(conn); + if(result) + return result; #ifdef USE_WINSOCK /* ** This functionality only works with WinSock >= 2.0. So, ** make sure have it. */ - code = check_wsock2(data); - if(code) - return code; + result = check_wsock2(data); + if(result) + return result; /* OK, so we have WinSock 2.0. We need to dynamically */ /* load ws2_32.dll and get the function pointers we need. */ wsock2 = LoadLibrary(TEXT("WS2_32.DLL")); if(wsock2 == NULL) { - failf(data,"failed to load WS2_32.DLL (%d)", ERRNO); + failf(data, "failed to load WS2_32.DLL (%d)", ERRNO); return CURLE_FAILED_INIT; } /* Grab a pointer to WSACreateEvent */ - create_event_func = GetProcAddress(wsock2,"WSACreateEvent"); + create_event_func = GetProcAddress(wsock2, "WSACreateEvent"); if(create_event_func == NULL) { - failf(data,"failed to find WSACreateEvent function (%d)", - ERRNO); + failf(data, "failed to find WSACreateEvent function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSACloseEvent */ - close_event_func = GetProcAddress(wsock2,"WSACloseEvent"); + close_event_func = GetProcAddress(wsock2, "WSACloseEvent"); if(close_event_func == NULL) { - failf(data,"failed to find WSACloseEvent function (%d)", - ERRNO); + failf(data, "failed to find WSACloseEvent function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEventSelect */ - event_select_func = GetProcAddress(wsock2,"WSAEventSelect"); + event_select_func = GetProcAddress(wsock2, "WSAEventSelect"); if(event_select_func == NULL) { - failf(data,"failed to find WSAEventSelect function (%d)", - ERRNO); + failf(data, "failed to find WSAEventSelect function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEnumNetworkEvents */ - enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents"); + enum_netevents_func = GetProcAddress(wsock2, "WSAEnumNetworkEvents"); if(enum_netevents_func == NULL) { - failf(data,"failed to find WSAEnumNetworkEvents function (%d)", - ERRNO); + failf(data, "failed to find WSAEnumNetworkEvents function (%d)", ERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } @@ -1386,7 +1380,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* First, create a sockets event object */ event_handle = (WSAEVENT)create_event_func(); if(event_handle == WSA_INVALID_EVENT) { - failf(data,"WSACreateEvent failed (%d)", SOCKERRNO); + failf(data, "WSACreateEvent failed (%d)", SOCKERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } @@ -1427,29 +1421,30 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) case WAIT_TIMEOUT: { for(;;) { - if(obj_count == 1) { + if(data->set.is_fread_set) { /* read from user-supplied method */ - code = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in); - if(code == CURL_READFUNC_ABORT) { + result = (int)data->set.fread_func(buf, 1, BUFSIZE - 1, + data->set.in); + if(result == CURL_READFUNC_ABORT) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } - if(code == CURL_READFUNC_PAUSE) + if(result == CURL_READFUNC_PAUSE) break; - if(code == 0) /* no bytes */ + if(result == 0) /* no bytes */ break; - readfile_read = code; /* fall thru with number of bytes read */ + readfile_read = result; /* fall thru with number of bytes read */ } else { /* read from stdin */ if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } @@ -1459,13 +1454,13 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), &readfile_read, NULL)) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } } - code = send_telnet_data(conn, buf, readfile_read); - if(code) { + result = send_telnet_data(conn, buf, readfile_read); + if(result) { keepon = FALSE; break; } @@ -1478,12 +1473,12 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), &readfile_read, NULL)) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } - code = send_telnet_data(conn, buf, readfile_read); - if(code) { + result = send_telnet_data(conn, buf, readfile_read); + if(result) { keepon = FALSE; break; } @@ -1495,20 +1490,20 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) events.lNetworkEvents = 0; if(SOCKET_ERROR == enum_netevents_func(sockfd, event_handle, &events)) { if((err = SOCKERRNO) != EINPROGRESS) { - infof(data,"WSAEnumNetworkEvents failed (%d)", err); + infof(data, "WSAEnumNetworkEvents failed (%d)", err); keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; } break; } if(events.lNetworkEvents & FD_READ) { /* read data from network */ - code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + result = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); /* read would've blocked. Loop again */ - if(code == CURLE_AGAIN) + if(result == CURLE_AGAIN) break; /* returned not-zero, this an error */ - else if(code) { + else if(result) { keepon = FALSE; break; } @@ -1519,8 +1514,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) break; } - code = telrcv(conn, (unsigned char *)buf, nread); - if(code) { + result = telrcv(conn, (unsigned char *) buf, nread); + if(result) { keepon = FALSE; break; } @@ -1544,7 +1539,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) now = Curl_tvnow(); if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); - code = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; } } @@ -1552,7 +1547,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* We called WSACreateEvent, so call WSACloseEvent */ if(!close_event_func(event_handle)) { - infof(data,"WSACloseEvent failed (%d)", SOCKERRNO); + infof(data, "WSACloseEvent failed (%d)", SOCKERRNO); } /* "Forget" pointers into the library we're about to free */ @@ -1563,18 +1558,18 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* We called LoadLibrary, so call FreeLibrary */ if(!FreeLibrary(wsock2)) - infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO); + infof(data, "FreeLibrary(wsock2) failed (%d)", ERRNO); #else pfd[0].fd = sockfd; pfd[0].events = POLLIN; - if(conn->fread_func != (curl_read_callback)fread) { + if(data->set.fread_func != (curl_read_callback)fread) { poll_cnt = 1; interval_ms = 100; /* poll user-supplied read function */ } else { /* really using fread, so infile is a FILE* */ - pfd[1].fd = fileno((FILE *)conn->fread_in); + pfd[1].fd = fileno((FILE *)data->set.in); pfd[1].events = POLLIN; poll_cnt = 2; interval_ms = 1 * 1000; @@ -1592,12 +1587,12 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) default: /* read! */ if(pfd[0].revents & POLLIN) { /* read data from network */ - code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + result = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); /* read would've blocked. Loop again */ - if(code == CURLE_AGAIN) + if(result == CURLE_AGAIN) break; /* returned not-zero, this an error */ - else if(code) { + else if(result) { keepon = FALSE; break; } @@ -1610,8 +1605,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) total_dl += nread; Curl_pgrsSetDownloadCounter(data, total_dl); - code = telrcv(conn, (unsigned char *)buf, nread); - if(code) { + result = telrcv(conn, (unsigned char *)buf, nread); + if(result) { keepon = FALSE; break; } @@ -1633,7 +1628,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } else { /* read from user-supplied method */ - nread = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in); + nread = (int)data->set.fread_func(buf, 1, BUFSIZE - 1, data->set.in); if(nread == CURL_READFUNC_ABORT) { keepon = FALSE; break; @@ -1643,8 +1638,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } if(nread > 0) { - code = send_telnet_data(conn, buf, nread); - if(code) { + result = send_telnet_data(conn, buf, nread); + if(result) { keepon = FALSE; break; } @@ -1661,13 +1656,13 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) now = Curl_tvnow(); if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); - code = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; } } if(Curl_pgrsUpdate(conn)) { - code = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; break; } } @@ -1675,6 +1670,6 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* mark this as "no further transfer wanted" */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - return code; + return result; } #endif @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -57,14 +57,11 @@ #include "url.h" #include "rawstr.h" #include "speedcheck.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -#include "curl_memory.h" +#include "curl_printf.h" #include "select.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* RFC2348 allows the block size to be negotiated */ @@ -148,8 +145,8 @@ typedef struct tftp_state_data { /* Forward declarations */ -static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ; -static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ; +static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event); +static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event); static CURLcode tftp_connect(struct connectdata *conn, bool *done); static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection); @@ -221,7 +218,7 @@ static CURLcode tftp_set_timeouts(tftp_state_data_t *state) state->max_time = state->start_time+maxtime; /* Set per-block timeout to total */ - timeout = maxtime ; + timeout = maxtime; /* Average restart after 5 seconds */ state->retry_max = (int)timeout/5; @@ -411,38 +408,38 @@ static size_t tftp_option_add(tftp_state_data_t *state, size_t csize, if(( strlen(option) + csize + 1 ) > (size_t)state->blksize) return 0; strcpy(buf, option); - return( strlen(option) + 1 ); + return strlen(option) + 1; } static CURLcode tftp_connect_for_tx(tftp_state_data_t *state, tftp_event_t event) { - CURLcode res; + CURLcode result; #ifndef CURL_DISABLE_VERBOSE_STRINGS struct SessionHandle *data = state->conn->data; infof(data, "%s\n", "Connected for transmit"); #endif state->state = TFTP_STATE_TX; - res = tftp_set_timeouts(state); - if(res != CURLE_OK) - return(res); + result = tftp_set_timeouts(state); + if(result) + return result; return tftp_tx(state, event); } static CURLcode tftp_connect_for_rx(tftp_state_data_t *state, tftp_event_t event) { - CURLcode res; + CURLcode result; #ifndef CURL_DISABLE_VERBOSE_STRINGS struct SessionHandle *data = state->conn->data; infof(data, "%s\n", "Connected for receive"); #endif state->state = TFTP_STATE_RX; - res = tftp_set_timeouts(state); - if(res != CURLE_OK) - return(res); + result = tftp_set_timeouts(state); + if(result) + return result; return tftp_rx(state, event); } @@ -454,7 +451,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) char *filename; char buf[64]; struct SessionHandle *data = state->conn->data; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; /* Set ascii mode if -B flag was used */ if(data->set.prefer_ascii) @@ -469,7 +466,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) if(state->retries>state->retry_max) { state->error = TFTP_ERR_NORESPONSE; state->state = TFTP_STATE_FIN; - return res; + return result; } if(data->set.upload) { @@ -534,24 +531,24 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) if(senddata != (ssize_t)sbytes) { failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO)); } - Curl_safefree(filename); + free(filename); break; case TFTP_EVENT_OACK: if(data->set.upload) { - res = tftp_connect_for_tx(state, event); + result = tftp_connect_for_tx(state, event); } else { - res = tftp_connect_for_rx(state, event); + result = tftp_connect_for_rx(state, event); } break; case TFTP_EVENT_ACK: /* Connected for transmit */ - res = tftp_connect_for_tx(state, event); + result = tftp_connect_for_tx(state, event); break; case TFTP_EVENT_DATA: /* Connected for receive */ - res = tftp_connect_for_rx(state, event); + result = tftp_connect_for_rx(state, event); break; case TFTP_EVENT_ERROR: @@ -562,7 +559,8 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) failf(state->conn->data, "tftp_send_first: internal error"); break; } - return res; + + return result; } /* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit @@ -702,7 +700,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) struct SessionHandle *data = state->conn->data; ssize_t sbytes; int rblock; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; switch(event) { @@ -728,7 +726,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) if(state->retries>state->retry_max) { failf(data, "tftp_tx: giving up waiting for block %d ack", state->block); - res = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; } else { /* Re-send the data packet */ @@ -739,10 +737,11 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) /* Check all sbytes were sent */ if(sbytes<0) { failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO)); - res = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; } } - return res; + + return result; } /* This is the expected packet. Reset the counters and send the next block */ @@ -759,11 +758,13 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) state->state = TFTP_STATE_FIN; return CURLE_OK; } - res = Curl_fillreadbuffer(state->conn, state->blksize, &state->sbytes); - if(res) - return res; - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4+state->sbytes, SEND_4TH_ARG, + + result = Curl_fillreadbuffer(state->conn, state->blksize, &state->sbytes); + if(result) + return result; + + sbytes = sendto(state->sockfd, (void *) state->spacket.data, + 4 + state->sbytes, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); /* Check all sbytes were sent */ @@ -819,7 +820,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) break; } - return res; + return result; } /********************************************************** @@ -831,48 +832,47 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) **********************************************************/ static CURLcode tftp_translate_code(tftp_error_t error) { - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; if(error != TFTP_ERR_NONE) { switch(error) { case TFTP_ERR_NOTFOUND: - code = CURLE_TFTP_NOTFOUND; + result = CURLE_TFTP_NOTFOUND; break; case TFTP_ERR_PERM: - code = CURLE_TFTP_PERM; + result = CURLE_TFTP_PERM; break; case TFTP_ERR_DISKFULL: - code = CURLE_REMOTE_DISK_FULL; + result = CURLE_REMOTE_DISK_FULL; break; case TFTP_ERR_UNDEF: case TFTP_ERR_ILLEGAL: - code = CURLE_TFTP_ILLEGAL; + result = CURLE_TFTP_ILLEGAL; break; case TFTP_ERR_UNKNOWNID: - code = CURLE_TFTP_UNKNOWNID; + result = CURLE_TFTP_UNKNOWNID; break; case TFTP_ERR_EXISTS: - code = CURLE_REMOTE_FILE_EXISTS; + result = CURLE_REMOTE_FILE_EXISTS; break; case TFTP_ERR_NOSUCHUSER: - code = CURLE_TFTP_NOSUCHUSER; + result = CURLE_TFTP_NOSUCHUSER; break; case TFTP_ERR_TIMEOUT: - code = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; break; case TFTP_ERR_NORESPONSE: - code = CURLE_COULDNT_CONNECT; + result = CURLE_COULDNT_CONNECT; break; default: - code= CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; break; } } - else { - code = CURLE_OK; - } + else + result = CURLE_OK; - return(code); + return result; } /********************************************************** @@ -885,20 +885,21 @@ static CURLcode tftp_translate_code(tftp_error_t error) static CURLcode tftp_state_machine(tftp_state_data_t *state, tftp_event_t event) { - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; struct SessionHandle *data = state->conn->data; + switch(state->state) { case TFTP_STATE_START: DEBUGF(infof(data, "TFTP_STATE_START\n")); - res = tftp_send_first(state, event); + result = tftp_send_first(state, event); break; case TFTP_STATE_RX: DEBUGF(infof(data, "TFTP_STATE_RX\n")); - res = tftp_rx(state, event); + result = tftp_rx(state, event); break; case TFTP_STATE_TX: DEBUGF(infof(data, "TFTP_STATE_TX\n")); - res = tftp_tx(state, event); + result = tftp_tx(state, event); break; case TFTP_STATE_FIN: infof(data, "%s\n", "TFTP finished"); @@ -906,10 +907,11 @@ static CURLcode tftp_state_machine(tftp_state_data_t *state, default: DEBUGF(infof(data, "STATE: %d\n", state->state)); failf(data, "%s", "Internal state machine error"); - res = CURLE_TFTP_ILLEGAL; + result = CURLE_TFTP_ILLEGAL; break; } - return res; + + return result; } /********************************************************** @@ -943,7 +945,6 @@ static CURLcode tftp_disconnect(struct connectdata *conn, bool dead_connection) **********************************************************/ static CURLcode tftp_connect(struct connectdata *conn, bool *done) { - CURLcode code; tftp_state_data_t *state; int blksize, rc; @@ -1017,8 +1018,8 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) Curl_pgrsStartNow(conn->data); *done = TRUE; - code = CURLE_OK; - return(code); + + return CURLE_OK; } /********************************************************** @@ -1031,7 +1032,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) static CURLcode tftp_done(struct connectdata *conn, CURLcode status, bool premature) { - CURLcode code = CURLE_OK; + CURLcode result = CURLE_OK; tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc; (void)status; /* unused */ @@ -1042,9 +1043,9 @@ static CURLcode tftp_done(struct connectdata *conn, CURLcode status, /* If we have encountered an error */ if(state) - code = tftp_translate_code(state->error); + result = tftp_translate_code(state->error); - return code; + return result; } /********************************************************** @@ -1208,8 +1209,8 @@ static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done) } else if(event != TFTP_EVENT_NONE) { result = tftp_state_machine(state, event); - if(result != CURLE_OK) - return(result); + if(result) + return result; *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; if(*done) /* Tell curl we're done */ @@ -1227,11 +1228,11 @@ static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done) } else if(rc != 0) { result = tftp_receive_packet(conn); - if(result != CURLE_OK) - return(result); + if(result) + return result; result = tftp_state_machine(state, state->event); - if(result != CURLE_OK) - return(result); + if(result) + return result; *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; if(*done) /* Tell curl we're done */ @@ -1286,8 +1287,8 @@ static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done) result = tftp_state_machine(state, TFTP_EVENT_INIT); - if(state->state == TFTP_STATE_FIN || result != CURLE_OK) - return(result); + if((state->state == TFTP_STATE_FIN) || result) + return result; tftp_multi_statemach(conn, dophase_done); @@ -1310,30 +1311,30 @@ static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done) static CURLcode tftp_do(struct connectdata *conn, bool *done) { - tftp_state_data_t *state; - CURLcode code; + tftp_state_data_t *state; + CURLcode result; *done = FALSE; if(!conn->proto.tftpc) { - code = tftp_connect(conn, done); - if(code) - return code; + result = tftp_connect(conn, done); + if(result) + return result; } state = (tftp_state_data_t *)conn->proto.tftpc; if(!state) return CURLE_BAD_CALLING_ORDER; - code = tftp_perform(conn, done); + result = tftp_perform(conn, done); /* If tftp_perform() returned an error, use that for return code. If it was OK, see if tftp_translate_code() has an error. */ - if(code == CURLE_OK) + if(!result) /* If we have encountered an internal tftp error, translate it. */ - code = tftp_translate_code(state->error); + result = tftp_translate_code(state->error); - return code; + return result; } static CURLcode tftp_setup_connection(struct connectdata * conn) diff --git a/lib/timeval.c b/lib/timeval.c index 2fd7201..45731ac 100644 --- a/lib/timeval.c +++ b/lib/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,9 +32,17 @@ struct timeval curlx_tvnow(void) ** increases monotonically and wraps once 49.7 days have elapsed. */ struct timeval now; +#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ + (_WIN32_WINNT < _WIN32_WINNT_VISTA) DWORD milliseconds = GetTickCount(); now.tv_sec = milliseconds / 1000; now.tv_usec = (milliseconds % 1000) * 1000; +#else + ULONGLONG milliseconds = GetTickCount64(); + now.tv_sec = (long) (milliseconds / 1000); + now.tv_usec = (long) (milliseconds % 1000) * 1000; +#endif + return now; } @@ -110,7 +118,7 @@ struct timeval curlx_tvnow(void) long curlx_tvdiff(struct timeval newer, struct timeval older) { return (newer.tv_sec-older.tv_sec)*1000+ - (newer.tv_usec-older.tv_usec)/1000; + (long)(newer.tv_usec-older.tv_usec)/1000; } /* diff --git a/lib/transfer.c b/lib/transfer.c index dc817a6..718139b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -75,16 +75,14 @@ #include "curl_ntlm.h" #include "http_negotiate.h" #include "share.h" -#include "curl_memory.h" #include "select.h" #include "multiif.h" #include "connect.h" #include "non-ascii.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* @@ -117,8 +115,8 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) /* this function returns a size_t, so we typecast to int to prevent warnings with picky compilers */ - nread = (int)conn->fread_func(data->req.upload_fromhere, 1, - buffersize, conn->fread_in); + nread = (int)data->set.fread_func(data->req.upload_fromhere, 1, + buffersize, data->set.in); if(nread == CURL_READFUNC_ABORT) { failf(data, "operation aborted by callback"); @@ -203,7 +201,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) strlen(endofline_network)); #ifdef CURL_DOES_CONVERSIONS - CURLcode res; + CURLcode result; int length; if(data->set.prefer_ascii) { /* translate the protocol and data */ @@ -213,10 +211,10 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) /* just translate the protocol portion */ length = strlen(hexbuffer); } - res = Curl_convert_to_network(data, data->req.upload_fromhere, length); + result = Curl_convert_to_network(data, data->req.upload_fromhere, length); /* Curl_convert_to_network calls failf if unsuccessful */ - if(res) - return(res); + if(result) + return result; #endif /* CURL_DOES_CONVERSIONS */ if((nread - hexlen) == 0) @@ -227,11 +225,11 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) } #ifdef CURL_DOES_CONVERSIONS else if((data->set.prefer_ascii) && (!sending_http_headers)) { - CURLcode res; - res = Curl_convert_to_network(data, data->req.upload_fromhere, nread); + CURLcode result; + result = Curl_convert_to_network(data, data->req.upload_fromhere, nread); /* Curl_convert_to_network calls failf if unsuccessful */ - if(res != CURLE_OK) - return(res); + if(result) + return result; } #endif /* CURL_DOES_CONVERSIONS */ @@ -319,8 +317,7 @@ static int data_pending(const struct connectdata *conn) TRUE. The thing is if we read everything, then http2_recv won't be called and we cannot signal the HTTP/2 stream has closed. As a workaround, we return nonzero here to call http2_recv. */ - ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20 && - conn->proto.httpc.closed); + ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20); #else Curl_ssl_data_pending(conn, FIRSTSOCKET); #endif @@ -435,6 +432,7 @@ static CURLcode readwrite_data(struct SessionHandle *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!\n")); nread = 0; } @@ -496,7 +494,7 @@ static CURLcode readwrite_data(struct SessionHandle *data, /* We've stopped dealing with input, get out of the do-while loop */ if(nread > 0) { - if(Curl_multi_pipeline_enabled(conn->data->multi)) { + if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) { infof(data, "Rewinding stream by : %zd" " bytes on url %s (zero-length body)\n", @@ -547,6 +545,18 @@ static CURLcode readwrite_data(struct SessionHandle *data, if(data->state.resume_from && !k->content_range && (data->set.httpreq==HTTPREQ_GET) && !k->ignorebody) { + + if(k->size == data->state.resume_from) { + /* 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"); + /* Abort download */ + k->keepon &= ~KEEP_RECV; + *done = TRUE; + return CURLE_OK; + } + /* we wanted to resume a download, although the server doesn't * seem to support this and we did this with a GET (if it * wasn't a GET we did a POST or PUT resume) */ @@ -629,7 +639,7 @@ static CURLcode readwrite_data(struct SessionHandle *data, if(dataleft != 0) { infof(conn->data, "Leftovers after chunking: %zu bytes\n", dataleft); - if(Curl_multi_pipeline_enabled(conn->data->multi)) { + if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) { /* only attempt the rewind if we truly are pipelining */ infof(conn->data, "Rewinding %zu bytes\n",dataleft); read_rewind(conn, dataleft); @@ -652,7 +662,7 @@ static CURLcode readwrite_data(struct SessionHandle *data, excess = (size_t)(k->bytecount + nread - k->maxdownload); if(excess > 0 && !k->ignorebody) { - if(Curl_multi_pipeline_enabled(conn->data->multi)) { + if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) { /* The 'excess' amount below can't be more than BUFSIZE which always will fit in a size_t */ infof(data, @@ -746,7 +756,6 @@ static CURLcode readwrite_data(struct SessionHandle *data, result = Curl_unencode_gzip_write(conn, k, nread); break; - case COMPRESS: default: failf (data, "Unrecognized content encoding type. " "libcurl understands `identity', `deflate' and `gzip' " @@ -818,13 +827,6 @@ static CURLcode readwrite_upload(struct SessionHandle *data, *didwhat |= KEEP_SEND; - /* - * We loop here to do the READ and SEND loop until we run out of - * data to send or until we get EWOULDBLOCK back - * - * FIXME: above comment is misleading. Currently no looping is - * actually done in do-while loop below. - */ do { /* only read more data if there's no upload data already @@ -891,15 +893,6 @@ static CURLcode readwrite_upload(struct SessionHandle *data, /* store number of bytes available for upload */ data->req.upload_present = nread; -#ifndef CURL_DISABLE_SMTP - if(conn->handler->protocol & PROTO_FAMILY_SMTP) { - result = Curl_smtp_escape_eob(conn, nread); - if(result) - return result; - } - else -#endif /* CURL_DISABLE_SMTP */ - /* convert LF to CRLF if so asked */ if((!sending_http_headers) && ( #ifdef CURL_DO_LINEEND_CONV @@ -907,12 +900,16 @@ static CURLcode readwrite_upload(struct SessionHandle *data, (data->set.prefer_ascii) || #endif (data->set.crlf))) { - if(data->state.scratch == NULL) - data->state.scratch = malloc(2*BUFSIZE); - if(data->state.scratch == NULL) { - failf (data, "Failed to alloc scratch buffer!"); - return CURLE_OUT_OF_MEMORY; + /* Do we need to allocate a scratch buffer? */ + if(!data->state.scratch) { + data->state.scratch = malloc(2 * BUFSIZE); + if(!data->state.scratch) { + failf(data, "Failed to alloc scratch buffer!"); + + return CURLE_OUT_OF_MEMORY; + } } + /* * ASCII/EBCDIC Note: This is presumably a text (not binary) * transfer so the data should already be in ASCII. @@ -932,6 +929,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data, else data->state.scratch[si] = data->req.upload_fromhere[i]; } + if(si != nread) { /* only perform the special operation if we really did replace anything */ @@ -944,6 +942,14 @@ static CURLcode readwrite_upload(struct SessionHandle *data, data->req.upload_present = nread; } } + +#ifndef CURL_DISABLE_SMTP + if(conn->handler->protocol & PROTO_FAMILY_SMTP) { + result = Curl_smtp_escape_eob(conn, nread); + if(result) + return result; + } +#endif /* CURL_DISABLE_SMTP */ } /* if 0 == data->req.upload_present */ else { /* We have a partial buffer left from a previous "round". Use @@ -1006,9 +1012,9 @@ static CURLcode readwrite_upload(struct SessionHandle *data, * be read and written to/from the connection. */ CURLcode Curl_readwrite(struct connectdata *conn, + struct SessionHandle *data, bool *done) { - struct SessionHandle *data = conn->data; struct SingleRequest *k = &data->req; CURLcode result; int didwhat=0; @@ -1032,6 +1038,11 @@ CURLcode Curl_readwrite(struct connectdata *conn, else fd_write = CURL_SOCKET_BAD; + if(conn->data->state.drain) { + select_res |= CURL_CSELECT_IN; + DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data\n")); + } + if(!select_res) /* Call for select()/poll() only, if read/write/error status is not known. */ select_res = Curl_socket_ready(fd_read, fd_write, 0); @@ -1202,10 +1213,10 @@ int Curl_single_getsock(const struct connectdata *conn, if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) { if((conn->sockfd != conn->writesockfd) || - !(data->req.keepon & KEEP_RECV)) { - /* only if they are not the same socket or we didn't have a readable + bitmap == GETSOCK_BLANK) { + /* only if they are not the same socket and we have a readable one, we increase index */ - if(data->req.keepon & KEEP_RECV) + if(bitmap != GETSOCK_BLANK) sockindex++; /* increase index if we need two entries */ DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD); @@ -1256,7 +1267,7 @@ long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps, * the next packet at the adjusted rate. We should wait * longer when using larger packets, for instance. */ - rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps); + rv = ((curl_off_t)(pkt_size * 1000) / rate_bps); /* Catch rounding errors and always slow down at least 1ms if * we are running too fast. @@ -1278,7 +1289,7 @@ long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps, */ CURLcode Curl_pretransfer(struct SessionHandle *data) { - CURLcode res; + CURLcode result; if(!data->change.url) { /* we can't do anything without URL */ failf(data, "No URL set!"); @@ -1288,32 +1299,35 @@ CURLcode Curl_pretransfer(struct SessionHandle *data) /* Init the SSL session ID cache here. We do it here since we want to do it after the *_setopt() calls (that could specify the size of the cache) but before any transfer takes place. */ - res = Curl_ssl_initsessions(data, data->set.ssl.max_ssl_sessions); - if(res) - return res; + result = Curl_ssl_initsessions(data, data->set.ssl.max_ssl_sessions); + if(result) + return result; data->set.followlocation=0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ data->state.errorbuf = FALSE; /* no error has occurred */ data->state.httpversion = 0; /* don't assume any particular server version */ - data->state.ssl_connect_retry = FALSE; - data->state.authproblem = FALSE; data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; Curl_safefree(data->info.wouldredirect); data->info.wouldredirect = NULL; + if(data->set.httpreq == HTTPREQ_PUT) + data->state.infilesize = data->set.filesize; + else + data->state.infilesize = data->set.postfieldsize; + /* If there is a list of cookie files to read, do it now! */ if(data->change.cookielist) Curl_cookie_loadfiles(data); /* If there is a list of host pairs to deal with */ if(data->change.resolve) - res = Curl_loadhostpairs(data); + result = Curl_loadhostpairs(data); - if(!res) { + if(!result) { /* Allow data->set.use_port to set which port to use. This needs to be * disabled for example when we follow Location: headers to URLs using * different ports! */ @@ -1328,6 +1342,7 @@ CURLcode Curl_pretransfer(struct SessionHandle *data) #endif Curl_initinfo(data); /* reset session-specific information "variables" */ + Curl_pgrsResetTimesSizes(data); Curl_pgrsStartNow(data); if(data->set.timeout) @@ -1343,7 +1358,7 @@ CURLcode Curl_pretransfer(struct SessionHandle *data) data->state.authproxy.picked &= data->state.authproxy.want; } - return res; + return result; } /* @@ -1619,7 +1634,7 @@ CURLcode Curl_follow(struct SessionHandle *data, if(type == FOLLOW_REDIR) { if((data->set.maxredirs != -1) && (data->set.followlocation >= data->set.maxredirs)) { - failf(data,"Maximum (%ld) redirects followed", data->set.maxredirs); + failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); return CURLE_TOO_MANY_REDIRECTS; } @@ -1828,13 +1843,13 @@ Curl_reconnect_request(struct connectdata **connp) * (again). Slight Lack of feedback in the report, but I don't think this * extra check can do much harm. */ - if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) { + if(!result || (CURLE_SEND_ERROR == result)) { bool async; bool protocol_done = TRUE; /* Now, redo the connect and get a new connection */ result = Curl_connect(data, connp, &async, &protocol_done); - if(CURLE_OK == result) { + if(!result) { /* We have connected or sent away a name resolve query fine */ conn = *connp; /* setup conn to again point to something nice */ @@ -1872,12 +1887,10 @@ CURLcode Curl_retry_request(struct connectdata *conn, !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP))) return CURLE_OK; - if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry || - ((data->req.bytecount + - data->req.headerbytecount == 0) && - conn->bits.reuse && - !data->set.opt_no_body && - data->set.rtspreq != RTSPREQ_RECEIVE)) { + if((data->req.bytecount + data->req.headerbytecount == 0) && + conn->bits.reuse && + !data->set.opt_no_body && + (data->set.rtspreq != RTSPREQ_RECEIVE)) { /* We got no data, we attempted to re-use a connection and yet we want a "body". This might happen if the connection was left alive when we were done using it before, but that was closed when we wanted to read from diff --git a/lib/transfer.h b/lib/transfer.h index ad4a3ac..316aeae 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,7 +40,8 @@ CURLcode Curl_follow(struct SessionHandle *data, char *newurl, followtype type); -CURLcode Curl_readwrite(struct connectdata *conn, bool *done); +CURLcode Curl_readwrite(struct connectdata *conn, + struct SessionHandle *data, bool *done); int Curl_single_getsock(const struct connectdata *conn, curl_socket_t *socks, int numsocks); @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -47,6 +47,10 @@ #include <inet.h> #endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + #ifndef HAVE_SOCKET #error "We can't compile without socket() support!" #endif @@ -120,15 +124,12 @@ int curl_win32_idn_to_ascii(const char *in, char **out); #include "curl_rtmp.h" #include "gopher.h" #include "http_proxy.h" -#include "bundles.h" #include "conncache.h" #include "multihandle.h" #include "pipeline.h" #include "dotdot.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "strdup.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -141,7 +142,6 @@ find_oldest_idle_connection_in_bundle(struct SessionHandle *data, struct connectbundle *bundle); static void conn_free(struct connectdata *conn); static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke); -static CURLcode do_init(struct connectdata *conn); static CURLcode parse_url_login(struct SessionHandle *data, struct connectdata *conn, char **userptr, char **passwdptr, @@ -215,6 +215,15 @@ static const struct Curl_handler * const protocols[] = { #endif #endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \ + (CURL_SIZEOF_CURL_OFF_T > 4) && \ + (!defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO)) + &Curl_handler_smb, +#ifdef USE_SSL + &Curl_handler_smbs, +#endif +#endif + #ifndef CURL_DISABLE_SMTP &Curl_handler_smtp, #ifdef USE_SSL @@ -270,8 +279,9 @@ void Curl_freeset(struct SessionHandle *data) { /* Free all dynamic strings stored in the data->set substructure. */ enum dupstring i; - for(i=(enum dupstring)0; i < STRING_LAST; i++) + for(i=(enum dupstring)0; i < STRING_LAST; i++) { Curl_safefree(data->set.str[i]); + } if(data->change.referer_alloc) { Curl_safefree(data->change.referer); @@ -345,7 +355,7 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) CURLcode Curl_dupset(struct SessionHandle *dst, struct SessionHandle *src) { - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; enum dupstring i; /* Copy src->set into dst->set first, then deal with the strings @@ -356,14 +366,25 @@ CURLcode Curl_dupset(struct SessionHandle *dst, struct SessionHandle *src) memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); /* duplicate all strings */ - for(i=(enum dupstring)0; i< STRING_LAST; i++) { - r = setstropt(&dst->set.str[i], src->set.str[i]); - if(r != CURLE_OK) - break; + for(i=(enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { + result = setstropt(&dst->set.str[i], src->set.str[i]); + if(result) + return result; + } + + /* duplicate memory areas pointed to */ + i = STRING_COPYPOSTFIELDS; + if(src->set.postfieldsize && src->set.str[i]) { + /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ + dst->set.str[i] = Curl_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); + if(!dst->set.str[i]) + return CURLE_OUT_OF_MEMORY; + /* point to the new copy */ + dst->set.postfields = dst->set.str[i]; } - /* If a failure occurred, freeing has to be performed externally. */ - return r; + return CURLE_OK; } /* @@ -425,10 +446,8 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_ssl_free_certinfo(data); /* Cleanup possible redirect junk */ - if(data->req.newurl) { - free(data->req.newurl); - data->req.newurl = NULL; - } + free(data->req.newurl); + data->req.newurl = NULL; if(data->change.referer_alloc) { Curl_safefree(data->change.referer); @@ -474,7 +493,7 @@ CURLcode Curl_close(struct SessionHandle *data) */ CURLcode Curl_init_userdefined(struct UserDefined *set) { - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; set->out = stdout; /* default output to stdout */ set->in = stdin; /* default input from stdin */ @@ -540,8 +559,9 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) define since we internally only use the lower 16 bits for the passed in bitmask to not conflict with the private bits */ set->allowed_protocols = CURLPROTO_ALL; - set->redir_protocols = - CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */ + set->redir_protocols = CURLPROTO_ALL & /* All except FILE, SCP and SMB */ + ~(CURLPROTO_FILE | CURLPROTO_SCP | CURLPROTO_SMB | + CURLPROTO_SMBS); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* @@ -550,17 +570,34 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) */ set->socks5_gssapi_nec = FALSE; /* set default GSS-API service name */ - res = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE], - (char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE); - if(res != CURLE_OK) - return res; + result = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE], + (char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE); + if(result) + return result; + + /* set default negotiate proxy service name */ + result = setstropt(&set->str[STRING_PROXY_SERVICE_NAME], + (char *) CURL_DEFAULT_PROXY_SERVICE_NAME); + if(result) + return result; + + /* set default negotiate service name */ + result = setstropt(&set->str[STRING_SERVICE_NAME], + (char *) CURL_DEFAULT_SERVICE_NAME); + if(result) + return result; #endif /* This is our preferred CA cert bundle/path since install time */ #if defined(CURL_CA_BUNDLE) - res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE); -#elif defined(CURL_CA_PATH) - res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH); + result = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE); + if(result) + return result; +#endif +#if defined(CURL_CA_PATH) + result = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH); + if(result) + return result; #endif set->wildcardmatch = FALSE; @@ -578,7 +615,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) set->ssl_enable_alpn = TRUE; set->expect_100_timeout = 1000L; /* Wait for a second by default. */ - return res; + set->sep_headers = TRUE; /* separated header lists by default */ + return result; } /** @@ -591,9 +629,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) CURLcode Curl_open(struct SessionHandle **curl) { - CURLcode res = CURLE_OK; + CURLcode result; struct SessionHandle *data; - CURLcode status; /* Very simple start-up: alloc the struct, init it with zeroes and return */ data = calloc(1, sizeof(struct SessionHandle)); @@ -605,11 +642,11 @@ CURLcode Curl_open(struct SessionHandle **curl) data->magic = CURLEASY_MAGIC_NUMBER; - status = Curl_resolver_init(&data->state.resolver); - if(status) { + result = Curl_resolver_init(&data->state.resolver); + if(result) { DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); free(data); - return status; + return result; } /* We do some initial setup here, all those fields that can't be just 0 */ @@ -617,10 +654,10 @@ CURLcode Curl_open(struct SessionHandle **curl) data->state.headerbuff = malloc(HEADERSIZE); if(!data->state.headerbuff) { DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } else { - res = Curl_init_userdefined(&data->set); + result = Curl_init_userdefined(&data->set); data->state.headersize=HEADERSIZE; @@ -638,10 +675,9 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ } - if(res) { + if(result) { Curl_resolver_cleanup(data->state.resolver); - if(data->state.headerbuff) - free(data->state.headerbuff); + free(data->state.headerbuff); Curl_freeset(data); free(data); data = NULL; @@ -649,7 +685,7 @@ CURLcode Curl_open(struct SessionHandle **curl) else *curl = data; - return res; + return result; } CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, @@ -744,7 +780,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, break; case CURLOPT_FAILONERROR: /* - * Don't output the >=300 error code HTML-page, but instead only + * Don't output the >=400 error code HTML-page, but instead only * return error. */ data->set.http_fail_on_error = (0 != va_arg(param, long))?TRUE:FALSE; @@ -867,7 +903,11 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * Set explicit SSL version to try to connect with, as some SSL * implementations are lame. */ +#ifdef USE_SSL data->set.ssl.version = va_arg(param, long); +#else + result = CURLE_UNKNOWN_OPTION; +#endif break; #ifndef CURL_DISABLE_HTTP @@ -1139,6 +1179,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, /* * Set cookie file name to dump all cookies to when we're done. */ + { + struct CookieInfo *newcookies; result = setstropt(&data->set.str[STRING_COOKIEJAR], va_arg(param, char *)); @@ -1146,8 +1188,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * Activate the cookie parser. This may or may not already * have been made. */ - data->cookies = Curl_cookie_init(data, NULL, data->cookies, - data->set.cookiesession); + newcookies = Curl_cookie_init(data, NULL, data->cookies, + data->set.cookiesession); + if(!newcookies) + result = CURLE_OUT_OF_MEMORY; + data->cookies = newcookies; + } break; case CURLOPT_COOKIESESSION: @@ -1191,14 +1237,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, /* flush cookies to file, takes care of the locking */ Curl_flush_cookies(data, 0); } + else if(Curl_raw_equal(argptr, "RELOAD")) { + /* reload cookies from file */ + Curl_cookie_loadfiles(data); + break; + } else { if(!data->cookies) /* if cookie engine was not running, activate it */ data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); argptr = strdup(argptr); - if(!argptr) { + if(!argptr || !data->cookies) { result = CURLE_OUT_OF_MEMORY; + free(argptr); } else { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -1431,12 +1483,29 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, va_arg(param, char *)); break; + case CURLOPT_PROXY_SERVICE_NAME: + /* + * Set negotiate proxy service name + */ + result = setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], + va_arg(param, char *)); + break; + case CURLOPT_SOCKS5_GSSAPI_NEC: /* * set flag for nec socks5 support */ data->set.socks5_gssapi_nec = (0 != va_arg(param, long))?TRUE:FALSE; break; + + case CURLOPT_SERVICE_NAME: + /* + * Set negotiate service identity + */ + result = setstropt(&data->set.str[STRING_SERVICE_NAME], + va_arg(param, char *)); + break; + #endif case CURLOPT_HEADERDATA: @@ -1959,30 +2028,63 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.ssl.verifyhost = (0 != arg)?TRUE:FALSE; break; -#ifdef USE_SSLEAY - /* since these two options are only possible to use on an OpenSSL- - powered libcurl we #ifdef them on this condition so that libcurls - built against other SSL libs will return a proper error when trying - to set this option! */ + case CURLOPT_SSL_VERIFYSTATUS: + /* + * Enable certificate status verifying. + */ + if(!Curl_ssl_cert_status_request()) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.ssl.verifystatus = (0 != va_arg(param, long))?TRUE:FALSE; + break; case CURLOPT_SSL_CTX_FUNCTION: +#ifdef have_curlssl_ssl_ctx /* * Set a SSL_CTX callback */ data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); +#else + result = CURLE_NOT_BUILT_IN; +#endif break; case CURLOPT_SSL_CTX_DATA: +#ifdef have_curlssl_ssl_ctx /* * Set a SSL_CTX callback parameter pointer */ data->set.ssl.fsslctxp = va_arg(param, void *); - break; +#else + result = CURLE_NOT_BUILT_IN; #endif -#if defined(USE_SSLEAY) || defined(USE_QSOSSL) || defined(USE_GSKIT) || \ - defined(USE_NSS) + break; + case CURLOPT_SSL_FALSESTART: + /* + * Enable TLS false start. + */ + if(!Curl_ssl_false_start()) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.ssl.falsestart = (0 != va_arg(param, long))?TRUE:FALSE; + break; case CURLOPT_CERTINFO: +#ifdef have_curlssl_certinfo data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE; - break; +#else + result = CURLE_NOT_BUILT_IN; #endif + break; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify file name of the public key in DER format. + */ + result = setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], + va_arg(param, char *)); + break; case CURLOPT_CAINFO: /* * Set CA info for SSL connection. Specify file name of the CA certificate @@ -1991,6 +2093,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, va_arg(param, char *)); break; case CURLOPT_CAPATH: +#ifdef have_curlssl_ca_path /* not supported by all backends */ /* * Set CA path info for SSL connection. Specify directory name of the CA * certificates which have been prepared using openssl c_rehash utility. @@ -1998,6 +2101,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, /* This does not work on windows. */ result = setstropt(&data->set.str[STRING_SSL_CAPATH], va_arg(param, char *)); +#else + result = CURLE_NOT_BUILT_IN; +#endif break; case CURLOPT_CRLFILE: /* @@ -2079,16 +2185,15 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->share->dirty++; - if(data->share->hostcache) { + if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) { /* use shared host cache */ - data->dns.hostcache = data->share->hostcache; + data->dns.hostcache = &data->share->hostcache; data->dns.hostcachetype = HCACHE_SHARED; } #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) if(data->share->cookies) { /* use shared cookie list, first free own one if any */ - if(data->cookies) - Curl_cookie_cleanup(data->cookies); + Curl_cookie_cleanup(data->cookies); /* enable cookies since we now use a share that uses cookies! */ data->cookies = data->share->cookies; } @@ -2129,7 +2234,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, case CURLOPT_SSL_OPTIONS: arg = va_arg(param, long); - data->set.ssl_enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE; + data->set.ssl_enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); + data->set.ssl_no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); break; #endif @@ -2316,7 +2422,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * know that an unsigned int will always hold the value so we blindly * typecast to this type */ - data->set.scope = curlx_sltoui(va_arg(param, long)); + data->set.scope_id = curlx_sltoui(va_arg(param, long)); break; case CURLOPT_PROTOCOLS: @@ -2533,6 +2639,19 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.ssl_enable_alpn = (0 != va_arg(param, long))?TRUE:FALSE; break; +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + result = setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], + va_arg(param, char *)); + break; +#endif + + case CURLOPT_PATH_AS_IS: + data->set.path_as_is = (0 != va_arg(param, long))?TRUE:FALSE; + break; + case CURLOPT_PIPEWAIT: + data->set.pipewait = (0 != va_arg(param, long))?TRUE:FALSE; + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; @@ -2565,7 +2684,8 @@ static void conn_free(struct connectdata *conn) if(CURL_SOCKET_BAD != conn->tempsock[1]) Curl_closesocket(conn, conn->tempsock[1]); -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) Curl_ntlm_wb_cleanup(conn); #endif @@ -2631,8 +2751,10 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) Curl_hostcache_prune(data); /* kill old DNS cache entries */ +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) /* Cleanup NTLM connection-related data */ Curl_http_ntlm_cleanup(conn); +#endif if(conn->handler->disconnect) /* This is set if protocol-specific cleanups should be made */ @@ -2655,16 +2777,15 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) free(conn->host.encalloc); /* encoded host name buffer, must be freed with idn_free() since this was allocated by curl_win32_idn_to_ascii */ - if(conn->proxy.encalloc) - free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed - with idn_free() since this was allocated by - curl_win32_idn_to_ascii */ + free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed + with idn_free() since this was allocated by + curl_win32_idn_to_ascii */ #endif Curl_ssl_close(conn, FIRSTSOCKET); /* Indicate to all handles on the pipe that we're dead */ - if(Curl_multi_pipeline_enabled(data->multi)) { + if(Curl_pipeline_wanted(data->multi, CURLPIPE_ANY)) { signalPipeClose(conn->send_pipe, TRUE); signalPipeClose(conn->recv_pipe, TRUE); } @@ -2692,32 +2813,31 @@ static bool SocketIsDead(curl_socket_t sock) return ret_val; } +/* + * IsPipeliningPossible() returns TRUE if the options set would allow + * pipelining/multiplexing and the connection is using a HTTP protocol. + */ static bool IsPipeliningPossible(const struct SessionHandle *handle, const struct connectdata *conn) { - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && - Curl_multi_pipeline_enabled(handle->multi) && - (handle->set.httpreq == HTTPREQ_GET || - handle->set.httpreq == HTTPREQ_HEAD) && - handle->set.httpversion != CURL_HTTP_VERSION_1_0) - return TRUE; + /* If a HTTP protocol and pipelining is enabled */ + if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + + if(Curl_pipeline_wanted(handle->multi, CURLPIPE_HTTP1) && + (handle->set.httpversion != CURL_HTTP_VERSION_1_0) && + (handle->set.httpreq == HTTPREQ_GET || + handle->set.httpreq == HTTPREQ_HEAD)) + /* didn't ask for HTTP/1.0 and a GET or HEAD */ + return TRUE; + if(Curl_pipeline_wanted(handle->multi, CURLPIPE_MULTIPLEX) && + (handle->set.httpversion == CURL_HTTP_VERSION_2_0)) + /* allows HTTP/2 */ + return TRUE; + } return FALSE; } -bool Curl_isPipeliningEnabled(const struct SessionHandle *handle) -{ - return Curl_multi_pipeline_enabled(handle->multi); -} - -CURLcode Curl_addHandleToPipeline(struct SessionHandle *data, - struct curl_llist *pipeline) -{ - if(!Curl_llist_insert_next(pipeline, pipeline->tail, data)) - return CURLE_OUT_OF_MEMORY; - return CURLE_OK; -} - int Curl_removeHandleFromPipeline(struct SessionHandle *handle, struct curl_llist *pipeline) { @@ -2765,15 +2885,14 @@ void Curl_getoff_all_pipelines(struct SessionHandle *data, struct connectdata *conn) { bool recv_head = (conn->readchannel_inuse && - (gethandleathead(conn->recv_pipe) == data)) ? TRUE : FALSE; - + Curl_recvpipe_head(data, conn)); bool send_head = (conn->writechannel_inuse && - (gethandleathead(conn->send_pipe) == data)) ? TRUE : FALSE; + Curl_sendpipe_head(data, conn)); if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head) - conn->readchannel_inuse = FALSE; + Curl_pipeline_leave_read(conn); if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head) - conn->writechannel_inuse = FALSE; + Curl_pipeline_leave_write(conn); } static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) @@ -2825,7 +2944,7 @@ find_oldest_idle_connection(struct SessionHandle *data) now = Curl_tvnow(); - Curl_hash_start_iterate(bc->hash, &iter); + Curl_hash_start_iterate(&bc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { @@ -2959,6 +3078,13 @@ static void prune_dead_connections(struct SessionHandle *data) } } + +static size_t max_pipeline_length(struct Curl_multi *multi) +{ + return multi ? multi->max_pipeline_length : 0; +} + + /* * Given one filled in connection struct (named needle), this function should * detect if there already is one that has all the significant details @@ -2975,17 +3101,21 @@ static bool ConnectionExists(struct SessionHandle *data, struct connectdata *needle, struct connectdata **usethis, - bool *force_reuse) + bool *force_reuse, + bool *waitpipe) { struct connectdata *check; struct connectdata *chosen = 0; bool canPipeline = IsPipeliningPossible(data, needle); +#ifdef USE_NTLM bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) || (data->state.authhost.want & CURLAUTH_NTLM_WB)) && (needle->handler->protocol & PROTO_FAMILY_HTTP) ? TRUE : FALSE; +#endif struct connectbundle *bundle; *force_reuse = FALSE; + *waitpipe = FALSE; /* We can't pipe if the site is blacklisted */ if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) { @@ -2994,10 +3124,11 @@ ConnectionExists(struct SessionHandle *data, /* Look up the bundle with all the connections to this particular host */ - bundle = Curl_conncache_find_bundle(data->state.conn_cache, - needle->host.name); + bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache); if(bundle) { - size_t max_pipe_len = Curl_multi_max_pipeline_length(data->multi); + /* Max pipe length is zero (unlimited) for multiplexed connections */ + size_t max_pipe_len = (bundle->multiuse != BUNDLE_MULTIPLEX)? + max_pipeline_length(data->multi):0; size_t best_pipe_len = max_pipe_len; struct curl_llist_element *curr; @@ -3005,15 +3136,25 @@ ConnectionExists(struct SessionHandle *data, needle->host.name, (void *)bundle); /* We can't pipe if we don't know anything about the server */ - if(canPipeline && !bundle->server_supports_pipelining) { - infof(data, "Server doesn't support pipelining\n"); - canPipeline = FALSE; + if(canPipeline) { + if(bundle->multiuse <= BUNDLE_UNKNOWN) { + if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) { + infof(data, "Server doesn't support multi-use yet, wait\n"); + *waitpipe = TRUE; + return FALSE; /* no re-use */ + } + + infof(data, "Server doesn't support multi-use (yet)\n"); + canPipeline = FALSE; + } } curr = bundle->conn_list->head; while(curr) { bool match = FALSE; +#if defined(USE_NTLM) bool credentialsMatch = FALSE; +#endif size_t pipeLen; /* @@ -3029,16 +3170,19 @@ ConnectionExists(struct SessionHandle *data, pipeLen = check->send_pipe->size + check->recv_pipe->size; if(canPipeline) { - /* Make sure the pipe has only GET requests */ - struct SessionHandle* sh = gethandleathead(check->send_pipe); - struct SessionHandle* rh = gethandleathead(check->recv_pipe); - if(sh) { - if(!IsPipeliningPossible(sh, check)) - continue; - } - else if(rh) { - if(!IsPipeliningPossible(rh, check)) - continue; + + if(!check->bits.multiplex) { + /* If not multiplexing, make sure the pipe has only GET requests */ + struct SessionHandle* sh = gethandleathead(check->send_pipe); + struct SessionHandle* rh = gethandleathead(check->recv_pipe); + if(sh) { + if(!IsPipeliningPossible(sh, check)) + continue; + } + else if(rh) { + if(!IsPipeliningPossible(rh, check)) + continue; + } } } else { @@ -3118,8 +3262,11 @@ ConnectionExists(struct SessionHandle *data, continue; } - if((!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) || - wantNTLMhttp) { + if((!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) +#ifdef USE_NTLM + || (wantNTLMhttp || check->ntlm.state != NTLMSTATE_NONE) +#endif + ) { /* This protocol requires credentials per connection or is HTTP+NTLM, so verify that we're using the same name and password as well */ if(!strequal(needle->user, check->user) || @@ -3127,7 +3274,9 @@ ConnectionExists(struct SessionHandle *data, /* one of them was different */ continue; } +#if defined(USE_NTLM) credentialsMatch = TRUE; +#endif } if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL || @@ -3179,6 +3328,7 @@ ConnectionExists(struct SessionHandle *data, } if(match) { +#if defined(USE_NTLM) /* If we are looking for an HTTP+NTLM connection, check if this is already authenticating with the right credentials. If not, keep looking so that we can reuse NTLM connections if @@ -3197,6 +3347,7 @@ ConnectionExists(struct SessionHandle *data, chosen = check; continue; } +#endif if(canPipeline) { /* We can pipeline if we want to. Let's continue looking for @@ -3210,19 +3361,42 @@ ConnectionExists(struct SessionHandle *data, } /* We can't use the connection if the pipe is full */ - if(pipeLen >= max_pipe_len) + if(max_pipe_len && (pipeLen >= max_pipe_len)) { + infof(data, "Pipe is full, skip (%zu)\n", pipeLen); continue; - + } +#ifdef USE_NGHTTP2 + /* If multiplexed, make sure we don't go over concurrency limit */ + if(check->bits.multiplex) { + /* Multiplexed connections can only be HTTP/2 for now */ + struct http_conn *httpc = &check->proto.httpc; + if(pipeLen >= httpc->settings.max_concurrent_streams) { + infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n", + pipeLen); + continue; + } + } +#endif /* We can't use the connection if the pipe is penalized */ - if(Curl_pipeline_penalized(data, check)) + if(Curl_pipeline_penalized(data, check)) { + infof(data, "Penalized, skip\n"); continue; + } - if(pipeLen < best_pipe_len) { - /* This connection has a shorter pipe so far. We'll pick this - and continue searching */ + if(max_pipe_len) { + if(pipeLen < best_pipe_len) { + /* This connection has a shorter pipe so far. We'll pick this + and continue searching */ + chosen = check; + best_pipe_len = pipeLen; + continue; + } + } + else { + /* When not pipelining (== multiplexed), we have a match here! */ chosen = check; - best_pipe_len = pipeLen; - continue; + infof(data, "Multiplexed connection found!\n"); + break; } } else { @@ -3274,20 +3448,6 @@ ConnectionDone(struct SessionHandle *data, struct connectdata *conn) return (conn_candidate == conn) ? FALSE : TRUE; } -/* - * The given input connection struct pointer is to be stored in the connection - * cache. If the cache is already full, least interesting existing connection - * (if any) gets closed. - * - * The given connection should be unique. That must've been checked prior to - * this call. - */ -static CURLcode ConnectionStore(struct SessionHandle *data, - struct connectdata *conn) -{ - return Curl_conncache_add_conn(data->state.conn_cache, conn); -} - /* after a TCP connection to the proxy has been verified, this function does the next magic step. @@ -3533,7 +3693,7 @@ static void fix_hostname(struct SessionHandle *data, host->dispname = host->name; len = strlen(host->name); - if(host->name[len-1] == '.') + if(len && (host->name[len-1] == '.')) /* strip off a single trailing dot if present, primarily for SNI but there's no use for it */ host->name[len-1]=0; @@ -3550,7 +3710,7 @@ static void fix_hostname(struct SessionHandle *data, stringprep_locale_charset ()); if(rc != IDNA_SUCCESS) infof(data, "Failed to convert %s to ACE; %s\n", - host->name, Curl_idn_strerror(conn,rc)); + host->name, Curl_idn_strerror(conn, rc)); else { /* tld_check_name() displays a warning if the host name contains "illegal" characters for this TLD */ @@ -3655,16 +3815,17 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) conn->ip_version = data->set.ipver; -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; conn->ntlm_auth_hlpr_pid = 0; conn->challenge_header = NULL; conn->response_header = NULL; #endif - if(Curl_multi_pipeline_enabled(data->multi) && - !conn->master_buffer) { - /* Allocate master_buffer to be used for pipelining */ + if(Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1) && + !conn->master_buffer) { + /* Allocate master_buffer to be used for HTTP/1 pipelining */ conn->master_buffer = calloc(BUFSIZE, sizeof (char)); if(!conn->master_buffer) goto error; @@ -3703,9 +3864,9 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) conn->send_pipe = NULL; conn->recv_pipe = NULL; - Curl_safefree(conn->master_buffer); - Curl_safefree(conn->localdev); - Curl_safefree(conn); + free(conn->master_buffer); + free(conn->localdev); + free(conn); return NULL; } @@ -3772,6 +3933,13 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, *prot_missing = FALSE; + /* We might pass the entire URL into the request so we need to make sure + * there are no bad characters in there.*/ + if(strpbrk(data->change.url, "\r\n")) { + failf(data, "Illegal characters found in URL"); + return CURLE_URL_MALFORMAT; + } + /************************************************************* * Parse the URL. * @@ -3941,7 +4109,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, path[0] = '/'; rebuild_url = TRUE; } - else { + else if(!data->set.path_as_is) { /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */ char *newp = Curl_dedotdotify(path); if(!newp) @@ -4003,7 +4171,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, * the host name */ result = parse_url_login(data, conn, userp, passwdp, optionsp); - if(result != CURLE_OK) + if(result) return result; if(conn->host.name[0] == '[') { @@ -4024,7 +4192,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, /* The address scope was well formed. Knock it out of the hostname. */ memmove(percent, endp, strlen(endp)+1); - conn->scope = (unsigned int)scope; + conn->scope_id = (unsigned int)scope; } else { /* Zone identifier is not numeric */ @@ -4046,11 +4214,11 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, } } if(scopeidx > 0) { + char *p = percent + identifier_offset + strlen(ifname); + /* Remove zone identifier from hostname */ - memmove(percent, - percent + identifier_offset + strlen(ifname), - identifier_offset + strlen(ifname)); - conn->scope = scopeidx; + memmove(percent, p, strlen(p) + 1); + conn->scope_id = scopeidx; } else #endif /* HAVE_NET_IF_H && IFNAMSIZ */ @@ -4059,9 +4227,9 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, } } - if(data->set.scope) + if(data->set.scope_id) /* Override any scope that was set above. */ - conn->scope = data->set.scope; + conn->scope_id = data->set.scope_id; /* Remove the fragment part of the path. Per RFC 2396, this is always the last part of the URI. We are looking for the first '#' so that we deal @@ -4153,7 +4321,7 @@ static CURLcode setup_connection_internals(struct connectdata *conn) if(p->setup_connection) { result = (*p->setup_connection)(conn); - if(result != CURLE_OK) + if(result) return result; p = conn->handler; /* May have changed. */ @@ -4327,9 +4495,8 @@ static char *detect_proxy(struct connectdata *conn) prox=curl_getenv(proxy_env); } - if(prox && *prox) { /* don't count "" strings */ + if(prox) proxy = prox; /* use this */ - } else { proxy = curl_getenv("all_proxy"); /* default proxy to use */ if(!proxy) @@ -4337,8 +4504,7 @@ static char *detect_proxy(struct connectdata *conn) } } /* if(!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified non-proxy */ - if(no_proxy) - free(no_proxy); + free(no_proxy); #else /* !CURL_DISABLE_HTTP */ @@ -4352,7 +4518,6 @@ static char *detect_proxy(struct connectdata *conn) * If this is supposed to use a proxy, we need to figure out the proxy * host name, so that we can re-use an existing connection * that may exist registered to the same proxy host. - * proxy will be freed before this function returns. */ static CURLcode parse_proxy(struct SessionHandle *data, struct connectdata *conn, char *proxy) @@ -4389,13 +4554,12 @@ static CURLcode parse_proxy(struct SessionHandle *data, /* Is there a username and password given in this proxy url? */ atsign = strchr(proxyptr, '@'); if(atsign) { - CURLcode res = CURLE_OK; char *proxyuser = NULL; char *proxypasswd = NULL; - - res = parse_login_details(proxyptr, atsign - proxyptr, - &proxyuser, &proxypasswd, NULL); - if(!res) { + CURLcode result = + parse_login_details(proxyptr, atsign - proxyptr, + &proxyuser, &proxypasswd, NULL); + if(!result) { /* found user and password, rip them out. note that we are unescaping them, as there is otherwise no way to have a username or password with reserved characters like ':' in @@ -4407,7 +4571,7 @@ static CURLcode parse_proxy(struct SessionHandle *data, conn->proxyuser = strdup(""); if(!conn->proxyuser) - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { Curl_safefree(conn->proxypasswd); if(proxypasswd && strlen(proxypasswd) < MAX_CURL_PASSWORD_LENGTH) @@ -4416,25 +4580,22 @@ static CURLcode parse_proxy(struct SessionHandle *data, conn->proxypasswd = strdup(""); if(!conn->proxypasswd) - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } - if(!res) { + if(!result) { conn->bits.proxy_user_passwd = TRUE; /* enable it */ atsign++; /* the right side of the @-letter */ - if(atsign) - proxyptr = atsign; /* now use this instead */ - else - res = CURLE_OUT_OF_MEMORY; + proxyptr = atsign; /* now use this instead */ } } - Curl_safefree(proxyuser); - Curl_safefree(proxypasswd); + free(proxyuser); + free(proxypasswd); - if(res) - return res; + if(result) + return result; } /* start scanning for port number at this point */ @@ -4595,7 +4756,7 @@ static CURLcode parse_url_login(struct SessionHandle *data, /* We could use the login information in the URL so extract it */ result = parse_login_details(login, ptr - login - 1, &userp, &passwdp, &optionsp); - if(result != CURLE_OK) + if(result) goto out; if(userp) { @@ -4643,9 +4804,9 @@ static CURLcode parse_url_login(struct SessionHandle *data, out: - Curl_safefree(userp); - Curl_safefree(passwdp); - Curl_safefree(optionsp); + free(userp); + free(passwdp); + free(optionsp); return result; } @@ -4733,7 +4894,7 @@ static CURLcode parse_login_details(const char *login, const size_t len, if(!result && passwdp && plen) { pbuf = malloc(plen + 1); if(!pbuf) { - Curl_safefree(ubuf); + free(ubuf); result = CURLE_OUT_OF_MEMORY; } } @@ -4742,8 +4903,8 @@ static CURLcode parse_login_details(const char *login, const size_t len, if(!result && optionsp && olen) { obuf = malloc(olen + 1); if(!obuf) { - Curl_safefree(pbuf); - Curl_safefree(ubuf); + free(pbuf); + free(ubuf); result = CURLE_OUT_OF_MEMORY; } } @@ -5022,6 +5183,32 @@ static CURLcode resolve_server(struct SessionHandle *data, /* set a pointer to the hostname we display */ fix_hostname(data, conn, &conn->host); +#ifdef USE_UNIX_SOCKETS + if(data->set.str[STRING_UNIX_SOCKET_PATH]) { + /* Unix domain sockets are local. The host gets ignored, just use the + * specified domain socket address. Do not cache "DNS entries". There is + * no DNS involved and we already have the filesystem path available */ + const char *path = data->set.str[STRING_UNIX_SOCKET_PATH]; + + hostaddr = calloc(1, sizeof(struct Curl_dns_entry)); + if(!hostaddr) + result = CURLE_OUT_OF_MEMORY; + else if((hostaddr->addr = Curl_unix2addr(path)) != NULL) + hostaddr->inuse++; + else { + /* Long paths are not supported for now */ + if(strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) { + failf(data, "Unix socket path too long: '%s'", path); + result = CURLE_COULDNT_RESOLVE_HOST; + } + else + result = CURLE_OUT_OF_MEMORY; + free(hostaddr); + hostaddr = NULL; + } + } + else +#endif if(!conn->proxy.name || !*conn->proxy.name) { /* If not connecting via a proxy, extract the port from the URL, if it is * there, thus overriding any defaults that might have been set above. */ @@ -5079,8 +5266,7 @@ static CURLcode resolve_server(struct SessionHandle *data, static void reuse_conn(struct connectdata *old_conn, struct connectdata *conn) { - if(old_conn->proxy.rawalloc) - free(old_conn->proxy.rawalloc); + free(old_conn->proxy.rawalloc); /* free the SSL config struct from this connection struct as this was allocated in vain and is targeted for destruction */ @@ -5168,8 +5354,9 @@ static CURLcode create_conn(struct SessionHandle *data, bool reuse; char *proxy = NULL; bool prot_missing = FALSE; - bool no_connections_available = FALSE; + bool connections_available = TRUE; bool force_reuse = FALSE; + bool waitpipe = FALSE; size_t max_host_connections = Curl_multi_max_host_connections(data->multi); size_t max_total_connections = Curl_multi_max_total_connections(data->multi); @@ -5250,7 +5437,7 @@ static CURLcode create_conn(struct SessionHandle *data, result = parseurlandfillconn(data, conn, &prot_missing, &user, &passwd, &options); - if(result != CURLE_OK) + if(result) goto out; /************************************************************* @@ -5310,7 +5497,7 @@ static CURLcode create_conn(struct SessionHandle *data, *************************************************************/ if(conn->bits.proxy_user_passwd) { result = parse_proxy_auth(data, conn); - if(result != CURLE_OK) + if(result) goto out; } @@ -5329,14 +5516,19 @@ static CURLcode create_conn(struct SessionHandle *data, if(data->set.str[STRING_NOPROXY] && check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) { - if(proxy) { - free(proxy); /* proxy is in exception list */ - proxy = NULL; - } + free(proxy); /* proxy is in exception list */ + proxy = NULL; } else if(!proxy) proxy = detect_proxy(conn); +#ifdef USE_UNIX_SOCKETS + if(proxy && data->set.str[STRING_UNIX_SOCKET_PATH]) { + free(proxy); /* Unix domain sockets cannot be proxied, so disable it */ + proxy = NULL; + } +#endif + if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { free(proxy); /* Don't bother with an empty proxy string or if the protocol doesn't work with network */ @@ -5351,7 +5543,8 @@ static CURLcode create_conn(struct SessionHandle *data, if(proxy) { result = parse_proxy(data, conn, proxy); - Curl_safefree(proxy); /* parse_proxy copies the proxy string */ + free(proxy); /* parse_proxy copies the proxy string */ + proxy = NULL; if(result) goto out; @@ -5372,8 +5565,10 @@ static CURLcode create_conn(struct SessionHandle *data, conn->bits.httpproxy = TRUE; #endif } - else + else { conn->bits.httpproxy = FALSE; /* not a HTTP proxy */ + conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ + } conn->bits.proxy = TRUE; } else { @@ -5397,16 +5592,16 @@ static CURLcode create_conn(struct SessionHandle *data, * Figure out the remote port number and fix it in the URL *************************************************************/ result = parse_remote_port(data, conn); - if(result != CURLE_OK) + if(result) goto out; /* Check for overridden login details and set them accordingly so they they are known when protocol->setup_connection is called! */ result = override_login(data, conn, &user, &passwd, &options); - if(result != CURLE_OK) + if(result) goto out; result = set_login(conn, user, passwd, options); - if(result != CURLE_OK) + if(result) goto out; /************************************************************* @@ -5414,7 +5609,7 @@ static CURLcode create_conn(struct SessionHandle *data, * we figured out what/if proxy to use. *************************************************************/ result = setup_connection_internals(conn); - if(result != CURLE_OK) + if(result) goto out; conn->recv[FIRSTSOCKET] = Curl_recv_plain; @@ -5434,11 +5629,11 @@ static CURLcode create_conn(struct SessionHandle *data, result = conn->handler->connect_it(conn, &done); /* Setup a "faked" transfer that'll do nothing */ - if(CURLE_OK == result) { + if(!result) { conn->data = data; conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ - ConnectionStore(data, conn); + Curl_conncache_add_conn(data->state.conn_cache, conn); /* * Setup whatever necessary for a resumed transfer @@ -5456,7 +5651,7 @@ static CURLcode create_conn(struct SessionHandle *data, } /* since we skip do_init() */ - do_init(conn); + Curl_init_do(data, conn); goto out; } @@ -5503,7 +5698,7 @@ static CURLcode create_conn(struct SessionHandle *data, if(data->set.reuse_fresh && !data->state.this_is_a_follow) reuse = FALSE; else - reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse); + reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); /* If we found a reusable connection, we may still want to open a new connection if we are pipelining. */ @@ -5540,18 +5735,24 @@ static CURLcode create_conn(struct SessionHandle *data, /* set a pointer to the hostname we display */ fix_hostname(data, conn, &conn->host); - infof(data, "Re-using existing connection! (#%ld) with host %s\n", + infof(data, "Re-using existing connection! (#%ld) with %s %s\n", conn->connection_id, + conn->bits.proxy?"proxy":"host", conn->proxy.name?conn->proxy.dispname:conn->host.dispname); } else { /* We have decided that we want a new connection. However, we may not be able to do that if we have reached the limit of how many connections we are allowed to open. */ - struct connectbundle *bundle; + struct connectbundle *bundle = NULL; + + if(waitpipe) + /* There is a connection that *might* become usable for pipelining + "soon", and we wait for that */ + connections_available = FALSE; + else + bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); - bundle = Curl_conncache_find_bundle(data->state.conn_cache, - conn->host.name); if(max_host_connections > 0 && bundle && (bundle->num_connections >= max_host_connections)) { struct connectdata *conn_candidate; @@ -5564,11 +5765,15 @@ static CURLcode create_conn(struct SessionHandle *data, conn_candidate->data = data; (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); } - else - no_connections_available = TRUE; + else { + infof(data, "No more connections allowed to host: %d\n", + max_host_connections); + connections_available = FALSE; + } } - if(max_total_connections > 0 && + if(connections_available && + (max_total_connections > 0) && (data->state.conn_cache->num_connections >= max_total_connections)) { struct connectdata *conn_candidate; @@ -5580,12 +5785,13 @@ static CURLcode create_conn(struct SessionHandle *data, conn_candidate->data = data; (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); } - else - no_connections_available = TRUE; + else { + infof(data, "No connections available in cache\n"); + connections_available = FALSE; + } } - - if(no_connections_available) { + if(!connections_available) { infof(data, "No connections available.\n"); conn_free(conn); @@ -5599,9 +5805,10 @@ static CURLcode create_conn(struct SessionHandle *data, * This is a brand new connection, so let's store it in the connection * cache of ours! */ - ConnectionStore(data, conn); + Curl_conncache_add_conn(data->state.conn_cache, conn); } +#if defined(USE_NTLM) /* If NTLM is requested in a part of this connection, make sure we don't assume the state is fine as this is a fresh connection and NTLM is connection based. */ @@ -5610,19 +5817,20 @@ static CURLcode create_conn(struct SessionHandle *data, infof(data, "NTLM picked AND auth done set, clear picked!\n"); data->state.authhost.picked = CURLAUTH_NONE; } + 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!\n"); data->state.authproxy.picked = CURLAUTH_NONE; } - +#endif } /* Mark the connection as used */ conn->inuse = TRUE; /* Setup and init stuff before DO starts, in preparing for the transfer. */ - do_init(conn); + Curl_init_do(data, conn); /* * Setup whatever necessary for a resumed transfer @@ -5637,8 +5845,6 @@ static CURLcode create_conn(struct SessionHandle *data, * Inherit the proper values from the urldata struct AFTER we have arranged * the persistent connection stuff */ - conn->fread_func = data->set.fread_func; - conn->fread_in = data->set.in; conn->seek_func = data->set.seek_func; conn->seek_client = data->set.seek_client; @@ -5649,10 +5855,10 @@ static CURLcode create_conn(struct SessionHandle *data, out: - Curl_safefree(options); - Curl_safefree(passwd); - Curl_safefree(user); - Curl_safefree(proxy); + free(options); + free(passwd); + free(user); + free(proxy); return result; } @@ -5747,14 +5953,14 @@ CURLcode Curl_connect(struct SessionHandle *data, bool *asyncp, bool *protocol_done) { - CURLcode code; + CURLcode result; *asyncp = FALSE; /* assume synchronous resolves by default */ /* call the stuff that needs to be called */ - code = create_conn(data, in_connect, asyncp); + result = create_conn(data, in_connect, asyncp); - if(CURLE_OK == code) { + if(!result) { /* no error */ if((*in_connect)->send_pipe->size || (*in_connect)->recv_pipe->size) /* pipelining */ @@ -5763,23 +5969,23 @@ CURLcode Curl_connect(struct SessionHandle *data, /* DNS resolution is done: that's either because this is a reused connection, in which case DNS was unnecessary, or because DNS really did finish already (synch resolver/fast async resolve) */ - code = Curl_setup_conn(*in_connect, protocol_done); + result = Curl_setup_conn(*in_connect, protocol_done); } } - if(code == CURLE_NO_CONNECTION_AVAILABLE) { + if(result == CURLE_NO_CONNECTION_AVAILABLE) { *in_connect = NULL; - return code; + return result; } - if(code && *in_connect) { + if(result && *in_connect) { /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_disconnect(*in_connect, FALSE); /* close the connection */ *in_connect = NULL; /* return a NULL */ } - return code; + return result; } CURLcode Curl_done(struct connectdata **connp, @@ -5796,37 +6002,19 @@ CURLcode Curl_done(struct connectdata **connp, conn = *connp; data = conn->data; - if(conn->bits.done) + DEBUGF(infof(data, "Curl_done\n")); + + if(data->state.done) /* Stop if Curl_done() has already been called */ return CURLE_OK; Curl_getoff_all_pipelines(data, conn); - if((conn->send_pipe->size + conn->recv_pipe->size != 0 && - !data->set.reuse_forbid && - !conn->bits.close)) - /* Stop if pipeline is not empty and we do not have to close - connection. */ - return CURLE_OK; - - conn->bits.done = TRUE; /* called just now! */ - /* Cleanup possible redirect junk */ - if(data->req.newurl) { - free(data->req.newurl); - data->req.newurl = NULL; - } - if(data->req.location) { - free(data->req.location); - data->req.location = NULL; - } - - Curl_resolver_cancel(conn); - - if(conn->dns_entry) { - Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ - conn->dns_entry = NULL; - } + free(data->req.newurl); + data->req.newurl = NULL; + free(data->req.location); + data->req.location = NULL; switch(status) { case CURLE_ABORTED_BY_CALLBACK: @@ -5850,12 +6038,27 @@ CURLcode Curl_done(struct connectdata **connp, if(!result && Curl_pgrsDone(conn)) result = CURLE_ABORTED_BY_CALLBACK; + if((conn->send_pipe->size + conn->recv_pipe->size != 0 && + !data->set.reuse_forbid && + !conn->bits.close)) { + /* Stop if pipeline is not empty and we do not have to close + connection. */ + DEBUGF(infof(data, "Connection still in use, no more Curl_done now!\n")); + return CURLE_OK; + } + + data->state.done = TRUE; /* called just now! */ + Curl_resolver_cancel(conn); + + if(conn->dns_entry) { + Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ + conn->dns_entry = NULL; + } + /* if the transfer was completed in a paused state there can be buffered data left to write and then kill */ - if(data->state.tempwrite) { - free(data->state.tempwrite); - data->state.tempwrite = NULL; - } + free(data->state.tempwrite); + data->state.tempwrite = NULL; /* if data->set.reuse_forbid is TRUE, it means the libcurl client has forced us to close this connection. This is ignored for requests taking @@ -5872,9 +6075,12 @@ CURLcode Curl_done(struct connectdata **connp, but currently we have no such detail knowledge. */ - if((data->set.reuse_forbid && !(conn->ntlm.state == NTLMSTATE_TYPE2 || - conn->proxyntlm.state == NTLMSTATE_TYPE2)) - || conn->bits.close || premature) { + if((data->set.reuse_forbid +#if defined(USE_NTLM) + && !(conn->ntlm.state == NTLMSTATE_TYPE2 || + conn->proxyntlm.state == NTLMSTATE_TYPE2) +#endif + ) || conn->bits.close || premature) { CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */ /* If we had an error already, make sure we return that one. But @@ -5906,20 +6112,24 @@ CURLcode Curl_done(struct connectdata **connp, } /* - * do_init() inits the readwrite session. This is inited each time (in the DO - * function before the protocol-specific DO functions are invoked) for a - * transfer, sometimes multiple times on the same SessionHandle. Make sure + * Curl_init_do() inits the readwrite session. This is inited each time (in + * the DO function before the protocol-specific DO functions are invoked) for + * a transfer, sometimes multiple times on the same SessionHandle. Make sure * nothing in here depends on stuff that are setup dynamically for the * transfer. + * + * Allow this function to get called with 'conn' set to NULL. */ -static CURLcode do_init(struct connectdata *conn) +CURLcode Curl_init_do(struct SessionHandle *data, struct connectdata *conn) { - struct SessionHandle *data = conn->data; struct SingleRequest *k = &data->req; - conn->bits.done = FALSE; /* Curl_done() is not called yet */ - conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ + if(conn) + conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to + * use */ + + data->state.done = FALSE; /* Curl_done() is not called yet */ data->state.expect100header = FALSE; if(data->set.opt_no_body) @@ -5986,7 +6196,7 @@ CURLcode Curl_do(struct connectdata **connp, bool *done) if(!data->multi) { result = Curl_reconnect_request(connp); - if(result == CURLE_OK) { + if(!result) { /* ... finally back to actually retry the DO phase */ conn = *connp; /* re-assign conn since Curl_reconnect_request creates a new connection */ @@ -5997,7 +6207,7 @@ CURLcode Curl_do(struct connectdata **connp, bool *done) return result; } - if((result == CURLE_OK) && *done) + if(!result && *done) /* do_complete must be called after the protocol-specific DO function */ do_complete(conn); } @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,6 +27,7 @@ * Prototypes for library-wide functions provided by url.c */ +CURLcode Curl_init_do(struct SessionHandle *data, struct connectdata *conn); CURLcode Curl_open(struct SessionHandle **curl); CURLcode Curl_init_userdefined(struct UserDefined *set); CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, @@ -69,6 +70,9 @@ void Curl_close_connections(struct SessionHandle *data); #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE "rcmd" /* default socks5 gssapi service */ +#define CURL_DEFAULT_PROXY_SERVICE_NAME "HTTP" /* default negotiate proxy + service */ +#define CURL_DEFAULT_SERVICE_NAME "HTTP" /* default negotiate service */ CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex); diff --git a/lib/urldata.h b/lib/urldata.h index 8594c2f..b1c2056 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,6 +40,8 @@ #define PORT_IMAPS 993 #define PORT_POP3 110 #define PORT_POP3S 995 +#define PORT_SMB 445 +#define PORT_SMBS 445 #define PORT_SMTP 25 #define PORT_SMTPS 465 /* sometimes called SSMTP */ #define PORT_RTSP 554 @@ -64,6 +66,7 @@ #define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS) #define PROTO_FAMILY_FTP (CURLPROTO_FTP|CURLPROTO_FTPS) #define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S) +#define PROTO_FAMILY_SMB (CURLPROTO_SMB|CURLPROTO_SMBS) #define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS) #define DEFAULT_CONNCACHE_SIZE 5 @@ -79,38 +82,12 @@ #include "cookie.h" #include "formdata.h" -#ifdef USE_SSLEAY #ifdef USE_OPENSSL -#include <openssl/rsa.h> -#include <openssl/crypto.h> -#include <openssl/x509.h> -#include <openssl/pem.h> #include <openssl/ssl.h> -#include <openssl/err.h> #ifdef HAVE_OPENSSL_ENGINE_H #include <openssl/engine.h> #endif -#ifdef HAVE_OPENSSL_PKCS12_H -#include <openssl/pkcs12.h> -#endif -#else /* SSLeay-style includes */ -#include <rsa.h> -#include <crypto.h> -#include <x509.h> -#include <pem.h> -#include <ssl.h> -#include <err.h> -#ifdef HAVE_OPENSSL_ENGINE_H -#include <engine.h> -#endif -#ifdef HAVE_OPENSSL_PKCS12_H -#include <pkcs12.h> -#endif #endif /* USE_OPENSSL */ -#ifdef USE_GNUTLS -#error Configuration error; cannot use GnuTLS *and* OpenSSL. -#endif -#endif /* USE_SSLEAY */ #ifdef USE_GNUTLS #include <gnutls/gnutls.h> @@ -138,15 +115,12 @@ #include <pk11pub.h> #endif -#ifdef USE_QSOSSL -#include <qsossl.h> -#endif - #ifdef USE_GSKIT #include <gskssl.h> #endif #ifdef USE_AXTLS +#include <axTLS/config.h> #include <axTLS/ssl.h> #undef malloc #undef calloc @@ -195,6 +169,7 @@ #include "ssh.h" #include "http.h" #include "rtsp.h" +#include "smb.h" #include "wildcard.h" #include "multihandle.h" @@ -223,6 +198,8 @@ #define HEADERSIZE 256 #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU +#define GOOD_EASY_HANDLE(x) \ + ((x) && (((struct SessionHandle *)(x))->magic == CURLEASY_MAGIC_NUMBER)) /* Some convenience macros to get the larger/smaller value out of two given. We prefix with CURL to prevent name collisions. */ @@ -288,13 +265,13 @@ struct ssl_connect_data { current state of the connection. */ bool use; ssl_connection_state state; -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL /* these ones requires specific SSL-types */ SSL_CTX* ctx; SSL* handle; X509* server_cert; ssl_connect_state connecting_state; -#endif /* USE_SSLEAY */ +#endif /* USE_OPENSSL */ #ifdef USE_GNUTLS gnutls_session_t session; gnutls_certificate_credentials_t cred; @@ -328,9 +305,6 @@ struct ssl_connect_data { PK11GenericObject *obj_clicert; ssl_connect_state connecting_state; #endif /* USE_NSS */ -#ifdef USE_QSOSSL - SSLHandle *handle; -#endif /* USE_QSOSSL */ #ifdef USE_GSKIT gsk_handle handle; int iocport; @@ -350,6 +324,9 @@ struct ssl_connect_data { size_t encdata_offset, decdata_offset; unsigned char *encdata_buffer, *decdata_buffer; unsigned long req_flags, ret_flags; + CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ + bool recv_sspi_close_notify; /* true if connection closed by close_notify */ + bool recv_connection_closed; /* true if connection closed, regardless how */ #endif /* USE_SCHANNEL */ #ifdef USE_DARWINSSL SSLContextRef ssl_ctx; @@ -366,6 +343,7 @@ struct ssl_config_data { bool verifypeer; /* set TRUE if this is desired */ bool verifyhost; /* set TRUE if CN/SAN must match hostname */ + bool verifystatus; /* set TRUE if certificate status must be checked */ char *CApath; /* certificate dir (doesn't work on windows) */ char *CAfile; /* certificate to verify peer against */ const char *CRLfile; /* CRL to check certificate revocation */ @@ -378,6 +356,7 @@ struct ssl_config_data { void *fsslctxp; /* parameter for call back */ bool sessionid; /* cache session IDs or not */ bool certinfo; /* gather lots of certificate info */ + bool falsestart; #ifdef USE_TLS_SRP char *username; /* TLS username (for, e.g., SRP) */ @@ -398,6 +377,10 @@ struct curl_ssl_session { /* Struct used for Digest challenge-response authentication */ struct digestdata { +#if defined(USE_WINDOWS_SSPI) + BYTE *input_token; + size_t input_token_len; +#else char *nonce; char *cnonce; char *realm; @@ -407,6 +390,7 @@ struct digestdata { char *qop; char *algorithm; int nc; /* nounce count */ +#endif }; typedef enum { @@ -426,8 +410,9 @@ typedef enum { #endif /* Struct used for GSSAPI (Kerberos V5) authentication */ -#if defined(USE_WINDOWS_SSPI) +#if defined(USE_KERBEROS5) struct kerberos5data { +#if defined(USE_WINDOWS_SSPI) CredHandle *credentials; CtxtHandle *context; TCHAR *spn; @@ -435,22 +420,26 @@ struct kerberos5data { SEC_WINNT_AUTH_IDENTITY *p_identity; size_t token_max; BYTE *output_token; +#else + gss_ctx_id_t context; + gss_name_t spn; +#endif }; #endif /* Struct used for NTLM challenge-response authentication */ +#if defined(USE_NTLM) struct ntlmdata { curlntlm state; #ifdef USE_WINDOWS_SSPI - CredHandle handle; - CtxtHandle c_handle; + CredHandle *credentials; + CtxtHandle *context; SEC_WINNT_AUTH_IDENTITY identity; SEC_WINNT_AUTH_IDENTITY *p_identity; - size_t max_token_length; + size_t token_max; BYTE *output_token; - int has_handles; - void *type_2; - unsigned long n_type_2; + BYTE *input_token; + size_t input_token_len; #else unsigned int flags; unsigned char nonce[8]; @@ -458,6 +447,7 @@ struct ntlmdata { unsigned int target_info_len; #endif }; +#endif #ifdef USE_SPNEGO struct negotiatedata { @@ -472,12 +462,12 @@ struct negotiatedata { #else #ifdef USE_WINDOWS_SSPI DWORD status; - CtxtHandle *context; CredHandle *credentials; + CtxtHandle *context; SEC_WINNT_AUTH_IDENTITY identity; SEC_WINNT_AUTH_IDENTITY *p_identity; TCHAR *server_name; - size_t max_token_length; + size_t token_max; BYTE *output_token; size_t output_token_length; #endif @@ -531,11 +521,6 @@ struct ConnectBits { requests */ bool netrc; /* name+password provided by netrc */ bool userpwd_in_url; /* name+password found in url */ - - bool done; /* set to FALSE when Curl_do() is called and set to TRUE - when Curl_done() is called, to prevent Curl_done() to - get invoked twice when the multi interface is - used. */ bool stream_was_rewound; /* Indicates that the stream was rewound after a request read past the end of its response byte boundary */ @@ -545,6 +530,7 @@ struct ConnectBits { bool bound; /* set true if bind() has already been done on this socket/ connection */ bool type_set; /* type= was used in the URL */ + bool multiplex; /* connection is multiplexed */ }; struct hostname { @@ -617,12 +603,6 @@ enum upgrade101 { UPGR101_WORKING /* talking upgraded protocol */ }; -enum negotiatenpn { - NPN_INIT, /* default state */ - NPN_HTTP1_1, /* HTTP/1.1 negotiated */ - NPN_HTTP2 /* HTTP2 (draft-xx) negotiated */ -}; - /* * Request specific data in the easy handle (SessionHandle). Previously, * these members were on the connectdata struct but since a conn struct may @@ -680,7 +660,6 @@ struct SingleRequest { #define IDENTITY 0 /* No encoding */ #define DEFLATE 1 /* zlib deflate [RFC 1950 & 1951] */ #define GZIP 2 /* gzip algorithm [RFC 1952] */ -#define COMPRESS 3 /* Not handled, added for completeness */ #ifdef HAVE_LIBZ zlibInitState zlib_init; /* possible zlib init state; @@ -883,7 +862,7 @@ struct connectdata { the ip_addr itself. */ char ip_addr_str[MAX_IPADR_LEN]; - unsigned int scope; /* address scope for IPv6 */ + unsigned int scope_id; /* Scope id for IPv6 */ int socktype; /* SOCK_STREAM or SOCK_DGRAM */ @@ -974,8 +953,8 @@ struct connectdata { char *te; /* TE: request header */ } allocptr; - int sec_complete; /* if kerberos is enabled for this connection */ #ifdef HAVE_GSSAPI + int sec_complete; /* if Kerberos is enabled for this connection */ enum protection_level command_prot; enum protection_level data_prot; enum protection_level request_data_prot; @@ -986,7 +965,7 @@ struct connectdata { struct sockaddr_in local_addr; #endif -#if defined(USE_WINDOWS_SSPI) /* Consider moving some of the above GSS-API */ +#if defined(USE_KERBEROS5) /* Consider moving some of the above GSS-API */ struct kerberos5data krb5; /* variables into the structure definition, */ #endif /* however, some of them are ftp specific. */ @@ -1013,22 +992,20 @@ struct connectdata { /*************** Request - specific items ************/ - /* previously this was in the urldata struct */ - curl_read_callback fread_func; /* function that reads the input */ - void *fread_in; /* pointer to pass to the fread() above */ - +#if defined(USE_NTLM) struct ntlmdata ntlm; /* NTLM differs from other authentication schemes because it authenticates connections, not single requests! */ struct ntlmdata proxyntlm; /* NTLM data for proxy */ -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if defined(NTLM_WB_ENABLED) /* used for communication with Samba's winbind daemon helper ntlm_auth */ curl_socket_t ntlm_auth_hlpr_socket; pid_t ntlm_auth_hlpr_pid; char* challenge_header; char* response_header; #endif +#endif char syserr_buf [256]; /* buffer for Curl_strerror() */ @@ -1051,6 +1028,7 @@ struct connectdata { struct pop3_conn pop3c; struct smtp_conn smtpc; struct rtsp_conn rtspc; + struct smb_conn smbc; void *generic; /* RTMP and LDAP use this */ } proto; @@ -1081,7 +1059,7 @@ struct connectdata { } tunnel_state[2]; /* two separate ones to allow FTP */ struct connectbundle *bundle; /* The bundle we are member of */ - enum negotiatenpn negnpn; + int negnpn; /* APLN or NPN TLS negotiated protocol, CURL_HTTP_VERSION* */ }; /* The end of connectdata. */ @@ -1279,9 +1257,9 @@ struct UrlState { void *resolver; /* resolver state, if it is used in the URL state - ares_channel f.e. */ -#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_ENGINE_H) ENGINE *engine; -#endif /* USE_SSLEAY */ +#endif /* USE_OPENSSL */ struct timeval expiretime; /* set this with Curl_expire() only */ struct Curl_tree timenode; /* for the splay stuff */ struct curl_llist *timeoutlist; /* list of pending timeouts */ @@ -1325,10 +1303,15 @@ struct UrlState { long rtsp_next_server_CSeq; /* the session's next server CSeq */ long rtsp_CSeq_recv; /* most recent CSeq received */ - /* if true, force SSL connection retry (workaround for certain servers) */ - bool ssl_connect_retry; curl_off_t infilesize; /* size of file to upload, -1 means unknown. Copied from set.filesize at start of operation */ + + int drain; /* Increased when this stream has data to read, even if its + socket not necessarily is readable. Decreased when + checked. */ + bool done; /* set to FALSE when Curl_do() is called and set to TRUE when + Curl_done() is called, to prevent Curl_done() to get invoked + twice when the multi interface is used. */ }; @@ -1378,13 +1361,13 @@ enum dupstring { STRING_KRB_LEVEL, /* krb security level */ STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find $HOME/.netrc */ - STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ STRING_PROXY, /* proxy to use */ STRING_SET_RANGE, /* range, if used */ STRING_SET_REFERER, /* custom string for the HTTP referer field */ STRING_SET_URL, /* what original URL to work on */ STRING_SSL_CAPATH, /* CA directory name (doesn't work on windows) */ STRING_SSL_CAFILE, /* certificate file to verify peer against */ + STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ STRING_SSL_EGDSOCKET, /* path to file containing the EGD daemon socket */ STRING_SSL_RANDOM_FILE, /* path to file containing "random" data */ @@ -1408,19 +1391,31 @@ enum dupstring { STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */ #endif #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - STRING_SOCKS5_GSSAPI_SERVICE, /* GSSAPI service name */ + STRING_SOCKS5_GSSAPI_SERVICE, /* GSSAPI service name */ + STRING_PROXY_SERVICE_NAME, /* Proxy service name */ + STRING_SERVICE_NAME, /* Service name */ #endif STRING_MAIL_FROM, STRING_MAIL_AUTH, #ifdef USE_TLS_SRP - STRING_TLSAUTH_USERNAME, /* TLS auth <username> */ - STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */ + STRING_TLSAUTH_USERNAME, /* TLS auth <username> */ + STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */ #endif + STRING_BEARER, /* <bearer>, if used */ +#ifdef USE_UNIX_SOCKETS + STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ +#endif + + /* -- end of zero-terminated strings -- */ - STRING_BEARER, /* <bearer>, if used */ + STRING_LASTZEROTERMINATED, + + /* -- below this are pointers to binary data that cannot be strdup'ed. + Each such pointer must be added manually to Curl_dupset() --- */ + + STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ - /* -- end of strings -- */ STRING_LAST /* not used, just an end-of-list marker */ }; @@ -1431,8 +1426,8 @@ struct UserDefined { long proxyport; /* If non-zero, use this port number by default. If the proxy string features a ":[port]" that one will override this. */ - void *out; /* the fetched file goes here */ - void *in; /* the uploaded file is read from here */ + void *out; /* CURLOPT_WRITEDATA */ + void *in; /* CURLOPT_READDATA */ void *writeheader; /* write the header to this if non-NULL */ void *rtp_out; /* write RTP to this if non-NULL */ long use_port; /* which port to use (when not using default) */ @@ -1553,7 +1548,7 @@ struct UserDefined { bool ftp_list_only; /* switch FTP command for listing directories */ bool ftp_use_port; /* use the FTP PORT command */ bool hide_progress; /* don't use the progress meter */ - bool http_fail_on_error; /* fail on HTTP error codes >= 300 */ + bool http_fail_on_error; /* fail on HTTP error codes >= 400 */ bool http_follow_location; /* follow HTTP redirects */ bool http_transfer_encoding; /* request compressed HTTP transfer-encoding */ bool http_disable_hostname_check_before_authentication; @@ -1566,7 +1561,7 @@ struct UserDefined { enum CURL_NETRC_OPTION use_netrc; /* defined in include/curl.h */ bool verbose; /* output verbosity */ - bool krb; /* kerberos connection requested */ + bool krb; /* Kerberos connection requested */ bool reuse_forbid; /* forbidden to be reused, close after use */ bool reuse_fresh; /* do not re-use an existing connection */ bool ftp_use_epsv; /* if EPSV is to be attempted or not */ @@ -1586,6 +1581,7 @@ struct UserDefined { bool connect_only; /* make connection, let application use the socket */ bool ssl_enable_beast; /* especially allow this flaw for interoperability's sake*/ + bool ssl_no_revoke; /* disable SSL certificate revocation checks */ long ssh_auth_types; /* allowed SSH auth types */ bool http_te_skip; /* pass the raw body data to the user, even when transfer-encoded (chunked, compressed) */ @@ -1596,7 +1592,7 @@ struct UserDefined { bool proxy_transfer_mode; /* set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ - unsigned int scope; /* address scope for IPv6 */ + unsigned int scope_id; /* Scope id for IPv6 */ long allowed_protocols; long redir_protocols; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -1627,7 +1623,9 @@ struct UserDefined { bool ssl_enable_npn; /* TLS NPN extension? */ bool ssl_enable_alpn; /* TLS ALPN extension? */ - + bool path_as_is; /* allow dotdots? */ + bool pipewait; /* wait for pipe/multiplex status before starting a + new connection */ long expect_100_timeout; /* in milliseconds */ }; diff --git a/lib/version.c b/lib/version.c index 788f3e9..1727c5a 100644 --- a/lib/version.c +++ b/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,9 +26,7 @@ #include "urldata.h" #include "vtls/vtls.h" #include "http2.h" - -#define _MPRINTF_REPLACE /* use the internal *printf() functions */ -#include <curl/mprintf.h> +#include "curl_printf.h" #ifdef USE_ARES # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ @@ -216,6 +214,14 @@ static const char * const protocols[] = { #ifdef USE_LIBSSH2 "sftp", #endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \ + (CURL_SIZEOF_CURL_OFF_T > 4) && \ + (!defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO)) + "smb", +# ifdef USE_SSL + "smbs", +# endif +#endif #ifndef CURL_DISABLE_SMTP "smtp", #endif @@ -247,12 +253,16 @@ static curl_version_info_data version_info = { #ifdef USE_NTLM | CURL_VERSION_NTLM #endif -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) | CURL_VERSION_NTLM_WB #endif #ifdef USE_SPNEGO | CURL_VERSION_SPNEGO #endif +#ifdef USE_KERBEROS5 + | CURL_VERSION_KERBEROS5 +#endif #ifdef HAVE_GSSAPI | CURL_VERSION_GSSAPI #endif @@ -284,6 +294,9 @@ static curl_version_info_data version_info = { #if defined(USE_NGHTTP2) | CURL_VERSION_HTTP2 #endif +#if defined(USE_UNIX_SOCKETS) + | CURL_VERSION_UNIX_SOCKETS +#endif , NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ diff --git a/lib/vtls/axtls.c b/lib/vtls/axtls.c index 1b577b1..1038432 100644 --- a/lib/vtls/axtls.c +++ b/lib/vtls/axtls.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010, DirecTV, Contact: Eric Hu, <ehu@directv.com>. - * Copyright (C) 2010 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2010 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,6 +29,7 @@ #include "curl_setup.h" #ifdef USE_AXTLS +#include <axTLS/config.h> #include <axTLS/ssl.h> #include "axtls.h" @@ -38,13 +39,13 @@ #include "parsedate.h" #include "connect.h" /* for the connect timeout */ #include "select.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> -#include "curl_memory.h" +#include "curl_printf.h" +#include "hostcheck.h" #include <unistd.h> -/* The last #include file should be: */ + +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -#include "hostcheck.h" /* Global axTLS init, called from Curl_ssl_init() */ @@ -463,9 +464,11 @@ Curl_axtls_connect(struct connectdata *conn, int sockindex) { + struct SessionHandle *data = conn->data; CURLcode conn_step = connect_prep(conn, sockindex); int ssl_fcn_return; SSL *ssl = conn->ssl[sockindex].ssl; + long timeout_ms; if(conn_step != CURLE_OK) { Curl_axtls_close(conn, sockindex); @@ -474,14 +477,23 @@ Curl_axtls_connect(struct connectdata *conn, /* Check to make sure handshake was ok. */ while(ssl_handshake_status(ssl) != SSL_OK) { + /* 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; + } + ssl_fcn_return = ssl_read(ssl, NULL); if(ssl_fcn_return < 0) { Curl_axtls_close(conn, sockindex); ssl_display_error(ssl_fcn_return); /* goes to stdout. */ return map_error_to_curl(ssl_fcn_return); } + /* TODO: avoid polling */ usleep(10000); - /* TODO: check for timeout as this could hang indefinitely otherwise */ } infof (conn->data, "handshake completed successfully\n"); @@ -515,12 +527,6 @@ static ssize_t axtls_send(struct connectdata *conn, return rc; } -void Curl_axtls_close_all(struct SessionHandle *data) -{ - (void)data; - infof(data, " Curl_axtls_close_all\n"); -} - void Curl_axtls_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; @@ -677,7 +683,7 @@ int Curl_axtls_random(struct SessionHandle *data, * race condition is that some global resources will leak. */ RNG_initialize(); } - get_random(length, entropy); + get_random((int)length, entropy); return 0; } diff --git a/lib/vtls/axtls.h b/lib/vtls/axtls.h index 0459cf2..223ecb8 100644 --- a/lib/vtls/axtls.h +++ b/lib/vtls/axtls.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010, DirecTV - * contact: Eric Hu <ehu@directv.com> + * Copyright (C) 2010, DirecTV, Contact: Eric Hu <ehu@directv.com> + * Copyright (C) 2010 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,10 +35,6 @@ CURLcode Curl_axtls_connect_nonblocking( int sockindex, bool *done); -/* tell axTLS to close down all open information regarding connections (and - thus session ID caching etc) */ -void Curl_axtls_close_all(struct SessionHandle *data); - /* close a SSL connection */ void Curl_axtls_close(struct connectdata *conn, int sockindex); @@ -50,23 +46,26 @@ int Curl_axtls_random(struct SessionHandle *data, unsigned char *entropy, size_t length); +/* Set the API backend definition to axTLS */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_AXTLS + /* API setup for axTLS */ #define curlssl_init Curl_axtls_init #define curlssl_cleanup Curl_axtls_cleanup #define curlssl_connect Curl_axtls_connect #define curlssl_connect_nonblocking Curl_axtls_connect_nonblocking #define curlssl_session_free(x) Curl_axtls_session_free(x) -#define curlssl_close_all Curl_axtls_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_axtls_close #define curlssl_shutdown(x,y) Curl_axtls_shutdown(x,y) -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_axtls_version #define curlssl_check_cxn(x) Curl_axtls_check_cxn(x) -#define curlssl_data_pending(x,y) (x=x, y=y, 0) +#define curlssl_data_pending(x,y) ((void)x, (void)y, 0) #define curlssl_random(x,y,z) Curl_axtls_random(x,y,z) -#define CURL_SSL_BACKEND CURLSSLBACKEND_AXTLS + #endif /* USE_AXTLS */ #endif /* HEADER_CURL_AXTLS_H */ diff --git a/lib/vtls/cyassl.c b/lib/vtls/cyassl.c index 9b5c7c6..3ded7f1 100644 --- a/lib/vtls/cyassl.c +++ b/lib/vtls/cyassl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,6 +30,20 @@ #ifdef USE_CYASSL +#define WOLFSSL_OPTIONS_IGNORE_SYS +/* CyaSSL's version.h, which should contain only the version, should come +before all other CyaSSL includes and be immediately followed by build config +aka options.h. http://curl.haxx.se/mail/lib-2015-04/0069.html */ +#include <cyassl/version.h> +#if defined(HAVE_CYASSL_OPTIONS_H) && (LIBCYASSL_VERSION_HEX > 0x03004008) +#if defined(CYASSL_API) || defined(WOLFSSL_API) +/* Safety measure. If either is defined some API include was already included +and that's a problem since options.h hasn't been included yet. */ +#error "CyaSSL API was included before the CyaSSL build options." +#endif +#include <cyassl/options.h> +#endif + #ifdef HAVE_LIMITS_H #include <limits.h> #endif @@ -43,10 +57,8 @@ #include "connect.h" /* for the connect timeout */ #include "select.h" #include "rawstr.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> -#include "curl_memory.h" +#include "x509asn1.h" +#include "curl_printf.h" #include <cyassl/ssl.h> #ifdef HAVE_CYASSL_ERROR_SSL_H @@ -55,10 +67,16 @@ #include <cyassl/error.h> #endif #include <cyassl/ctaocrypt/random.h> +#include <cyassl/ctaocrypt/sha256.h> -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" +#if LIBCYASSL_VERSION_HEX < 0x02007002 /* < 2.7.2 */ +#define CYASSL_MAX_ERROR_SZ 80 +#endif + static Curl_recv cyassl_recv; static Curl_send cyassl_send; @@ -82,46 +100,58 @@ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { + char error_buffer[CYASSL_MAX_ERROR_SZ]; struct SessionHandle *data = conn->data; struct ssl_connect_data* conssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = NULL; void* ssl_sessionid = NULL; curl_socket_t sockfd = conn->sock[sockindex]; +#ifdef HAVE_SNI + bool sni = FALSE; +#define use_sni(x) sni = (x) +#else +#define use_sni(x) Curl_nop_stmt +#endif if(conssl->state == ssl_connection_complete) return CURLE_OK; - /* CyaSSL doesn't support SSLv2 */ - if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { - failf(data, "CyaSSL does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - } - /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: - /* we try to figure out version */ - req_method = SSLv23_client_method(); - break; case CURL_SSLVERSION_TLSv1: - infof(data, "CyaSSL cannot be configured to use TLS 1.0-1.2, " +#if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ + /* minimum protocol version is set later after the CTX object is created */ + req_method = SSLv23_client_method(); +#else + infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); +#endif + use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_0: req_method = TLSv1_client_method(); + use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); + use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); + use_sni(TRUE); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); + use_sni(FALSE); break; + case CURL_SSLVERSION_SSLv2: + failf(data, "CyaSSL does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; default: - req_method = TLSv1_client_method(); + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { @@ -138,15 +168,36 @@ cyassl_connect_step1(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } + switch(data->set.ssl.version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: +#if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ + /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever + minimum version of TLS was built in and at least TLS 1.0. For later library + versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so + we have this short circuit evaluation to find the minimum supported TLS + version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion + because only the former will work before the user's CTX callback is called. + */ + if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) && + (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) && + (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) { + failf(data, "SSL: couldn't set the minimum protocol version"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif + break; + } + #ifndef NO_FILESYSTEM /* load trusted cacert */ if(data->set.str[STRING_SSL_CAFILE]) { - if(!SSL_CTX_load_verify_locations(conssl->ctx, - data->set.str[STRING_SSL_CAFILE], - data->set.str[STRING_SSL_CAPATH])) { + if(1 != SSL_CTX_load_verify_locations(conssl->ctx, + data->set.str[STRING_SSL_CAFILE], + data->set.str[STRING_SSL_CAPATH])) { if(data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ - failf(data,"error setting certificate verify locations:\n" + failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", data->set.str[STRING_SSL_CAFILE]? data->set.str[STRING_SSL_CAFILE]: "none", @@ -192,11 +243,7 @@ cyassl_connect_step1(struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } } -#else - if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) { - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* NO_FILESYSTEM */ +#endif /* !NO_FILESYSTEM */ /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue @@ -206,6 +253,46 @@ cyassl_connect_step1(struct connectdata *conn, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, NULL); +#ifdef HAVE_SNI + if(sni) { + struct in_addr addr4; +#ifdef ENABLE_IPV6 + struct in6_addr addr6; +#endif + size_t hostname_len = strlen(conn->host.name); + if((hostname_len < USHRT_MAX) && + (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) && +#ifdef ENABLE_IPV6 + (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) && +#endif + (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name, + (unsigned short)hostname_len) != 1)) { + infof(data, "WARNING: failed to configure server name indication (SNI) " + "TLS extension\n"); + } + } +#endif + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + CURLcode result = CURLE_OK; + result = (*data->set.ssl.fsslctx)(data, conssl->ctx, + data->set.ssl.fsslctxp); + if(result) { + failf(data, "error signaled by ssl ctx callback"); + return result; + } + } +#ifdef NO_FILESYSTEM + else if(data->set.ssl.verifypeer) { + failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built" + " with \"no filesystem\". Either disable peer verification" + " (insecure) or if you are building an application with libcurl you" + " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); + return CURLE_SSL_CONNECT_ERROR; + } +#endif + /* Let's make an SSL structure */ if(conssl->handle) SSL_free(conssl->handle); @@ -220,7 +307,7 @@ cyassl_connect_step1(struct connectdata *conn, /* we got a session id, use it! */ if(!SSL_set_session(conssl->handle, ssl_sessionid)) { failf(data, "SSL: SSL_set_session failed: %s", - ERR_error_string(SSL_get_error(conssl->handle, 0),NULL)); + ERR_error_string(SSL_get_error(conssl->handle, 0), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ @@ -246,9 +333,6 @@ cyassl_connect_step2(struct connectdata *conn, struct SessionHandle *data = conn->data; struct ssl_connect_data* conssl = &conn->ssl[sockindex]; - infof(data, "CyaSSL: Connecting to %s:%d\n", - conn->host.name, conn->remote_port); - conn->recv[sockindex] = cyassl_recv; conn->send[sockindex] = cyassl_send; @@ -261,7 +345,7 @@ cyassl_connect_step2(struct connectdata *conn, ret = SSL_connect(conssl->handle); if(ret != 1) { - char error_buffer[80]; + char error_buffer[CYASSL_MAX_ERROR_SZ]; int detail = SSL_get_error(conssl->handle, ret); if(SSL_ERROR_WANT_READ == detail) { @@ -321,6 +405,44 @@ cyassl_connect_step2(struct connectdata *conn, } } + if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) { + X509 *x509; + const char *x509_der; + int x509_der_len; + curl_X509certificate x509_parsed; + curl_asn1Element *pubkey; + CURLcode result; + + x509 = SSL_get_peer_certificate(conssl->handle); + if(!x509) { + failf(data, "SSL: failed retrieving server certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len); + if(!x509_der) { + failf(data, "SSL: failed retrieving ASN.1 server certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + memset(&x509_parsed, 0, sizeof x509_parsed); + Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len); + + pubkey = &x509_parsed.subjectPublicKeyInfo; + if(!pubkey->header || pubkey->end <= pubkey->header) { + failf(data, "SSL: failed retrieving public key from server certificate"); + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + } + + result = Curl_pin_peer_pubkey(data->set.str[STRING_SSL_PINNEDPUBLICKEY], + (const unsigned char *)pubkey->header, + (size_t)(pubkey->end - pubkey->header)); + if(result) { + failf(data, "SSL: public key does not match pinned public key!"); + return result; + } + } + conssl->connecting_state = ssl_connect_3; infof(data, "SSL connected\n"); @@ -332,11 +454,11 @@ static CURLcode cyassl_connect_step3(struct connectdata *conn, int sockindex) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; void *old_ssl_sessionid=NULL; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - int incache; + bool incache; SSL_SESSION *our_ssl_sessionid; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -351,18 +473,19 @@ cyassl_connect_step3(struct connectdata *conn, incache = FALSE; } } + if(!incache) { - retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, - 0 /* unknown size */); - if(retcode) { + result = Curl_ssl_addsessionid(conn, our_ssl_sessionid, + 0 /* unknown size */); + if(result) { failf(data, "failed to store ssl session"); - return retcode; + return result; } } connssl->connecting_state = ssl_connect_done; - return retcode; + return result; } @@ -372,7 +495,7 @@ static ssize_t cyassl_send(struct connectdata *conn, size_t len, CURLcode *curlcode) { - char error_buffer[80]; + char error_buffer[CYASSL_MAX_ERROR_SZ]; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen); @@ -396,11 +519,6 @@ static ssize_t cyassl_send(struct connectdata *conn, return rc; } -void Curl_cyassl_close_all(struct SessionHandle *data) -{ - (void)data; -} - void Curl_cyassl_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *conssl = &conn->ssl[sockindex]; @@ -422,7 +540,7 @@ static ssize_t cyassl_recv(struct connectdata *conn, size_t buffersize, CURLcode *curlcode) { - char error_buffer[80]; + char error_buffer[CYASSL_MAX_ERROR_SZ]; int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; int nread = SSL_read(conn->ssl[num].handle, buf, buffsize); @@ -458,7 +576,9 @@ void Curl_cyassl_session_free(void *ptr) size_t Curl_cyassl_version(char *buffer, size_t size) { -#ifdef CYASSL_VERSION +#ifdef WOLFSSL_VERSION + return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); +#elif defined(CYASSL_VERSION) return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION); #else return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8"); @@ -468,10 +588,7 @@ size_t Curl_cyassl_version(char *buffer, size_t size) int Curl_cyassl_init(void) { - if(CyaSSL_Init() == 0) - return 1; - - return -1; + return (CyaSSL_Init() == SSL_SUCCESS); } @@ -507,7 +624,7 @@ cyassl_connect_common(struct connectdata *conn, bool nonblocking, bool *done) { - CURLcode retcode; + CURLcode result; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; @@ -529,9 +646,10 @@ cyassl_connect_common(struct connectdata *conn, failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } - retcode = cyassl_connect_step1(conn, sockindex); - if(retcode) - return retcode; + + result = cyassl_connect_step1(conn, sockindex); + if(result) + return result; } while(ssl_connect_2 == connssl->connecting_state || @@ -583,22 +701,21 @@ cyassl_connect_common(struct connectdata *conn, * ensuring that a client using select() or epoll() will always * have a valid fdset to wait on. */ - retcode = cyassl_connect_step2(conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; - + result = cyassl_connect_step2(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) { - retcode = cyassl_connect_step3(conn, sockindex); - if(retcode) - return retcode; + if(ssl_connect_3 == connssl->connecting_state) { + result = cyassl_connect_step3(conn, sockindex); + if(result) + return result; } - if(ssl_connect_done==connssl->connecting_state) { + if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; conn->recv[sockindex] = cyassl_recv; conn->send[sockindex] = cyassl_send; @@ -627,12 +744,12 @@ CURLcode Curl_cyassl_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done = FALSE; - retcode = cyassl_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + result = cyassl_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; DEBUGASSERT(done); @@ -647,9 +764,23 @@ int Curl_cyassl_random(struct SessionHandle *data, (void)data; if(InitRng(&rng)) return 1; - if(RNG_GenerateBlock(&rng, entropy, length)) + if(length > UINT_MAX) + return 1; + if(RNG_GenerateBlock(&rng, entropy, (unsigned)length)) return 1; return 0; } +void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum /* output */, + size_t unused) +{ + Sha256 SHA256pw; + (void)unused; + InitSha256(&SHA256pw); + Sha256Update(&SHA256pw, tmp, tmplen); + Sha256Final(&SHA256pw, sha256sum); +} + #endif diff --git a/lib/vtls/cyassl.h b/lib/vtls/cyassl.h index b10b607..167de74 100644 --- a/lib/vtls/cyassl.h +++ b/lib/vtls/cyassl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,13 +26,9 @@ #ifdef USE_CYASSL CURLcode Curl_cyassl_connect(struct connectdata *conn, int sockindex); -bool Curl_cyassl_data_pending(const struct connectdata* conn,int connindex); +bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex); int Curl_cyassl_shutdown(struct connectdata* conn, int sockindex); -/* tell CyaSSL to close down all open information regarding connections (and - thus session ID caching etc) */ -void Curl_cyassl_close_all(struct SessionHandle *data); - /* close a SSL connection */ void Curl_cyassl_close(struct connectdata *conn, int sockindex); @@ -46,6 +42,16 @@ CURLcode Curl_cyassl_connect_nonblocking(struct connectdata *conn, int Curl_cyassl_random(struct SessionHandle *data, unsigned char *entropy, size_t length); +void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t unused); + +/* Set the API backend definition to Schannel */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_CYASSL + +/* this backend supports CURLOPT_SSL_CTX_* */ +#define have_curlssl_ssl_ctx 1 /* API setup for CyaSSL */ #define curlssl_init Curl_cyassl_init @@ -53,17 +59,17 @@ int Curl_cyassl_random(struct SessionHandle *data, #define curlssl_connect Curl_cyassl_connect #define curlssl_connect_nonblocking Curl_cyassl_connect_nonblocking #define curlssl_session_free(x) Curl_cyassl_session_free(x) -#define curlssl_close_all Curl_cyassl_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_cyassl_close #define curlssl_shutdown(x,y) Curl_cyassl_shutdown(x,y) -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_cyassl_version -#define curlssl_check_cxn(x) (x=x, -1) +#define curlssl_check_cxn(x) ((void)x, -1) #define curlssl_data_pending(x,y) Curl_cyassl_data_pending(x,y) #define curlssl_random(x,y,z) Curl_cyassl_random(x,y,z) -#define CURL_SSL_BACKEND CURLSSLBACKEND_CYASSL +#define curlssl_sha256sum(a,b,c,d) Curl_cyassl_sha256sum(a,b,c,d) #endif /* USE_CYASSL */ #endif /* HEADER_CURL_CYASSL_H */ diff --git a/lib/vtls/curl_darwinssl.c b/lib/vtls/darwinssl.c index f229c6f..03adcef 100644 --- a/lib/vtls/curl_darwinssl.c +++ b/lib/vtls/darwinssl.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>. - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -86,6 +86,7 @@ #define CURL_SUPPORT_MAC_10_6 0 #define CURL_SUPPORT_MAC_10_7 0 #define CURL_SUPPORT_MAC_10_8 0 +#define CURL_SUPPORT_MAC_10_9 0 #else #error "The darwinssl back-end requires iOS or OS X." @@ -101,10 +102,8 @@ #include "connect.h" #include "select.h" #include "vtls.h" -#include "curl_darwinssl.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "darwinssl.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -1057,10 +1056,8 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLSetProtocolVersionMax != NULL) { switch(data->set.ssl.version) { - case CURL_SSLVERSION_DEFAULT: default: - (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3); - (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12); - break; + default: + case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kTLSProtocol1); (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12); @@ -1078,7 +1075,11 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12); break; case CURL_SSLVERSION_SSLv3: - (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3); + err = SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3); + if(err != noErr) { + failf(data, "Your version of the OS does not support SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + } (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kSSLProtocol3); break; case CURL_SSLVERSION_SSLv2: @@ -1096,20 +1097,8 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, kSSLProtocolAll, false); switch (data->set.ssl.version) { - case CURL_SSLVERSION_DEFAULT: default: - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, - kSSLProtocol3, - true); - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, - kTLSProtocol1, - true); - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, - kTLSProtocol11, - true); - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, - kTLSProtocol12, - true); - break; + default: + case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kTLSProtocol1, @@ -1137,9 +1126,13 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, true); break; case CURL_SSLVERSION_SSLv3: - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocol3, true); + if(err != noErr) { + failf(data, "Your version of the OS does not support SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + } break; case CURL_SSLVERSION_SSLv2: err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx, @@ -1158,13 +1151,6 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, switch(data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, - kSSLProtocol3, - true); - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, - kTLSProtocol1, - true); - break; case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, @@ -1187,9 +1173,13 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, } break; case CURL_SSLVERSION_SSLv3: - (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocol3, true); + if(err != noErr) { + failf(data, "Your version of the OS does not support SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + } break; } #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ @@ -1469,14 +1459,17 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 /* We want to enable 1/n-1 when using a CBC cipher unless the user specifically doesn't want us doing that: */ - if(SSLSetSessionOption != NULL) + if(SSLSetSessionOption != NULL) { SSLSetSessionOption(connssl->ssl_ctx, kSSLSessionOptionSendOneByteRecord, !data->set.ssl_enable_beast); + SSLSetSessionOption(connssl->ssl_ctx, kSSLSessionOptionFalseStart, + data->set.ssl.falsestart); /* false start support */ + } #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, (void **)&ssl_sessionid, - &ssl_sessionid_len)) { + &ssl_sessionid_len)) { /* we got a session id, use it! */ err = SSLSetPeerID(connssl->ssl_ctx, ssl_sessionid, ssl_sessionid_len); if(err != noErr) { @@ -1489,20 +1482,23 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, /* If there isn't one, then let's make one up! This has to be done prior to starting the handshake. */ else { - CURLcode retcode; + CURLcode result; + ssl_sessionid = + aprintf("%s:%d:%d:%s:%hu", data->set.str[STRING_SSL_CAFILE], + data->set.ssl.verifypeer, data->set.ssl.verifyhost, + conn->host.name, conn->remote_port); + ssl_sessionid_len = strlen(ssl_sessionid); - ssl_sessionid = malloc(256*sizeof(char)); - ssl_sessionid_len = snprintf(ssl_sessionid, 256, "curl:%s:%hu", - conn->host.name, conn->remote_port); err = SSLSetPeerID(connssl->ssl_ctx, ssl_sessionid, ssl_sessionid_len); if(err != noErr) { failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } - retcode = Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_sessionid_len); - if(retcode!= CURLE_OK) { + + result = Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_sessionid_len); + if(result) { failf(data, "failed to store ssl session"); - return retcode; + return result; } } @@ -2081,7 +2077,7 @@ darwinssl_connect_common(struct connectdata *conn, bool nonblocking, bool *done) { - CURLcode retcode; + CURLcode result; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; @@ -2103,9 +2099,10 @@ darwinssl_connect_common(struct connectdata *conn, failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } - retcode = darwinssl_connect_step1(conn, sockindex); - if(retcode) - return retcode; + + result = darwinssl_connect_step1(conn, sockindex); + if(result) + return result; } while(ssl_connect_2 == connssl->connecting_state || @@ -2122,8 +2119,8 @@ darwinssl_connect_common(struct connectdata *conn, } /* 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) { + 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; @@ -2156,23 +2153,23 @@ darwinssl_connect_common(struct connectdata *conn, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - retcode = darwinssl_connect_step2(conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; + result = darwinssl_connect_step2(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) { - retcode = darwinssl_connect_step3(conn, sockindex); - if(retcode) - return retcode; + if(ssl_connect_3 == connssl->connecting_state) { + result = darwinssl_connect_step3(conn, sockindex); + if(result) + return result; } - if(ssl_connect_done==connssl->connecting_state) { + if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; conn->recv[sockindex] = darwinssl_recv; conn->send[sockindex] = darwinssl_send; @@ -2199,13 +2196,13 @@ CURLcode Curl_darwinssl_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done = FALSE; - retcode = darwinssl_connect_common(conn, sockindex, FALSE, &done); + result = darwinssl_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + if(result) + return result; DEBUGASSERT(done); @@ -2233,12 +2230,6 @@ void Curl_darwinssl_close(struct connectdata *conn, int sockindex) connssl->ssl_sockfd = 0; } -void Curl_darwinssl_close_all(struct SessionHandle *data) -{ - /* SecureTransport doesn't separate sessions from contexts, so... */ - (void)data; -} - int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; @@ -2376,6 +2367,14 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ (void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum); } +bool Curl_darwinssl_false_start(void) { +#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 + if(SSLSetSessionOption != NULL) + return TRUE; +#endif + return FALSE; +} + static ssize_t darwinssl_send(struct connectdata *conn, int sockindex, const void *mem, diff --git a/lib/vtls/curl_darwinssl.h b/lib/vtls/darwinssl.h index f5c03d8..3bb69c0 100644 --- a/lib/vtls/curl_darwinssl.h +++ b/lib/vtls/darwinssl.h @@ -8,6 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,9 +32,6 @@ CURLcode Curl_darwinssl_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done); -/* this function doesn't actually do anything */ -void Curl_darwinssl_close_all(struct SessionHandle *data); - /* close a SSL connection */ void Curl_darwinssl_close(struct connectdata *conn, int sockindex); @@ -50,9 +48,10 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ size_t tmplen, unsigned char *md5sum, /* output */ size_t md5len); +bool Curl_darwinssl_false_start(void); -/* this backend provides these functions: */ -#define have_curlssl_md5sum 1 +/* Set the API backend definition to SecureTransport */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL /* API setup for SecureTransport */ #define curlssl_init() (1) @@ -60,18 +59,18 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ #define curlssl_connect Curl_darwinssl_connect #define curlssl_connect_nonblocking Curl_darwinssl_connect_nonblocking #define curlssl_session_free(x) Curl_darwinssl_session_free(x) -#define curlssl_close_all Curl_darwinssl_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_darwinssl_close #define curlssl_shutdown(x,y) 0 -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_darwinssl_version #define curlssl_check_cxn Curl_darwinssl_check_cxn #define curlssl_data_pending(x,y) Curl_darwinssl_data_pending(x, y) -#define curlssl_random(x,y,z) Curl_darwinssl_random(y,z) +#define curlssl_random(x,y,z) ((void)x, Curl_darwinssl_random(y,z)) #define curlssl_md5sum(a,b,c,d) Curl_darwinssl_md5sum(a,b,c,d) -#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL +#define curlssl_false_start() Curl_darwinssl_false_start() #endif /* USE_DARWINSSL */ #endif /* HEADER_CURL_DARWINSSL_H */ diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c index 0f8b08f..d884bd4 100644 --- a/lib/vtls/gskit.c +++ b/lib/vtls/gskit.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -74,9 +74,7 @@ #include "select.h" #include "strequal.h" #include "x509asn1.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ @@ -134,8 +132,12 @@ static const gskit_cipher ciphertable[] = { CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, { "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK }, + { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK }, { "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK }, + { "aes128-gcm-sha256", + "9C", CURL_GSKPROTO_TLSV12_MASK }, + { "aes256-gcm-sha384", + "9D", CURL_GSKPROTO_TLSV12_MASK }, { "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK }, { "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK }, { "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK }, @@ -164,8 +166,6 @@ static bool is_separator(char c) static CURLcode gskit_status(struct SessionHandle *data, int rc, const char *procname, CURLcode defcode) { - CURLcode cc; - /* Process GSKit status and map it to a CURLcode. */ switch (rc) { case GSK_OK: @@ -298,7 +298,7 @@ static CURLcode set_ciphers(struct SessionHandle *data, int i; int l; bool unsupported; - CURLcode cc; + CURLcode result; struct { char *buf; char *ptr; @@ -331,7 +331,7 @@ static CURLcode set_ciphers(struct SessionHandle *data, /* Process each cipher in input string. */ unsupported = FALSE; - cc = CURLE_OK; + result = CURLE_OK; for(;;) { for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) cipherlist++; @@ -344,7 +344,7 @@ static CURLcode set_ciphers(struct SessionHandle *data, break; if(!ctp->name) { failf(data, "Unknown cipher %.*s", l, clp); - cc = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; } else { unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK | @@ -372,53 +372,53 @@ static CURLcode set_ciphers(struct SessionHandle *data, /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */ if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) { - cc = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE); - if(cc == CURLE_UNSUPPORTED_PROTOCOL) { - cc = CURLE_OK; + result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS, + ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE); + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + result = CURLE_OK; if(unsupported) { failf(data, "TLSv1.1-only ciphers are not yet supported"); - cc = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; } } } - if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) { - cc = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE); - if(cc == CURLE_UNSUPPORTED_PROTOCOL) { - cc = CURLE_OK; + if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) { + result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS, + ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE); + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + result = CURLE_OK; if(unsupported) { failf(data, "TLSv1.2-only ciphers are not yet supported"); - cc = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; } } } /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */ - if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) { - cc = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE); - if(cc == CURLE_UNSUPPORTED_PROTOCOL) { - cc = CURLE_OK; + if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) { + result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS, + ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE); + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + result = CURLE_OK; strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr, ciphers[CURL_GSKPROTO_TLSV10].ptr); } } /* Set-up other ciphers. */ - if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_SSLV3_MASK)) - cc = set_buffer(data, h, GSK_V3_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE); - if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_SSLV2_MASK)) - cc = set_buffer(data, h, GSK_V2_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE); + if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK)) + result = set_buffer(data, h, GSK_V3_CIPHER_SPECS, + ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE); + if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK)) + result = set_buffer(data, h, GSK_V2_CIPHER_SPECS, + ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE); /* Clean-up. */ for(i = 0; i < CURL_GSKPROTO_LAST; i++) free(ciphers[i].buf); - return cc; + return result; } @@ -442,7 +442,7 @@ static CURLcode init_environment(struct SessionHandle *data, const char *password) { int rc; - CURLcode c; + CURLcode result; gsk_handle h; /* Creates the GSKit environment. */ @@ -458,29 +458,29 @@ static CURLcode init_environment(struct SessionHandle *data, return CURLE_SSL_CONNECT_ERROR; } - c = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE); - if(c == CURLE_OK && appid) - c = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE); - if(c == CURLE_OK && file) - c = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE); - if(c == CURLE_OK && label) - c = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE); - if(c == CURLE_OK && password) - c = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE); - - if(c == CURLE_OK) { + result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE); + if(!result && appid) + result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE); + if(!result && file) + result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE); + if(!result && label) + result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE); + if(!result && password) + result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE); + + if(!result) { /* Locate CAs, Client certificate and key according to our settings. Note: this call may be blocking for some tenths of seconds. */ - c = gskit_status(data, gsk_environment_init(h), - "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); - if(c == CURLE_OK) { + result = gskit_status(data, gsk_environment_init(h), + "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); + if(!result) { *envir = h; - return c; + return result; } } /* Error: rollback. */ gsk_environment_close(&h); - return c; + return result; } @@ -558,7 +558,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; gsk_handle envir; - CURLcode cc; + CURLcode result; int rc; char *keyringfile; char *keyringpwd; @@ -600,22 +600,22 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) if(!envir) { /* Use keyring mode. */ - cc = init_environment(data, &envir, (const char *) NULL, - keyringfile, keyringlabel, keyringpwd); - if(cc != CURLE_OK) - return cc; + result = init_environment(data, &envir, (const char *) NULL, + keyringfile, keyringlabel, keyringpwd); + if(result) + return result; } /* Create secure session. */ - cc = gskit_status(data, gsk_secure_soc_open(envir, &connssl->handle), - "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); + result = gskit_status(data, gsk_secure_soc_open(envir, &connssl->handle), + "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); gsk_environment_close(&envir); - if(cc != CURLE_OK) - return cc; + if(result) + return result; /* Determine which SSL/TLS version should be enabled. */ - protoflags = CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK; + protoflags = CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | + CURL_GSKPROTO_TLSV12_MASK; sni = conn->host.name; switch (data->set.ssl.version) { case CURL_SSLVERSION_SSLv2: @@ -623,7 +623,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) sni = (char *) NULL; break; case CURL_SSLVERSION_SSLv3: - protoflags = CURL_GSKPROTO_SSLV2_MASK; + protoflags = CURL_GSKPROTO_SSLV3_MASK; sni = (char *) NULL; break; case CURL_SSLVERSION_TLSv1: @@ -643,81 +643,84 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ if(sni) { - cc = set_buffer(data, connssl->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE); - if(cc == CURLE_UNSUPPORTED_PROTOCOL) - cc = CURLE_OK; + result = set_buffer(data, connssl->handle, + GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE); + if(result == CURLE_UNSUPPORTED_PROTOCOL) + result = CURLE_OK; } /* Set session parameters. */ - if(cc == CURLE_OK) { + if(!result) { /* Compute the handshake timeout. Since GSKit granularity is 1 second, we round up the required value. */ timeout = Curl_timeleft(data, NULL, TRUE); if(timeout < 0) - cc = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; else - cc = set_numeric(data, connssl->handle, GSK_HANDSHAKE_TIMEOUT, - (timeout + 999) / 1000); + result = set_numeric(data, connssl->handle, GSK_HANDSHAKE_TIMEOUT, + (timeout + 999) / 1000); } - if(cc == CURLE_OK) - cc = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]); - if(cc == CURLE_OK) - cc = set_ciphers(data, connssl->handle, &protoflags); + if(!result) + result = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]); + if(!result) + result = set_ciphers(data, connssl->handle, &protoflags); if(!protoflags) { failf(data, "No SSL protocol/cipher combination enabled"); - cc = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; } - if(cc == CURLE_OK) - cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV2, - (protoflags & CURL_GSKPROTO_SSLV2_MASK)? - GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE); - if(cc == CURLE_OK) - cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV3, - (protoflags & CURL_GSKPROTO_SSLV3_MASK)? - GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE); - if(cc == CURLE_OK) - cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV1, - (protoflags & CURL_GSKPROTO_TLSV10_MASK)? - GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE); - if(cc == CURLE_OK) { - cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV11, - (protoflags & CURL_GSKPROTO_TLSV11_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(cc == CURLE_UNSUPPORTED_PROTOCOL) { - cc = CURLE_OK; + if(!result) + result = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV2, + (protoflags & CURL_GSKPROTO_SSLV2_MASK)? + GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE); + if(!result) + result = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV3, + (protoflags & CURL_GSKPROTO_SSLV3_MASK)? + GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE); + if(!result) + result = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV1, + (protoflags & CURL_GSKPROTO_TLSV10_MASK)? + GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE); + if(!result) { + result = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV11, + (protoflags & CURL_GSKPROTO_TLSV11_MASK)? + GSK_TRUE: GSK_FALSE, TRUE); + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + result = CURLE_OK; if(protoflags == CURL_GSKPROTO_TLSV11_MASK) { failf(data, "TLS 1.1 not yet supported"); - cc = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; } } } - if(cc == CURLE_OK) { - cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV12, - (protoflags & CURL_GSKPROTO_TLSV12_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(cc == CURLE_UNSUPPORTED_PROTOCOL) { - cc = CURLE_OK; + if(!result) { + result = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV12, + (protoflags & CURL_GSKPROTO_TLSV12_MASK)? + GSK_TRUE: GSK_FALSE, TRUE); + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + result = CURLE_OK; if(protoflags == CURL_GSKPROTO_TLSV12_MASK) { failf(data, "TLS 1.2 not yet supported"); - cc = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; } } } - if(cc == CURLE_OK) - cc = set_enum(data, connssl->handle, GSK_SERVER_AUTH_TYPE, - data->set.ssl.verifypeer? GSK_SERVER_AUTH_FULL: - GSK_SERVER_AUTH_PASSTHRU, FALSE); + if(!result) + result = set_enum(data, connssl->handle, GSK_SERVER_AUTH_TYPE, + data->set.ssl.verifypeer? GSK_SERVER_AUTH_FULL: + GSK_SERVER_AUTH_PASSTHRU, FALSE); - if(cc == CURLE_OK) { + if(!result) { /* Start handshake. Try asynchronous first. */ memset(&commarea, 0, sizeof commarea); connssl->iocport = QsoCreateIOCompletionPort(); if(connssl->iocport != -1) { - cc = gskit_status(data, gsk_secure_soc_startInit(connssl->handle, - connssl->iocport, &commarea), - "gsk_secure_soc_startInit()", CURLE_SSL_CONNECT_ERROR); - if(cc == CURLE_OK) { + result = gskit_status(data, + gsk_secure_soc_startInit(connssl->handle, + connssl->iocport, + &commarea), + "gsk_secure_soc_startInit()", + CURLE_SSL_CONNECT_ERROR); + if(!result) { connssl->connecting_state = ssl_connect_2; return CURLE_OK; } @@ -725,12 +728,13 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) close_async_handshake(connssl); } else if(errno != ENOBUFS) - cc = gskit_status(data, GSK_ERROR_IO, "QsoCreateIOCompletionPort()", 0); + result = gskit_status(data, GSK_ERROR_IO, + "QsoCreateIOCompletionPort()", 0); else { /* No more completion port available. Use synchronous IO. */ - cc = gskit_status(data, gsk_secure_soc_init(connssl->handle), - "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); - if(cc == CURLE_OK) { + result = gskit_status(data, gsk_secure_soc_init(connssl->handle), + "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); + if(!result) { connssl->connecting_state = ssl_connect_3; return CURLE_OK; } @@ -739,7 +743,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex) /* Error: rollback. */ close_one(connssl, data); - return cc; + return result; } @@ -751,7 +755,7 @@ static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex, Qso_OverlappedIO_t cstat; long timeout_ms; struct timeval stmv; - CURLcode cc; + CURLcode result; /* Poll or wait for end of SSL asynchronous handshake. */ @@ -786,12 +790,12 @@ static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex, } break; } - cc = gskit_status(data, cstat.returnValue, "SSL handshake", - CURLE_SSL_CONNECT_ERROR); - if(cc == CURLE_OK) + result = gskit_status(data, cstat.returnValue, "SSL handshake", + CURLE_SSL_CONNECT_ERROR); + if(!result) connssl->connecting_state = ssl_connect_3; close_async_handshake(connssl); - return cc; + return result; } @@ -804,8 +808,9 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex) const gsk_cert_data_elem *p; const char *cert = (const char *) NULL; const char *certend; + const char *ptr; int i; - CURLcode cc; + CURLcode result; /* SSL handshake done: gather certificate info and verify host. */ @@ -838,9 +843,9 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex) } /* Verify host. */ - cc = Curl_verifyhost(conn, cert, certend); - if(cc != CURLE_OK) - return cc; + result = Curl_verifyhost(conn, cert, certend); + if(result) + return result; /* The only place GSKit can get the whole CA chain is a validation callback where no user data pointer is available. Therefore it's not @@ -848,12 +853,31 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex) However the server certificate may be available, thus we can return info about it. */ if(data->set.ssl.certinfo) { - if(Curl_ssl_init_certinfo(data, 1)) - return CURLE_OUT_OF_MEMORY; + result = Curl_ssl_init_certinfo(data, 1); + if(result) + return result; + if(cert) { - cc = Curl_extract_certinfo(conn, 0, cert, certend); - if(cc != CURLE_OK) - return cc; + result = Curl_extract_certinfo(conn, 0, cert, certend); + if(result) + return result; + } + } + + /* Check pinned public key. */ + ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + if(!result && ptr) { + curl_X509certificate x509; + curl_asn1Element *p; + + if(!cert) + return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + Curl_parseX509(&x509, cert, certend); + p = &x509.subjectPublicKeyInfo; + result = Curl_pin_peer_pubkey(ptr, p->header, p->end - p->header); + if(result) { + failf(data, "SSL: public key does not match pinned public key!"); + return result; } } @@ -869,7 +893,7 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex, struct ssl_connect_data *connssl = &conn->ssl[sockindex]; long timeout_ms; Qso_OverlappedIO_t cstat; - CURLcode cc = CURLE_OK; + CURLcode result = CURLE_OK; *done = connssl->state == ssl_connection_complete; if(*done) @@ -883,31 +907,31 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex, if(timeout_ms < 0) { /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); - cc = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; } else - cc = gskit_connect_step1(conn, sockindex); + result = gskit_connect_step1(conn, sockindex); } /* Step 2: check if handshake is over. */ - if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_2) { + if(!result && connssl->connecting_state == ssl_connect_2) { /* 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"); - cc = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; } else - cc = gskit_connect_step2(conn, sockindex, nonblocking); + result = gskit_connect_step2(conn, sockindex, nonblocking); } /* Step 3: gather certificate info, verify host. */ - if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_3) - cc = gskit_connect_step3(conn, sockindex); + if(!result && connssl->connecting_state == ssl_connect_3) + result = gskit_connect_step3(conn, sockindex); - if(cc != CURLE_OK) + if(result) close_one(connssl, data); else if(connssl->connecting_state == ssl_connect_done) { connssl->state = ssl_connection_complete; @@ -917,7 +941,7 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex, *done = TRUE; } - return cc; + return result; } @@ -925,24 +949,24 @@ CURLcode Curl_gskit_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done) { - CURLcode cc; + CURLcode result; - cc = gskit_connect_common(conn, sockindex, TRUE, done); - if(*done || cc != CURLE_OK) + result = gskit_connect_common(conn, sockindex, TRUE, done); + if(*done || result) conn->ssl[sockindex].connecting_state = ssl_connect_1; - return cc; + return result; } CURLcode Curl_gskit_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done; conn->ssl[sockindex].connecting_state = ssl_connect_1; - retcode = gskit_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + result = gskit_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; DEBUGASSERT(done); @@ -960,14 +984,6 @@ void Curl_gskit_close(struct connectdata *conn, int sockindex) } -int Curl_gskit_close_all(struct SessionHandle *data) -{ - /* Unimplemented. */ - (void) data; - return 0; -} - - int Curl_gskit_shutdown(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; diff --git a/lib/vtls/gskit.h b/lib/vtls/gskit.h index a4caa6f..af31faf 100644 --- a/lib/vtls/gskit.h +++ b/lib/vtls/gskit.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,15 +32,20 @@ #ifdef USE_GSKIT int Curl_gskit_init(void); void Curl_gskit_cleanup(void); -CURLcode Curl_gskit_connect(struct connectdata * conn, int sockindex); -CURLcode Curl_gskit_connect_nonblocking(struct connectdata * conn, - int sockindex, bool * done); +CURLcode Curl_gskit_connect(struct connectdata *conn, int sockindex); +CURLcode Curl_gskit_connect_nonblocking(struct connectdata *conn, + int sockindex, bool *done); void Curl_gskit_close(struct connectdata *conn, int sockindex); -int Curl_gskit_close_all(struct SessionHandle * data); -int Curl_gskit_shutdown(struct connectdata * conn, int sockindex); +int Curl_gskit_shutdown(struct connectdata *conn, int sockindex); -size_t Curl_gskit_version(char * buffer, size_t size); -int Curl_gskit_check_cxn(struct connectdata * cxn); +size_t Curl_gskit_version(char *buffer, size_t size); +int Curl_gskit_check_cxn(struct connectdata *cxn); + +/* Set the API backend definition to GSKit */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_GSKIT + +/* this backend supports CURLOPT_CERTINFO */ +#define have_curlssl_certinfo 1 /* API setup for GSKit */ #define curlssl_init Curl_gskit_init @@ -50,7 +55,7 @@ int Curl_gskit_check_cxn(struct connectdata * cxn); /* No session handling for GSKit */ #define curlssl_session_free(x) Curl_nop_stmt -#define curlssl_close_all Curl_gskit_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_gskit_close #define curlssl_shutdown(x,y) Curl_gskit_shutdown(x,y) #define curlssl_set_engine(x,y) CURLE_NOT_BUILT_IN @@ -59,7 +64,8 @@ int Curl_gskit_check_cxn(struct connectdata * cxn); #define curlssl_version Curl_gskit_version #define curlssl_check_cxn(x) Curl_gskit_check_cxn(x) #define curlssl_data_pending(x,y) 0 -#define CURL_SSL_BACKEND CURLSSLBACKEND_GSKIT +#define curlssl_random(x,y,z) -1 + #endif /* USE_GSKIT */ #endif /* HEADER_CURL_GSKIT_H */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index d64f95d..c54dfc1 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,12 +32,14 @@ #ifdef USE_GNUTLS +#include <gnutls/abstract.h> #include <gnutls/gnutls.h> #include <gnutls/x509.h> #ifdef USE_GNUTLS_NETTLE #include <gnutls/crypto.h> #include <nettle/md5.h> +#include <nettle/sha2.h> #else #include <gcrypt.h> #endif @@ -52,9 +54,8 @@ #include "select.h" #include "rawstr.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "x509asn1.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -91,14 +92,23 @@ static bool gtls_inited = FALSE; # define GNUTLS_MAPS_WINSOCK_ERRORS 1 # endif -# ifdef USE_NGHTTP2 -# undef HAS_ALPN -# if (GNUTLS_VERSION_NUMBER >= 0x030200) -# define HAS_ALPN -# endif +# if (GNUTLS_VERSION_NUMBER >= 0x030200) +# define HAS_ALPN +# endif + +# if (GNUTLS_VERSION_NUMBER >= 0x03020d) +# define HAS_OCSP +# endif + +# if (GNUTLS_VERSION_NUMBER >= 0x030306) +# define HAS_CAPATH # endif #endif +#ifdef HAS_OCSP +# include <gnutls/ocsp.h> +#endif + /* * Custom push and pull callback functions used by GNU TLS to read and write * to the socket. These functions are simple wrappers to send() and recv() @@ -203,7 +213,7 @@ static void showtime(struct SessionHandle *data, snprintf(data->state.buffer, BUFSIZE, - "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n", + "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT", text, Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], tm->tm_mday, @@ -222,7 +232,7 @@ static gnutls_datum_t load_file (const char *file) long filelen; void *ptr; - if(!(f = fopen(file, "r"))) + if(!(f = fopen(file, "rb"))) return loaded_file; if(fseek(f, 0, SEEK_END) != 0 || (filelen = ftell(f)) < 0 @@ -306,8 +316,6 @@ static CURLcode handshake(struct connectdata *conn, gnutls_record_get_direction(session)? ssl_connect_2_writing:ssl_connect_2_reading; continue; - if(nonblocking) - return CURLE_OK; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { const char *strerr = NULL; @@ -320,7 +328,8 @@ static CURLcode handshake(struct connectdata *conn, if(strerr == NULL) strerr = gnutls_strerror(rc); - failf(data, "gnutls_handshake() warning: %s", strerr); + infof(data, "gnutls_handshake() warning: %s\n", strerr); + continue; } else if(rc < 0) { const char *strerr = NULL; @@ -393,10 +402,6 @@ gtls_connect_step1(struct connectdata *conn, const char* prioritylist; const char *err = NULL; #endif -#ifdef HAS_ALPN - int protocols_size = 2; - gnutls_datum_t protocols[2]; -#endif if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the @@ -464,6 +469,24 @@ gtls_connect_step1(struct connectdata *conn, rc, data->set.ssl.CAfile); } +#ifdef HAS_CAPATH + if(data->set.ssl.CApath) { + /* set the trusted CA cert directory */ + rc = gnutls_certificate_set_x509_trust_dir(conn->ssl[sockindex].cred, + data->set.ssl.CApath, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)\n", + data->set.ssl.CAfile, gnutls_strerror(rc)); + if(data->set.ssl.verifypeer) + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "found %d certificates in %s\n", + rc, data->set.ssl.CApath); + } +#endif + if(data->set.ssl.CRLfile) { /* set the CRL list file */ rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred, @@ -610,19 +633,25 @@ gtls_connect_step1(struct connectdata *conn, #endif #ifdef HAS_ALPN - if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { - if(data->set.ssl_enable_alpn) { - protocols[0].data = NGHTTP2_PROTO_VERSION_ID; - protocols[0].size = NGHTTP2_PROTO_VERSION_ID_LEN; - protocols[1].data = ALPN_HTTP_1_1; - protocols[1].size = ALPN_HTTP_1_1_LENGTH; - gnutls_alpn_set_protocols(session, protocols, protocols_size, 0); - infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID, - ALPN_HTTP_1_1); - } - else { - infof(data, "SSL, can't negotiate HTTP/2.0 without ALPN\n"); + if(data->set.ssl_enable_alpn) { + int cur = 0; + gnutls_datum_t protocols[2]; + +#ifdef USE_NGHTTP2 + if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { + protocols[cur].data = (unsigned char *)NGHTTP2_PROTO_VERSION_ID; + protocols[cur].size = NGHTTP2_PROTO_VERSION_ID_LEN; + cur++; + infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } +#endif + + protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1; + protocols[cur].size = ALPN_HTTP_1_1_LENGTH; + cur++; + infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); + + gnutls_alpn_set_protocols(session, protocols, cur, 0); } #endif @@ -644,13 +673,21 @@ gtls_connect_step1(struct connectdata *conn, if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP, conn->ssl[sockindex].srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) + if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } } else #endif + { rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, conn->ssl[sockindex].cred); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } /* set the connection handle (file descriptor for the socket) */ gnutls_transport_set_ptr(session, @@ -663,6 +700,16 @@ gtls_connect_step1(struct connectdata *conn, /* lowat must be set to zero when using custom push and pull functions. */ gnutls_transport_set_lowat(session, 0); +#ifdef HAS_OCSP + if(data->set.ssl.verifystatus) { + rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif + /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ @@ -677,6 +724,62 @@ gtls_connect_step1(struct connectdata *conn, return CURLE_OK; } +static CURLcode pkp_pin_peer_pubkey(gnutls_x509_crt_t cert, + const char *pinnedpubkey) +{ + /* Scratch */ + size_t len1 = 0, len2 = 0; + unsigned char *buff1 = NULL; + + gnutls_pubkey_t key = NULL; + + /* Result is returned to caller */ + int ret = 0; + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + /* if a path wasn't specified, don't pin */ + if(NULL == pinnedpubkey) + return CURLE_OK; + + if(NULL == cert) + return result; + + do { + /* Begin Gyrations to get the public key */ + gnutls_pubkey_init(&key); + + ret = gnutls_pubkey_import_x509(key, cert, 0); + if(ret < 0) + break; /* failed */ + + ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1); + if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0) + break; /* failed */ + + buff1 = malloc(len1); + if(NULL == buff1) + break; /* failed */ + + len2 = len1; + + ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2); + if(ret < 0 || len1 != len2) + break; /* failed */ + + /* End Gyrations */ + + /* The one good exit point */ + result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1); + } while(0); + + if(NULL != key) + gnutls_pubkey_deinit(key); + + Curl_safefree(buff1); + + return result; +} + static Curl_recv gtls_recv; static Curl_send gtls_send; @@ -686,8 +789,8 @@ gtls_connect_step3(struct connectdata *conn, { unsigned int cert_list_size; const gnutls_datum_t *chainp; - unsigned int verify_status; - gnutls_x509_crt_t x509_cert,x509_issuer; + unsigned int verify_status = 0; + gnutls_x509_crt_t x509_cert, x509_issuer; gnutls_datum_t issuerp; char certbuf[256] = ""; /* big enough? */ size_t size; @@ -698,13 +801,23 @@ gtls_connect_step3(struct connectdata *conn, struct SessionHandle *data = conn->data; gnutls_session_t session = conn->ssl[sockindex].session; int rc; - int incache; + bool incache; void *ssl_sessionid; #ifdef HAS_ALPN gnutls_datum_t proto; #endif CURLcode result = CURLE_OK; + gnutls_protocol_t version = gnutls_protocol_get_version(session); + + /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ + ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session), + gnutls_cipher_get(session), + gnutls_mac_get(session)); + + infof(data, "SSL connection using %s / %s\n", + gnutls_protocol_get_name(version), ptr); + /* This function will return the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for X.509). In case of a X.509 then a certificate list may be present. The @@ -735,6 +848,23 @@ gtls_connect_step3(struct connectdata *conn, infof(data, "\t common name: WARNING couldn't obtain\n"); } + if(data->set.ssl.certinfo && chainp) { + unsigned int i; + + result = Curl_ssl_init_certinfo(data, cert_list_size); + if(result) + return result; + + for(i = 0; i < cert_list_size; i++) { + const char *beg = (const char *) chainp[i].data; + const char *end = beg + chainp[i].size; + + result = Curl_extract_certinfo(conn, i, beg, end); + if(result) + return result; + } + } + if(data->set.ssl.verifypeer) { /* This function will try to verify the peer's certificate and return its status (trusted, invalid etc.). The value of status should be one or @@ -766,6 +896,111 @@ gtls_connect_step3(struct connectdata *conn, else infof(data, "\t server certificate verification SKIPPED\n"); +#ifdef HAS_OCSP + if(data->set.ssl.verifystatus) { + if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { + gnutls_datum_t status_request; + gnutls_ocsp_resp_t ocsp_resp; + + gnutls_ocsp_cert_status_t status; + gnutls_x509_crl_reason_t reason; + + rc = gnutls_ocsp_status_request_get(session, &status_request); + + infof(data, "\t server certificate status verification FAILED\n"); + + if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + failf(data, "No OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + gnutls_ocsp_resp_init(&ocsp_resp); + + rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + rc = gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, + &status, NULL, NULL, NULL, &reason); + + switch(status) { + case GNUTLS_OCSP_CERT_GOOD: + break; + + case GNUTLS_OCSP_CERT_REVOKED: { + const char *crl_reason; + + switch(reason) { + default: + case GNUTLS_X509_CRLREASON_UNSPECIFIED: + crl_reason = "unspecified reason"; + break; + + case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: + crl_reason = "private key compromised"; + break; + + case GNUTLS_X509_CRLREASON_CACOMPROMISE: + crl_reason = "CA compromised"; + break; + + case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: + crl_reason = "affiliation has changed"; + break; + + case GNUTLS_X509_CRLREASON_SUPERSEDED: + crl_reason = "certificate superseded"; + break; + + case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: + crl_reason = "operation has ceased"; + break; + + case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: + crl_reason = "certificate is on hold"; + break; + + case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: + crl_reason = "will be removed from delta CRL"; + break; + + case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: + crl_reason = "privilege withdrawn"; + break; + + case GNUTLS_X509_CRLREASON_AACOMPROMISE: + crl_reason = "AA compromised"; + break; + } + + failf(data, "Server certificate was revoked: %s", crl_reason); + break; + } + + default: + case GNUTLS_OCSP_CERT_UNKNOWN: + failf(data, "Server certificate status is unknown"); + break; + } + + gnutls_ocsp_resp_deinit(ocsp_resp); + + return CURLE_SSL_INVALIDCERTSTATUS; + } + else + infof(data, "\t server certificate status verification OK\n"); + } + else + infof(data, "\t server certificate status verification SKIPPED\n"); +#endif + /* initialize an X.509 certificate structure. */ gnutls_x509_crt_init(&x509_cert); @@ -778,14 +1013,16 @@ gtls_connect_step3(struct connectdata *conn, gnutls_x509_crt_init(&x509_issuer); issuerp = load_file(data->set.ssl.issuercert); gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer); + rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); + gnutls_x509_crt_deinit(x509_issuer); unload_file(issuerp); if(rc <= 0) { failf(data, "server certificate issuer check failed (IssuerCert: %s)", data->set.ssl.issuercert?data->set.ssl.issuercert:"none"); + gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_ISSUER_ERROR; } - infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n", + infof(data, "\t server certificate issuer check OK (Issuer Cert: %s)\n", data->set.ssl.issuercert?data->set.ssl.issuercert:"none"); } @@ -868,6 +1105,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock == (time_t)-1) { if(data->set.ssl.verifypeer) { failf(data, "server cert expiration date verify failed"); + gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_CONNECT_ERROR; } else @@ -877,6 +1115,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock < time(NULL)) { if(data->set.ssl.verifypeer) { failf(data, "server certificate expiration date has passed."); + gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else @@ -891,6 +1130,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock == (time_t)-1) { if(data->set.ssl.verifypeer) { failf(data, "server cert activation date verify failed"); + gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_CONNECT_ERROR; } else @@ -900,6 +1140,7 @@ gtls_connect_step3(struct connectdata *conn, if(certclock > time(NULL)) { if(data->set.ssl.verifypeer) { failf(data, "server certificate not activated yet."); + gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else @@ -909,9 +1150,18 @@ gtls_connect_step3(struct connectdata *conn, infof(data, "\t server certificate activation date OK\n"); } + ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + if(ptr) { + result = pkp_pin_peer_pubkey(x509_cert, ptr); + if(result != CURLE_OK) { + failf(data, "SSL: public key does not match pinned public key!"); + gnutls_x509_crt_deinit(x509_cert); + return result; + } + } + /* Show: - - ciphers used - subject - start date - expire date @@ -951,14 +1201,6 @@ gtls_connect_step3(struct connectdata *conn, /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */ infof(data, "\t compression: %s\n", ptr); - /* the name of the cipher used. ie 3DES. */ - ptr = gnutls_cipher_get_name(gnutls_cipher_get(session)); - infof(data, "\t cipher: %s\n", ptr); - - /* the MAC algorithms name. ie SHA1 */ - ptr = gnutls_mac_get_name(gnutls_mac_get(session)); - infof(data, "\t MAC: %s\n", ptr); - #ifdef HAS_ALPN if(data->set.ssl_enable_alpn) { rc = gnutls_alpn_get_selected_protocol(session, &proto); @@ -966,19 +1208,21 @@ gtls_connect_step3(struct connectdata *conn, infof(data, "ALPN, server accepted to use %.*s\n", proto.size, proto.data); +#ifdef USE_NGHTTP2 if(proto.size == NGHTTP2_PROTO_VERSION_ID_LEN && - memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data, - NGHTTP2_PROTO_VERSION_ID_LEN) == 0) { - conn->negnpn = NPN_HTTP2; + !memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data, + NGHTTP2_PROTO_VERSION_ID_LEN)) { + conn->negnpn = CURL_HTTP_VERSION_2_0; } - else if(proto.size == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1, - proto.data, ALPN_HTTP_1_1_LENGTH) == 0) { - conn->negnpn = NPN_HTTP1_1; + else +#endif + if(proto.size == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) { + conn->negnpn = CURL_HTTP_VERSION_1_1; } } - else { + else infof(data, "ALPN, server did not agree to a protocol\n"); - } } #endif @@ -1079,12 +1323,12 @@ Curl_gtls_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done = FALSE; - retcode = gtls_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + result = gtls_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; DEBUGASSERT(done); @@ -1110,12 +1354,6 @@ static ssize_t gtls_send(struct connectdata *conn, return rc; } -void Curl_gtls_close_all(struct SessionHandle *data) -{ - /* FIX: make the OpenSSL code more generic and use parts of it here */ - (void)data; -} - static void close_one(struct connectdata *conn, int idx) { @@ -1232,10 +1470,10 @@ static ssize_t gtls_recv(struct connectdata *conn, /* connection data */ if(ret == GNUTLS_E_REHANDSHAKE) { /* BLOCKING call, this is bad but a work-around for now. Fixing this "the proper way" takes a whole lot of work. */ - CURLcode rc = handshake(conn, num, FALSE, FALSE); - if(rc) + CURLcode result = handshake(conn, num, FALSE, FALSE); + if(result) /* handshake() writes error message on its own */ - *curlcode = rc; + *curlcode = result; else *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */ return -1; @@ -1320,4 +1558,32 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */ #endif } +void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t sha256len) +{ +#if defined(USE_GNUTLS_NETTLE) + struct sha256_ctx SHA256pw; + sha256_init(&SHA256pw); + sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); + sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); +#elif defined(USE_GNUTLS) + gcry_md_hd_t SHA256pw; + gcry_md_open(&SHA256pw, GCRY_MD_SHA256, 0); + gcry_md_write(SHA256pw, tmp, tmplen); + memcpy(sha256sum, gcry_md_read (SHA256pw, 0), sha256len); + gcry_md_close(SHA256pw); +#endif +} + +bool Curl_gtls_cert_status_request(void) +{ +#ifdef HAS_OCSP + return TRUE; +#else + return FALSE; +#endif +} + #endif /* USE_GNUTLS */ diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index cd6152c..0afd9b9 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -35,10 +35,6 @@ CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done); -/* tell GnuTLS to close down all open information regarding connections (and - thus session ID caching etc) */ -void Curl_gtls_close_all(struct SessionHandle *data); - /* close a SSL connection */ void Curl_gtls_close(struct connectdata *conn, int sockindex); @@ -52,9 +48,21 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */ size_t tmplen, unsigned char *md5sum, /* output */ size_t md5len); +void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t sha256len); -/* this backend provides these functions: */ -#define have_curlssl_md5sum 1 +bool Curl_gtls_cert_status_request(void); + +/* Set the API backend definition to GnuTLS */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_GNUTLS + +/* this backend supports the CAPATH option */ +#define have_curlssl_ca_path 1 + +/* this backend supports CURLOPT_CERTINFO */ +#define have_curlssl_certinfo 1 /* API setup for GnuTLS */ #define curlssl_init Curl_gtls_init @@ -62,18 +70,19 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */ #define curlssl_connect Curl_gtls_connect #define curlssl_connect_nonblocking Curl_gtls_connect_nonblocking #define curlssl_session_free(x) Curl_gtls_session_free(x) -#define curlssl_close_all Curl_gtls_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_gtls_close #define curlssl_shutdown(x,y) Curl_gtls_shutdown(x,y) -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_gtls_version -#define curlssl_check_cxn(x) (x=x, -1) -#define curlssl_data_pending(x,y) (x=x, y=y, 0) +#define curlssl_check_cxn(x) ((void)x, -1) +#define curlssl_data_pending(x,y) ((void)x, (void)y, 0) #define curlssl_random(x,y,z) Curl_gtls_random(x,y,z) #define curlssl_md5sum(a,b,c,d) Curl_gtls_md5sum(a,b,c,d) -#define CURL_SSL_BACKEND CURLSSLBACKEND_GNUTLS +#define curlssl_sha256sum(a,b,c,d) Curl_gtls_sha256sum(a,b,c,d) +#define curlssl_cert_status_request() Curl_gtls_cert_status_request() #endif /* USE_GNUTLS */ #endif /* HEADER_CURL_GTLS_H */ diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 83b3e32..91727c7 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -38,10 +38,7 @@ #include "select.h" #include "vtls.h" #include "llist.h" - -#define _MPRINTF_REPLACE /* use the internal *printf() functions */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "nssg.h" #include <nspr.h> #include <nss.h> @@ -59,13 +56,20 @@ #include <base64.h> #include <cert.h> #include <prerror.h> +#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */ + +#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH) + +#if NSSVERNUM >= 0x030f00 /* 3.15.0 */ +#include <ocsp.h> +#endif -#include "curl_memory.h" #include "rawstr.h" #include "warnless.h" #include "x509asn1.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #define SSL_DIR "/etc/pki/nssdb" @@ -343,7 +347,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, CK_BBOOL ckfalse = CK_FALSE; CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; int attr_cnt = 0; - CURLcode err = (cacert) + CURLcode result = (cacert) ? CURLE_SSL_CACERT_BADFILE : CURLE_SSL_CERTPROBLEM; @@ -355,7 +359,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, slot = PK11_FindSlotByName(slot_name); free(slot_name); if(!slot) - return err; + return result; PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); @@ -370,7 +374,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE); PK11_FreeSlot(slot); if(!obj) - return err; + return result; if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) { PK11_DestroyGenericObject(obj); @@ -405,16 +409,16 @@ static void nss_destroy_crl_item(void *user, void *ptr) static CURLcode nss_load_cert(struct ssl_connect_data *ssl, const char *filename, PRBool cacert) { - CURLcode err = (cacert) + CURLcode result = (cacert) ? CURLE_SSL_CACERT_BADFILE : CURLE_SSL_CERTPROBLEM; /* libnsspem.so leaks memory if the requested file does not exist. For more * details, go to <https://bugzilla.redhat.com/734760>. */ if(is_file(filename)) - err = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); + result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); - if(CURLE_OK == err && !cacert) { + if(!result && !cacert) { /* we have successfully loaded a client certificate */ CERTCertificate *cert; char *nickname = NULL; @@ -436,7 +440,7 @@ static CURLcode nss_load_cert(struct ssl_connect_data *ssl, } } - return err; + return result; } /* add given CRL to cache if it is not already there */ @@ -448,7 +452,7 @@ static CURLcode nss_cache_crl(SECItem *crl_der) /* CRL already cached */ SEC_DestroyCrl(crl); SECITEM_FreeItem(crl_der, PR_TRUE); - return CURLE_SSL_CRL_BADFILE; + return CURLE_OK; } /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ @@ -542,14 +546,15 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex, { PK11SlotInfo *slot; SECStatus status; - CURLcode rv; + CURLcode result; struct ssl_connect_data *ssl = conn->ssl; + (void)sockindex; /* unused */ - rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); - if(CURLE_OK != rv) { + result = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); + if(result) { PR_SetError(SEC_ERROR_BAD_KEY, 0); - return rv; + return result; } slot = PK11_FindSlotByName("PEM Token #1"); @@ -563,9 +568,8 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex, status = PK11_Authenticate(slot, PR_TRUE, conn->data->set.str[STRING_KEY_PASSWD]); PK11_FreeSlot(slot); - return (SECSuccess == status) - ? CURLE_OK - : CURLE_SSL_CERTPROBLEM; + + return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; } static int display_error(struct connectdata *conn, PRInt32 err, @@ -588,35 +592,35 @@ static CURLcode cert_stuff(struct connectdata *conn, int sockindex, char *cert_file, char *key_file) { struct SessionHandle *data = conn->data; - CURLcode rv; + CURLcode result; if(cert_file) { - rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); - if(CURLE_OK != rv) { + result = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); + if(result) { const PRErrorCode err = PR_GetError(); if(!display_error(conn, err, cert_file)) { const char *err_name = nss_error_to_name(err); failf(data, "unable to load client cert: %d (%s)", err, err_name); } - return rv; + return result; } } if(key_file || (is_file(cert_file))) { if(key_file) - rv = nss_load_key(conn, sockindex, key_file); + result = nss_load_key(conn, sockindex, key_file); else /* In case the cert file also has the key */ - rv = nss_load_key(conn, sockindex, cert_file); - if(CURLE_OK != rv) { + result = nss_load_key(conn, sockindex, cert_file); + if(result) { const PRErrorCode err = PR_GetError(); if(!display_error(conn, err, key_file)) { const char *err_name = nss_error_to_name(err); failf(data, "unable to load client key: %d (%s)", err, err_name); } - return rv; + return result; } } @@ -626,6 +630,7 @@ static CURLcode cert_stuff(struct connectdata *conn, int sockindex, static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) { (void)slot; /* unused */ + if(retry || NULL == arg) return NULL; else @@ -638,6 +643,34 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, PRBool isServer) { struct connectdata *conn = (struct connectdata *)arg; + +#ifdef SSL_ENABLE_OCSP_STAPLING + if(conn->data->set.ssl.verifystatus) { + SECStatus cacheResult; + + const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd); + if(!csa) { + failf(conn->data, "Invalid OCSP response"); + return SECFailure; + } + + if(csa->len == 0) { + failf(conn->data, "No OCSP response received"); + return SECFailure; + } + + cacheResult = CERT_CacheOCSPResponseFromSideChannel( + CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd), + PR_Now(), &csa->items[0], arg + ); + + if(cacheResult != SECSuccess) { + failf(conn->data, "Invalid OCSP response"); + return cacheResult; + } + } +#endif + if(!conn->data->set.ssl.verifypeer) { infof(conn->data, "skipping SSL peer certificate verification\n"); return SECSuccess; @@ -651,7 +684,6 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, */ static void HandshakeCallback(PRFileDesc *sock, void *arg) { -#ifdef USE_NGHTTP2 struct connectdata *conn = (struct connectdata*) arg; unsigned int buflenmax = 50; unsigned char buf[50]; @@ -665,36 +697,94 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { switch(state) { - case SSL_NEXT_PROTO_NO_SUPPORT: - case SSL_NEXT_PROTO_NO_OVERLAP: - infof(conn->data, "TLS, neither ALPN nor NPN succeeded\n"); - return; + case SSL_NEXT_PROTO_NO_SUPPORT: + case SSL_NEXT_PROTO_NO_OVERLAP: + infof(conn->data, "ALPN/NPN, server did not agree to a protocol\n"); + return; #ifdef SSL_ENABLE_ALPN - case SSL_NEXT_PROTO_SELECTED: - infof(conn->data, "ALPN, server accepted to use %.*s\n", buflen, buf); - break; + case SSL_NEXT_PROTO_SELECTED: + infof(conn->data, "ALPN, server accepted to use %.*s\n", buflen, buf); + break; #endif - case SSL_NEXT_PROTO_NEGOTIATED: - infof(conn->data, "NPN, server accepted to use %.*s\n", buflen, buf); - break; + case SSL_NEXT_PROTO_NEGOTIATED: + infof(conn->data, "NPN, server accepted to use %.*s\n", buflen, buf); + break; } +#ifdef USE_NGHTTP2 if(buflen == NGHTTP2_PROTO_VERSION_ID_LEN && - memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN) - == 0) { - conn->negnpn = NPN_HTTP2; + !memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN)) { + conn->negnpn = CURL_HTTP_VERSION_2_0; } - else if(buflen == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1, buf, - ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = NPN_HTTP1_1; + else +#endif + if(buflen == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { + conn->negnpn = CURL_HTTP_VERSION_1_1; } } -#else - (void)sock; - (void)arg; -#endif } +#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ +static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data, + PRBool *canFalseStart) +{ + struct connectdata *conn = client_data; + struct SessionHandle *data = conn->data; + + SSLChannelInfo channelInfo; + SSLCipherSuiteInfo cipherInfo; + + SECStatus rv; + PRBool negotiatedExtension; + + *canFalseStart = PR_FALSE; + + if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess) + return SECFailure; + + if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, + sizeof(cipherInfo)) != SECSuccess) + return SECFailure; + + /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for + * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310 + */ + if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2) + goto end; + + /* Only allow ECDHE key exchange algorithm. + * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */ + if(cipherInfo.keaType != ssl_kea_ecdh) + goto end; + + /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC + * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt + * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */ + if(cipherInfo.symCipher != ssl_calg_aes_gcm) + goto end; + + /* Enforce ALPN or NPN to do False Start, as an indicator of server + * compatibility. */ + rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn, + &negotiatedExtension); + if(rv != SECSuccess || !negotiatedExtension) { + rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn, + &negotiatedExtension); + } + + if(rv != SECSuccess || !negotiatedExtension) + goto end; + + *canFalseStart = PR_TRUE; + + infof(data, "Trying TLS False Start\n"); + +end: + return SECSuccess; +} +#endif + static void display_cert_info(struct SessionHandle *data, CERTCertificate *cert) { @@ -723,8 +813,9 @@ static void display_cert_info(struct SessionHandle *data, PR_Free(common_name); } -static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) +static CURLcode display_conn_info(struct connectdata *conn, PRFileDesc *sock) { + CURLcode result = CURLE_OK; SSLChannelInfo channel; SSLCipherSuiteInfo suite; CERTCertificate *cert; @@ -743,7 +834,6 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) } cert = SSL_PeerCertificate(sock); - if(cert) { infof(conn->data, "Server certificate:\n"); @@ -768,21 +858,29 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) cert2 = cert3; } } - Curl_ssl_init_certinfo(conn->data, i); - for(i = 0; cert; cert = cert2) { - Curl_extract_certinfo(conn, i++, (char *)cert->derCert.data, - (char *)cert->derCert.data + cert->derCert.len); - if(cert->isRoot) { + + result = Curl_ssl_init_certinfo(conn->data, i); + if(!result) { + for(i = 0; cert; cert = cert2) { + result = Curl_extract_certinfo(conn, i++, (char *)cert->derCert.data, + (char *)cert->derCert.data + + cert->derCert.len); + if(result) + break; + + if(cert->isRoot) { + CERT_DestroyCertificate(cert); + break; + } + + cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); CERT_DestroyCertificate(cert); - break; } - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - CERT_DestroyCertificate(cert); } } } - return; + return result; } static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) @@ -820,7 +918,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) static SECStatus check_issuer_cert(PRFileDesc *sock, char *issuer_nickname) { - CERTCertificate *cert,*cert_issuer,*issuer; + CERTCertificate *cert, *cert_issuer, *issuer; SECStatus res=SECSuccess; void *proto_win = NULL; @@ -831,7 +929,7 @@ static SECStatus check_issuer_cert(PRFileDesc *sock, */ cert = SSL_PeerCertificate(sock); - cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner); + cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner); proto_win = SSL_RevealPinArg(sock); issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); @@ -848,6 +946,53 @@ static SECStatus check_issuer_cert(PRFileDesc *sock, return res; } +static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, + const char *pinnedpubkey) +{ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + struct SessionHandle *data = connssl->data; + CERTCertificate *cert; + + if(!pinnedpubkey) + /* no pinned public key specified */ + return CURLE_OK; + + /* get peer certificate */ + cert = SSL_PeerCertificate(connssl->handle); + if(cert) { + /* extract public key from peer certificate */ + SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); + if(pubkey) { + /* encode the public key as DER */ + SECItem *cert_der = PK11_DEREncodePublicKey(pubkey); + if(cert_der) { + /* compare the public key with the pinned public key */ + result = Curl_pin_peer_pubkey(pinnedpubkey, + cert_der->data, + cert_der->len); + SECITEM_FreeItem(cert_der, PR_TRUE); + } + SECKEY_DestroyPublicKey(pubkey); + } + CERT_DestroyCertificate(cert); + } + + /* report the resulting status */ + switch(result) { + case CURLE_OK: + infof(data, "pinned public key verified successfully!\n"); + break; + case CURLE_SSL_PINNEDPUBKEYNOTMATCH: + failf(data, "failed to verify pinned public key"); + break; + default: + /* OOM, etc. */ + break; + } + + return result; +} + /** * * Callback to pick the SSL client certificate. @@ -935,36 +1080,6 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, return SECSuccess; } -/* This function is supposed to decide, which error codes should be used - * to conclude server is TLS intolerant. - * - * taken from xulrunner - nsNSSIOLayer.cpp - */ -static PRBool -isTLSIntoleranceError(PRInt32 err) -{ - switch (err) { - case SSL_ERROR_BAD_MAC_ALERT: - case SSL_ERROR_BAD_MAC_READ: - case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: - case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: - case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE: - case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: - case SSL_ERROR_NO_CYPHER_OVERLAP: - case SSL_ERROR_BAD_SERVER: - case SSL_ERROR_BAD_BLOCK_PADDING: - case SSL_ERROR_UNSUPPORTED_VERSION: - case SSL_ERROR_PROTOCOL_VERSION_ALERT: - case SSL_ERROR_RX_MALFORMED_FINISHED: - case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: - case SSL_ERROR_DECODE_ERROR_ALERT: - case SSL_ERROR_RX_UNKNOWN_ALERT: - return PR_TRUE; - default: - return PR_FALSE; - } -} - /* update blocking direction in case of PR_WOULD_BLOCK_ERROR */ static void nss_update_connecting_state(ssl_connect_state state, void *secret) { @@ -1019,6 +1134,7 @@ static PRStatus nspr_io_close(PRFileDesc *fd) return close_fn(fd); } +/* data might be NULL */ static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir) { NSSInitParameters initparams; @@ -1056,11 +1172,12 @@ static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir) return CURLE_SSL_CACERT_BADFILE; } +/* data might be NULL */ static CURLcode nss_init(struct SessionHandle *data) { char *cert_dir; struct_stat st; - CURLcode rv; + CURLcode result; if(initialized) return CURLE_OK; @@ -1102,14 +1219,15 @@ static CURLcode nss_init(struct SessionHandle *data) nspr_io_methods.close = nspr_io_close; } - rv = nss_init_core(data, cert_dir); - if(rv) - return rv; + result = nss_init_core(data, cert_dir); + if(result) + return result; if(num_enabled_ciphers() == 0) NSS_SetDomesticPolicy(); initialized = 1; + return CURLE_OK; } @@ -1133,20 +1251,22 @@ int Curl_nss_init(void) return 1; } +/* data might be NULL */ CURLcode Curl_nss_force_init(struct SessionHandle *data) { - CURLcode rv; + CURLcode result; if(!nss_initlock) { - failf(data, - "unable to initialize NSS, curl_global_init() should have been " - "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); + if(data) + failf(data, "unable to initialize NSS, curl_global_init() should have " + "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); return CURLE_FAILED_INIT; } PR_Lock(nss_initlock); - rv = nss_init(data); + result = nss_init(data); PR_Unlock(nss_initlock); - return rv; + + return result; } /* Global cleanup */ @@ -1229,10 +1349,8 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) * authentication data from a previous connection. */ SSL_InvalidateSession(connssl->handle); - if(connssl->client_nickname != NULL) { - free(connssl->client_nickname); - connssl->client_nickname = NULL; - } + free(connssl->client_nickname); + connssl->client_nickname = NULL; /* destroy all NSS objects in order to avoid failure of NSS shutdown */ Curl_llist_destroy(connssl->obj_list, NULL); connssl->obj_list = NULL; @@ -1243,16 +1361,6 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) } } -/* - * This function is called when the 'data' struct is going away. Close - * down everything and free all resources! - */ -int Curl_nss_close_all(struct SessionHandle *data) -{ - (void)data; - return 0; -} - /* return true if NSS can provide error code (and possibly msg) for the error */ static bool is_nss_error(CURLcode err) @@ -1295,9 +1403,9 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, const char *capath = data->set.ssl.CApath; if(cafile) { - CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); - if(CURLE_OK != rv) - return rv; + CURLcode result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); + if(result) + return result; } if(capath) { @@ -1342,18 +1450,11 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, static CURLcode nss_init_sslver(SSLVersionRange *sslver, struct SessionHandle *data) { - switch (data->set.ssl.version) { + switch(data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: - sslver->min = SSL_LIBRARY_VERSION_3_0; - if(data->state.ssl_connect_retry) { - infof(data, "TLS disabled due to previous handshake failure\n"); - sslver->max = SSL_LIBRARY_VERSION_3_0; - return CURLE_OK; - } - /* intentional fall-through to default to highest TLS version if possible */ - case CURL_SSLVERSION_TLSv1: + sslver->min = SSL_LIBRARY_VERSION_TLS_1_0; #ifdef SSL_LIBRARY_VERSION_TLS_1_2 sslver->max = SSL_LIBRARY_VERSION_TLS_1_2; #elif defined SSL_LIBRARY_VERSION_TLS_1_1 @@ -1403,12 +1504,8 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, struct SessionHandle *data, CURLcode curlerr) { - SSLVersionRange sslver; PRErrorCode err = 0; - /* reset the flag to avoid an infinite loop */ - data->state.ssl_connect_retry = FALSE; - if(is_nss_error(curlerr)) { /* read NSPR error code */ err = PR_GetError(); @@ -1426,17 +1523,6 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, Curl_llist_destroy(connssl->obj_list, NULL); connssl->obj_list = NULL; - if(connssl->handle - && (SSL_VersionRangeGet(connssl->handle, &sslver) == SECSuccess) - && (sslver.min == SSL_LIBRARY_VERSION_3_0) - && (sslver.max != SSL_LIBRARY_VERSION_3_0) - && isTLSIntoleranceError(err)) { - /* schedule reconnect through Curl_retry_request() */ - data->state.ssl_connect_retry = TRUE; - infof(data, "Error in TLS handshake, trying SSLv3...\n"); - return CURLE_OK; - } - return curlerr; } @@ -1464,27 +1550,13 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - CURLcode curlerr; + CURLcode result; SSLVersionRange sslver = { SSL_LIBRARY_VERSION_TLS_1_0, /* min */ SSL_LIBRARY_VERSION_TLS_1_0 /* max */ }; -#ifdef USE_NGHTTP2 -#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) - unsigned int alpn_protos_len = NGHTTP2_PROTO_VERSION_ID_LEN + - ALPN_HTTP_1_1_LENGTH + 2; - unsigned char alpn_protos[NGHTTP2_PROTO_VERSION_ID_LEN + ALPN_HTTP_1_1_LENGTH - + 2]; - int cur = 0; -#endif -#endif - - - if(connssl->state == ssl_connection_complete) - return CURLE_OK; - connssl->data = data; /* list of all NSS objects we need to destroy in Curl_nss_close() */ @@ -1494,13 +1566,13 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); - curlerr = nss_init(conn->data); - if(CURLE_OK != curlerr) { + result = nss_init(conn->data); + if(result) { PR_Unlock(nss_initlock); goto error; } - curlerr = CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; if(!mod) { char *configstring = aprintf("library=%s name=PEM", pem_library); @@ -1517,7 +1589,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) mod = NULL; } infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.\n", pem_library); + "OpenSSL PEM certificates will not work.\n", pem_library); } } @@ -1560,12 +1632,9 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n"); #endif - /* reset the flag to avoid an infinite loop */ - data->state.ssl_connect_retry = FALSE; - if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { - curlerr = CURLE_SSL_CIPHER; + result = CURLE_SSL_CIPHER; goto error; } } @@ -1587,16 +1656,16 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) if(data->set.ssl.verifypeer) { const CURLcode rv = nss_load_ca_certificates(conn, sockindex); - if(CURLE_OK != rv) { - curlerr = rv; + if(rv) { + result = rv; goto error; } } if(data->set.ssl.CRLfile) { const CURLcode rv = nss_load_crl(data->set.ssl.CRLfile); - if(CURLE_OK != rv) { - curlerr = rv; + if(rv) { + result = rv; goto error; } infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile); @@ -1611,9 +1680,9 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) else { CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY]); - if(CURLE_OK != rv) { + if(rv) { /* failf() is already done in cert_stuff() */ - curlerr = rv; + result = rv; goto error; } } @@ -1626,7 +1695,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { - curlerr = CURLE_SSL_CERTPROBLEM; + result = CURLE_SSL_CERTPROBLEM; goto error; } @@ -1667,42 +1736,57 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } -#ifdef USE_NGHTTP2 - if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { +#ifdef SSL_ENABLE_OCSP_STAPLING + if(data->set.ssl.verifystatus) { + if(SSL_OptionSet(connssl->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) + != SECSuccess) + goto error; + } +#endif + #ifdef SSL_ENABLE_NPN - if(data->set.ssl_enable_npn) { - if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, PR_TRUE) != SECSuccess) - goto error; - } + if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, data->set.ssl_enable_npn + ? PR_TRUE : PR_FALSE) != SECSuccess) + goto error; #endif #ifdef SSL_ENABLE_ALPN - if(data->set.ssl_enable_alpn) { - if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, PR_TRUE) - != SECSuccess) - goto error; - } + if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, data->set.ssl_enable_alpn + ? PR_TRUE : PR_FALSE) != SECSuccess) + goto error; +#endif + +#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ + if(data->set.ssl.falsestart) { + if(SSL_OptionSet(connssl->handle, SSL_ENABLE_FALSE_START, PR_TRUE) + != SECSuccess) + goto error; + + if(SSL_SetCanFalseStartCallback(connssl->handle, CanFalseStartCallback, + conn) != SECSuccess) + goto error; + } #endif #if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) - if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) { - alpn_protos[cur] = NGHTTP2_PROTO_VERSION_ID_LEN; - cur++; - memcpy(&alpn_protos[cur], NGHTTP2_PROTO_VERSION_ID, + if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) { + int cur = 0; + unsigned char protocols[128]; + +#ifdef USE_NGHTTP2 + if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { + protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN; + memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN); cur += NGHTTP2_PROTO_VERSION_ID_LEN; - alpn_protos[cur] = ALPN_HTTP_1_1_LENGTH; - cur++; - memcpy(&alpn_protos[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - - if(SSL_SetNextProtoNego(connssl->handle, alpn_protos, alpn_protos_len) - != SECSuccess) - goto error; - } - else { - infof(data, "SSL, can't negotiate HTTP/2.0 with neither NPN nor ALPN\n"); } #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; + + if(SSL_SetNextProtoNego(connssl->handle, protocols, cur) != SECSuccess) + goto error; } #endif @@ -1718,21 +1802,21 @@ error: if(model) PR_Close(model); - return nss_fail_connect(connssl, data, curlerr); + return nss_fail_connect(connssl, data, result); } static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct SessionHandle *data = conn->data; - CURLcode curlerr = CURLE_SSL_CONNECT_ERROR; + CURLcode result = CURLE_SSL_CONNECT_ERROR; PRUint32 timeout; /* check timeout situation */ const long time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0L) { failf(data, "timed out before SSL handshake"); - curlerr = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; goto error; } @@ -1743,17 +1827,15 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) /* blocking direction is updated by nss_update_connecting_state() */ return CURLE_AGAIN; else if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) - curlerr = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) - curlerr = CURLE_SSL_CACERT; + result = CURLE_SSL_CACERT; goto error; } - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = nss_recv; - conn->send[sockindex] = nss_send; - - display_conn_info(conn, connssl->handle); + result = display_conn_info(conn, connssl->handle); + if(result) + goto error; if(data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret = SECFailure; @@ -1765,8 +1847,8 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) } if(SECFailure == ret) { - infof(data,"SSL certificate issuer check failed\n"); - curlerr = CURLE_SSL_ISSUER_ERROR; + infof(data, "SSL certificate issuer check failed\n"); + result = CURLE_SSL_ISSUER_ERROR; goto error; } else { @@ -1774,10 +1856,15 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) } } + result = cmp_peer_pubkey(connssl, data->set.str[STRING_SSL_PINNEDPUBLICKEY]); + if(result) + /* status already printed */ + goto error; + return CURLE_OK; error: - return nss_fail_connect(connssl, data, curlerr); + return nss_fail_connect(connssl, data, result); } static CURLcode nss_connect_common(struct connectdata *conn, int sockindex, @@ -1786,26 +1873,29 @@ static CURLcode nss_connect_common(struct connectdata *conn, int sockindex, struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct SessionHandle *data = conn->data; const bool blocking = (done == NULL); - CURLcode rv; + CURLcode result; + + if(connssl->state == ssl_connection_complete) + return CURLE_OK; if(connssl->connecting_state == ssl_connect_1) { - rv = nss_setup_connect(conn, sockindex); - if(rv) + result = nss_setup_connect(conn, sockindex); + if(result) /* we do not expect CURLE_AGAIN from nss_setup_connect() */ - return rv; + return result; if(!blocking) { /* in non-blocking mode, set NSS non-blocking mode before handshake */ - rv = nss_set_nonblock(connssl, data); - if(rv) - return rv; + result = nss_set_nonblock(connssl, data); + if(result) + return result; } connssl->connecting_state = ssl_connect_2; } - rv = nss_do_connect(conn, sockindex); - switch(rv) { + result = nss_do_connect(conn, sockindex); + switch(result) { case CURLE_OK: break; case CURLE_AGAIN: @@ -1814,20 +1904,26 @@ static CURLcode nss_connect_common(struct connectdata *conn, int sockindex, return CURLE_OK; /* fall through */ default: - return rv; + return result; } if(blocking) { /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ - rv = nss_set_nonblock(connssl, data); - if(rv) - return rv; + result = nss_set_nonblock(connssl, data); + if(result) + return result; } else /* signal completed SSL handshake */ *done = TRUE; - connssl->connecting_state = ssl_connect_done; + connssl->state = ssl_connection_complete; + conn->recv[sockindex] = nss_recv; + conn->send[sockindex] = nss_send; + + /* ssl_connect_done is never used outside, go back to the initial state */ + connssl->connecting_state = ssl_connect_1; + return CURLE_OK; } @@ -1866,8 +1962,10 @@ static ssize_t nss_send(struct connectdata *conn, /* connection data */ ? CURLE_SSL_CERTPROBLEM : CURLE_SEND_ERROR; } + return -1; } + return rc; /* number of bytes */ } @@ -1897,8 +1995,10 @@ static ssize_t nss_recv(struct connectdata * conn, /* connection data */ ? CURLE_SSL_CERTPROBLEM : CURLE_RECV_ERROR; } + return -1; } + return nread; } @@ -1907,6 +2007,7 @@ size_t Curl_nss_version(char *buffer, size_t size) return snprintf(buffer, size, "NSS/%s", NSS_VERSION); } +/* data might be NULL */ int Curl_nss_seed(struct SessionHandle *data) { /* make sure that NSS is initialized */ @@ -1918,13 +2019,12 @@ int Curl_nss_random(struct SessionHandle *data, unsigned char *entropy, size_t length) { - if(data) - Curl_nss_seed(data); /* Initiate the seed if not already done */ - if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) { - /* no way to signal a failure from here, we have to abort */ - failf(data, "PK11_GenerateRandom() failed, calling abort()..."); - abort(); - } + Curl_nss_seed(data); /* Initiate the seed if not already done */ + + if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) + /* signal a failure */ + return -1; + return 0; } @@ -1935,9 +2035,40 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */ { PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5); unsigned int MD5out; + PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen)); PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len)); PK11_DestroyContext(MD5pw, PR_TRUE); } +void Curl_nss_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t sha256len) +{ + PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); + unsigned int SHA256out; + + PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); + PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); + PK11_DestroyContext(SHA256pw, PR_TRUE); +} + +bool Curl_nss_cert_status_request(void) +{ +#ifdef SSL_ENABLE_OCSP_STAPLING + return TRUE; +#else + return FALSE; +#endif +} + +bool Curl_nss_false_start(void) { +#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ + return TRUE; +#else + return FALSE; +#endif +} + #endif /* USE_NSS */ diff --git a/lib/vtls/nssg.h b/lib/vtls/nssg.h index 311f873..5fd7275 100644 --- a/lib/vtls/nssg.h +++ b/lib/vtls/nssg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -37,10 +37,6 @@ CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn, /* close a SSL connection */ void Curl_nss_close(struct connectdata *conn, int sockindex); -/* tell NSS to close down all open information regarding connections (and - thus session ID caching etc) */ -int Curl_nss_close_all(struct SessionHandle *data); - int Curl_nss_init(void); void Curl_nss_cleanup(void); @@ -60,8 +56,23 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */ unsigned char *md5sum, /* output */ size_t md5len); -/* this backend provides these functions: */ -#define have_curlssl_md5sum 1 +void Curl_nss_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum, /* output */ + size_t sha256len); + +bool Curl_nss_cert_status_request(void); + +bool Curl_nss_false_start(void); + +/* Set the API backend definition to NSS */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_NSS + +/* this backend supports the CAPATH option */ +#define have_curlssl_ca_path 1 + +/* this backend supports CURLOPT_CERTINFO */ +#define have_curlssl_certinfo 1 /* API setup for NSS */ #define curlssl_init Curl_nss_init @@ -71,19 +82,21 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */ /* NSS has its own session ID cache */ #define curlssl_session_free(x) Curl_nop_stmt -#define curlssl_close_all Curl_nss_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_nss_close /* NSS has no shutdown function provided and thus always fail */ -#define curlssl_shutdown(x,y) (x=x, y=y, 1) -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_shutdown(x,y) ((void)x, (void)y, 1) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_nss_version #define curlssl_check_cxn(x) Curl_nss_check_cxn(x) -#define curlssl_data_pending(x,y) (x=x, y=y, 0) +#define curlssl_data_pending(x,y) ((void)x, (void)y, 0) #define curlssl_random(x,y,z) Curl_nss_random(x,y,z) #define curlssl_md5sum(a,b,c,d) Curl_nss_md5sum(a,b,c,d) -#define CURL_SSL_BACKEND CURLSSLBACKEND_NSS +#define curlssl_sha256sum(a,b,c,d) Curl_nss_sha256sum(a,b,c,d) +#define curlssl_cert_status_request() Curl_nss_cert_status_request() +#define curlssl_false_start() Curl_nss_false_start() #endif /* USE_NSS */ #endif /* HEADER_CURL_NSSG_H */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index da92854..90e4c2b 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -32,6 +32,8 @@ #include "curl_setup.h" +#ifdef USE_OPENSSL + #ifdef HAVE_LIMITS_H #include <limits.h> #endif @@ -49,13 +51,9 @@ #include "vtls.h" #include "rawstr.h" #include "hostcheck.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use the internal *printf() functions */ -#include <curl/mprintf.h> - -#ifdef USE_SSLEAY - -#ifdef USE_OPENSSL +#include <openssl/ssl.h> #include <openssl/rand.h> #include <openssl/x509v3.h> #include <openssl/dsa.h> @@ -63,36 +61,29 @@ #include <openssl/err.h> #include <openssl/md5.h> #include <openssl/conf.h> -#else -#include <rand.h> -#include <x509v3.h> -#include <md5.h> +#include <openssl/bn.h> +#include <openssl/rsa.h> + +#ifdef HAVE_OPENSSL_PKCS12_H +#include <openssl/pkcs12.h> +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_IS_BORINGSSL) +#include <openssl/ocsp.h> #endif #include "warnless.h" -#include "curl_memory.h" #include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */ -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" #ifndef OPENSSL_VERSION_NUMBER #error "OPENSSL_VERSION_NUMBER not defined" #endif -#if OPENSSL_VERSION_NUMBER >= 0x0090581fL -#define HAVE_SSL_GET1_SESSION 1 -#else -#undef HAVE_SSL_GET1_SESSION -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x00904100L -#define HAVE_USERDATA_IN_PWD_CALLBACK 1 -#else -#undef HAVE_USERDATA_IN_PWD_CALLBACK -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x00907001L +#if OPENSSL_VERSION_NUMBER >= 0x00907001L && !defined(OPENSSL_IS_BORINGSSL) /* ENGINE_load_private_key() takes four arguments */ #define HAVE_ENGINE_LOAD_FOUR_ARGS #include <openssl/ui.h> @@ -101,18 +92,16 @@ #undef HAVE_ENGINE_LOAD_FOUR_ARGS #endif -#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H) -/* OpenSSL has PKCS 12 support */ +#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && \ + defined(HAVE_OPENSSL_PKCS12_H) && \ + !defined(OPENSSL_IS_BORINGSSL) +/* OpenSSL has PKCS 12 support, BoringSSL does not */ #define HAVE_PKCS12_SUPPORT #else -/* OpenSSL/SSLEay does not have PKCS12 support */ +/* OpenSSL does not have PKCS12 support */ #undef HAVE_PKCS12_SUPPORT #endif -#if OPENSSL_VERSION_NUMBER >= 0x00906001L -#define HAVE_ERR_ERROR_STRING_N 1 -#endif - #if OPENSSL_VERSION_NUMBER >= 0x00909000L #define SSL_METHOD_QUAL const #else @@ -126,15 +115,32 @@ #define X509_STORE_set_flags(x,y) Curl_nop_stmt #endif -#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#ifdef OPENSSL_IS_BORINGSSL +/* BoringSSL has no ERR_remove_state() */ +#define ERR_remove_state(x) +#elif (OPENSSL_VERSION_NUMBER >= 0x10000000L) #define HAVE_ERR_REMOVE_THREAD_STATE 1 #endif -#ifndef HAVE_SSLV2_CLIENT_METHOD +#if !defined(HAVE_SSLV2_CLIENT_METHOD) || \ + OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0+ has no SSLv2 */ #undef OPENSSL_NO_SSL2 /* undef first to avoid compiler warnings */ #define OPENSSL_NO_SSL2 #endif +#if defined(OPENSSL_IS_BORINGSSL) +#define NO_RAND_SEED 1 +/* In BoringSSL OpenSSL_add_all_algorithms does nothing */ +#define OpenSSL_add_all_algorithms() +/* BoringSSL does not have CONF_modules_load_file */ +#define CONF_modules_load_file(a,b,c) +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) || defined(OPENSSL_IS_BORINGSSL) +/* not present in BoringSSL or older OpenSSL */ +#define OPENSSL_load_builtin_modules(x) +#endif + /* * Number of bytes to read from the random number seed file. This must be * a finite value (because some entropy "files" like /dev/urandom have @@ -143,18 +149,8 @@ */ #define RAND_LOAD_LENGTH 1024 -#ifndef HAVE_USERDATA_IN_PWD_CALLBACK -static char global_passwd[64]; -#endif - -static int passwd_callback(char *buf, int num, int encrypting -#ifdef HAVE_USERDATA_IN_PWD_CALLBACK - /* This was introduced in 0.9.4, we can set this - using SSL_CTX_set_default_passwd_cb_userdata() - */ - , void *global_passwd -#endif - ) +static int passwd_callback(char *buf, int num, int encrypting, + void *global_passwd) { DEBUGASSERT(0 == encrypting); @@ -175,6 +171,7 @@ static int passwd_callback(char *buf, int num, int encrypting * pass in an argument that is never used. */ +#ifndef NO_RAND_SEED #ifdef HAVE_RAND_STATUS #define seed_enough(x) rand_enough() static bool rand_enough(void) @@ -259,7 +256,7 @@ static int ossl_seed(struct SessionHandle *data) return nread; } -static int Curl_ossl_seed(struct SessionHandle *data) +static void Curl_ossl_seed(struct SessionHandle *data) { /* we have the "SSL is seeded" boolean static to prevent multiple time-consuming seedings in vain */ @@ -270,8 +267,11 @@ static int Curl_ossl_seed(struct SessionHandle *data) ossl_seed(data); ssl_seeded = TRUE; } - return 0; } +#else +/* BoringSSL needs no seeding */ +#define Curl_ossl_seed(x) +#endif #ifndef SSL_FILETYPE_ENGINE @@ -308,8 +308,7 @@ static int ssl_ui_reader(UI *ui, UI_STRING *uis) case UIT_PROMPT: case UIT_VERIFY: password = (const char*)UI_get0_user_data(ui); - if(NULL != password && - UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD) { + if(password && (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) { UI_set_result(ui, uis, password); return 1; } @@ -327,8 +326,8 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis) switch(UI_get_string_type(uis)) { case UIT_PROMPT: case UIT_VERIFY: - if(NULL != UI_get0_user_data(ui) && - UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD) { + if(UI_get0_user_data(ui) && + (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) { return 1; } default: @@ -350,43 +349,29 @@ int cert_stuff(struct connectdata *conn, int file_type = do_file_type(cert_type); - if(cert_file != NULL || file_type == SSL_FILETYPE_ENGINE) { + if(cert_file || (file_type == SSL_FILETYPE_ENGINE)) { SSL *ssl; X509 *x509; int cert_done = 0; if(data->set.str[STRING_KEY_PASSWD]) { -#ifndef HAVE_USERDATA_IN_PWD_CALLBACK - /* - * If password has been given, we store that in the global - * area (*shudder*) for a while: - */ - size_t len = strlen(data->set.str[STRING_KEY_PASSWD]); - if(len < sizeof(global_passwd)) - memcpy(global_passwd, data->set.str[STRING_KEY_PASSWD], len+1); - else - global_passwd[0] = '\0'; -#else - /* - * We set the password in the callback userdata - */ + /* set the password in the callback userdata */ SSL_CTX_set_default_passwd_cb_userdata(ctx, data->set.str[STRING_KEY_PASSWD]); -#endif /* Set passwd callback: */ SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); } -#define SSL_CLIENT_CERT_ERR \ - "unable to use client certificate (no key found or wrong pass phrase?)" - switch(file_type) { case SSL_FILETYPE_PEM: /* SSL_CTX_use_certificate_chain_file() only works on PEM files */ if(SSL_CTX_use_certificate_chain_file(ctx, cert_file) != 1) { - failf(data, SSL_CLIENT_CERT_ERR); + failf(data, + "could not load PEM client certificate, OpenSSL error %s, " + "(no key found, wrong pass phrase, or wrong file format?)", + ERR_error_string(ERR_get_error(), NULL) ); return 0; } break; @@ -398,7 +383,10 @@ int cert_stuff(struct connectdata *conn, if(SSL_CTX_use_certificate_file(ctx, cert_file, file_type) != 1) { - failf(data, SSL_CLIENT_CERT_ERR); + failf(data, + "could not load ASN1 client certificate, OpenSSL error %s, " + "(no key found, wrong pass phrase, or wrong file format?)", + ERR_error_string(ERR_get_error(), NULL) ); return 0; } break; @@ -464,7 +452,7 @@ int cert_stuff(struct connectdata *conn, STACK_OF(X509) *ca = NULL; int i; - f = fopen(cert_file,"rb"); + f = fopen(cert_file, "rb"); if(!f) { failf(data, "could not open PKCS12 file '%s'", cert_file); return 0; @@ -473,7 +461,7 @@ int cert_stuff(struct connectdata *conn, fclose(f); if(!p12) { - failf(data, "error reading PKCS12 file '%s'", cert_file ); + failf(data, "error reading PKCS12 file '%s'", cert_file); return 0; } @@ -491,7 +479,9 @@ int cert_stuff(struct connectdata *conn, PKCS12_free(p12); if(SSL_CTX_use_certificate(ctx, x509) != 1) { - failf(data, SSL_CLIENT_CERT_ERR); + failf(data, + "could not load PKCS12 client certificate, OpenSSL error %s", + ERR_error_string(ERR_get_error(), NULL) ); goto fail; } @@ -556,7 +546,7 @@ int cert_stuff(struct connectdata *conn, case SSL_FILETYPE_PEM: if(cert_done) break; - if(key_file == NULL) + if(!key_file) /* cert & key can only be in PEM case in the same file */ key_file=cert_file; case SSL_FILETYPE_ASN1: @@ -574,7 +564,7 @@ int cert_stuff(struct connectdata *conn, #ifdef HAVE_ENGINE_LOAD_FOUR_ARGS UI_METHOD *ui_method = UI_create_method((char *)"cURL user interface"); - if(NULL == ui_method) { + if(!ui_method) { failf(data, "unable do create OpenSSL user-interface method"); return 0; } @@ -585,7 +575,7 @@ int cert_stuff(struct connectdata *conn, #endif /* the typecast below was added to please mingw32 */ priv_key = (EVP_PKEY *) - ENGINE_load_private_key(data->state.engine,key_file, + ENGINE_load_private_key(data->state.engine, key_file, #ifdef HAVE_ENGINE_LOAD_FOUR_ARGS ui_method, #endif @@ -626,8 +616,8 @@ int cert_stuff(struct connectdata *conn, } ssl=SSL_new(ctx); - if(NULL == ssl) { - failf(data,"unable to create an SSL structure"); + if(!ssl) { + failf(data, "unable to create an SSL structure"); return 0; } @@ -635,9 +625,9 @@ int cert_stuff(struct connectdata *conn, /* This version was provided by Evan Jordan and is supposed to not leak memory as the previous version: */ - if(x509 != NULL) { + if(x509) { EVP_PKEY *pktmp = X509_get_pubkey(x509); - EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl)); + EVP_PKEY_copy_parameters(pktmp, SSL_get_privatekey(ssl)); EVP_PKEY_free(pktmp); } @@ -653,10 +643,6 @@ int cert_stuff(struct connectdata *conn, failf(data, "Private key does not match the certificate public key"); return 0; } -#ifndef HAVE_USERDATA_IN_PWD_CALLBACK - /* erase it now */ - memset(global_passwd, 0, sizeof(global_passwd)); -#endif } return 1; } @@ -691,36 +677,17 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) #endif } -static -int cert_verify_callback(int ok, X509_STORE_CTX *ctx) -{ - X509 *err_cert; - char buf[256]; - - err_cert=X509_STORE_CTX_get_current_cert(ctx); - (void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); - return ok; -} - /* Return error string for last OpenSSL error */ static char *SSL_strerror(unsigned long error, char *buf, size_t size) { -#ifdef HAVE_ERR_ERROR_STRING_N /* OpenSSL 0.9.6 and later has a function named - ERRO_error_string_n() that takes the size of the buffer as a + ERR_error_string_n() that takes the size of the buffer as a third argument */ ERR_error_string_n(error, buf, size); -#else - (void) size; - ERR_error_string(error, buf); -#endif return buf; } -#endif /* USE_SSLEAY */ - -#ifdef USE_SSLEAY /** * Global SSL init * @@ -729,6 +696,8 @@ static char *SSL_strerror(unsigned long error, char *buf, size_t size) */ int Curl_ossl_init(void) { + OPENSSL_load_builtin_modules(); + #ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES ENGINE_load_builtin_engines(); #endif @@ -749,17 +718,19 @@ int Curl_ossl_init(void) calls CONF_modules_load_file() and we use that instead and we ignore its return code! */ - (void)CONF_modules_load_file(NULL, NULL, - CONF_MFLAGS_DEFAULT_SECTION| - CONF_MFLAGS_IGNORE_MISSING_FILE); + /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and + 0.9.8e */ +#ifndef CONF_MFLAGS_DEFAULT_SECTION +#define CONF_MFLAGS_DEFAULT_SECTION 0x0 +#endif + + CONF_modules_load_file(NULL, NULL, + CONF_MFLAGS_DEFAULT_SECTION| + CONF_MFLAGS_IGNORE_MISSING_FILE); return 1; } -#endif /* USE_SSLEAY */ - -#ifdef USE_SSLEAY - /* Global cleanup */ void Curl_ossl_cleanup(void) { @@ -814,7 +785,7 @@ int Curl_ossl_check_cxn(struct connectdata *conn) */ CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine) { -#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_ENGINE_H) ENGINE *e; #if OPENSSL_VERSION_NUMBER >= 0x00909000L @@ -862,7 +833,7 @@ CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data) #ifdef HAVE_OPENSSL_ENGINE_H if(data->state.engine) { if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) { - infof(data,"set default crypto engine '%s'\n", + infof(data, "set default crypto engine '%s'\n", ENGINE_get_id(data->state.engine)); } else { @@ -882,7 +853,7 @@ CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data) struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data) { struct curl_slist *list = NULL; -#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_ENGINE_H) struct curl_slist *beg; ENGINE *e; @@ -1031,7 +1002,7 @@ void Curl_ossl_session_free(void *ptr) * This function is called when the 'data' struct is going away. Close * down everything and free all resources! */ -int Curl_ossl_close_all(struct SessionHandle *data) +void Curl_ossl_close_all(struct SessionHandle *data) { #ifdef HAVE_OPENSSL_ENGINE_H if(data->state.engine) { @@ -1042,7 +1013,6 @@ int Curl_ossl_close_all(struct SessionHandle *data) #else (void)data; #endif - return 0; } static int asn1_output(const ASN1_UTCTIME *tm, @@ -1052,7 +1022,7 @@ static int asn1_output(const ASN1_UTCTIME *tm, const char *asn1_string; int gmt=FALSE; int i; - int year=0,month=0,day=0,hour=0,minute=0,second=0; + int year=0, month=0, day=0, hour=0, minute=0, second=0; i=tm->length; asn1_string=(const char *)tm->data; @@ -1112,8 +1082,7 @@ static int asn1_output(const ASN1_UTCTIME *tm, in the certificate and must exactly match the IP in the URI. */ -static CURLcode verifyhost(struct connectdata *conn, - X509 *server_cert) +static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) { int matched = -1; /* -1 is no alternative match yet, 1 means match and 0 means mismatch */ @@ -1126,7 +1095,7 @@ static CURLcode verifyhost(struct connectdata *conn, #else struct in_addr addr; #endif - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -1207,19 +1176,19 @@ static CURLcode verifyhost(struct connectdata *conn, infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname); failf(data, "SSL: no alternative certificate subject name matches " "target host name '%s'", conn->host.dispname); - res = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; } 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 j, i=-1; /* 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) ; + X509_NAME *name = X509_get_subject_name(server_cert); if(name) while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0) i=j; @@ -1229,7 +1198,8 @@ static CURLcode verifyhost(struct connectdata *conn, UTF8 etc. */ if(i>=0) { - ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i)); + ASN1_STRING *tmp = + X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input is already UTF-8 encoded. We check for this case and copy the raw @@ -1254,7 +1224,7 @@ static CURLcode verifyhost(struct connectdata *conn, /* there was a terminating zero before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); - res = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; } } } @@ -1271,18 +1241,18 @@ static CURLcode verifyhost(struct connectdata *conn, } } - if(res) + if(result) /* error already detected, pass through */ ; else if(!peer_CN) { failf(data, "SSL: unable to obtain common name from peer certificate"); - res = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) { failf(data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, conn->host.dispname); - res = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; } else { infof(data, "\t common name: %s (matched)\n", peer_CN); @@ -1290,9 +1260,138 @@ static CURLcode verifyhost(struct connectdata *conn, if(peer_CN) OPENSSL_free(peer_CN); } - return res; + + return result; +} + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_IS_BORINGSSL) +static CURLcode verifystatus(struct connectdata *conn, + struct ssl_connect_data *connssl) +{ + int i, ocsp_status; + const unsigned char *p; + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + + OCSP_RESPONSE *rsp = NULL; + OCSP_BASICRESP *br = NULL; + X509_STORE *st = NULL; + STACK_OF(X509) *ch = NULL; + + long len = SSL_get_tlsext_status_ocsp_resp(connssl->handle, &p); + + if(!p) { + failf(data, "No OCSP response received"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if(!rsp) { + failf(data, "Invalid OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + ocsp_status = OCSP_response_status(rsp); + if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + failf(data, "Invalid OCSP response status: %s (%d)", + OCSP_response_status_str(ocsp_status), ocsp_status); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + br = OCSP_response_get1_basic(rsp); + if(!br) { + failf(data, "Invalid OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + ch = SSL_get_peer_cert_chain(connssl->handle); + st = SSL_CTX_get_cert_store(connssl->ctx); + +#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ + defined(LIBRESSL_VERSION_NUMBER)) + /* The authorized responder cert in the OCSP response MUST be signed by the + peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert, + no problem, but if it's an intermediate cert OpenSSL has a bug where it + expects this issuer to be present in the chain embedded in the OCSP + response. So we add it if necessary. */ + + /* First make sure the peer cert chain includes both a peer and an issuer, + and the OCSP response contains a responder cert. */ + if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) { + X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1); + + /* Find issuer of responder cert and add it to the OCSP response chain */ + for(i = 0; i < sk_X509_num(ch); i++) { + X509 *issuer = sk_X509_value(ch, i); + if(X509_check_issued(issuer, responder) == X509_V_OK) { + if(!OCSP_basic_add1_cert(br, issuer)) { + failf(data, "Could not add issuer cert to OCSP response"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + } + } + } +#endif + + if(OCSP_basic_verify(br, ch, st, 0) <= 0) { + failf(data, "OCSP response verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + for(i = 0; i < OCSP_resp_count(br); i++) { + int cert_status, crl_reason; + OCSP_SINGLERESP *single = NULL; + + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + + if(!(single = OCSP_resp_get0(br, i))) + continue; + + cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, + &thisupd, &nextupd); + + if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { + failf(data, "OCSP response has expired"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + + infof(data, "SSL certificate status: %s (%d)\n", + OCSP_cert_status_str(cert_status), cert_status); + + switch(cert_status) { + case V_OCSP_CERTSTATUS_GOOD: + break; + + case V_OCSP_CERTSTATUS_REVOKED: + result = CURLE_SSL_INVALIDCERTSTATUS; + + failf(data, "SSL certificate revocation reason: %s (%d)", + OCSP_crl_reason_str(crl_reason), crl_reason); + goto end; + + case V_OCSP_CERTSTATUS_UNKNOWN: + result = CURLE_SSL_INVALIDCERTSTATUS; + goto end; + } + } + +end: + if(br) OCSP_BASICRESP_free(br); + OCSP_RESPONSE_free(rsp); + + return result; } -#endif /* USE_SSLEAY */ +#endif + +#endif /* USE_OPENSSL */ /* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions and thus this cannot be done there. */ @@ -1300,6 +1399,7 @@ static CURLcode verifyhost(struct connectdata *conn, static const char *ssl_msg_type(int ssl_ver, int msg) { +#ifdef SSL2_VERSION_MAJOR if(ssl_ver == SSL2_VERSION_MAJOR) { switch (msg) { case SSL2_MT_ERROR: @@ -1322,7 +1422,9 @@ static const char *ssl_msg_type(int ssl_ver, int msg) return "Client CERT"; } } - else if(ssl_ver == SSL3_VERSION_MAJOR) { + else +#endif + if(ssl_ver == SSL3_VERSION_MAJOR) { switch (msg) { case SSL3_MT_HELLO_REQUEST: return "Hello request"; @@ -1330,8 +1432,12 @@ static const char *ssl_msg_type(int ssl_ver, int msg) return "Client hello"; case SSL3_MT_SERVER_HELLO: return "Server hello"; +#ifdef SSL3_MT_NEWSESSION_TICKET + case SSL3_MT_NEWSESSION_TICKET: + return "Newsession Ticket"; +#endif case SSL3_MT_CERTIFICATE: - return "CERT"; + return "Certificate"; case SSL3_MT_SERVER_KEY_EXCHANGE: return "Server key exchange"; case SSL3_MT_CLIENT_KEY_EXCHANGE: @@ -1344,6 +1450,10 @@ static const char *ssl_msg_type(int ssl_ver, int msg) return "CERT verify"; case SSL3_MT_FINISHED: return "Finished"; +#ifdef SSL3_MT_CERTIFICATE_STATUS + case SSL3_MT_CERTIFICATE_STATUS: + return "Certificate Status"; +#endif } } return "Unknown"; @@ -1351,12 +1461,22 @@ static const char *ssl_msg_type(int ssl_ver, int msg) static const char *tls_rt_type(int type) { - return ( - type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " : - type == SSL3_RT_ALERT ? "TLS alert, " : - type == SSL3_RT_HANDSHAKE ? "TLS handshake, " : - type == SSL3_RT_APPLICATION_DATA ? "TLS app data, " : - "TLS Unknown, "); + switch(type) { +#ifdef SSL3_RT_HEADER + case SSL3_RT_HEADER: + return "TLS header"; +#endif + case SSL3_RT_CHANGE_CIPHER_SPEC: + return "TLS change cipher"; + case SSL3_RT_ALERT: + return "TLS alert"; + case SSL3_RT_HANDSHAKE: + return "TLS handshake"; + case SSL3_RT_APPLICATION_DATA: + return "TLS app data"; + default: + return "TLS Unknown"; + } } @@ -1364,38 +1484,77 @@ static const char *tls_rt_type(int type) * Our callback from the SSL/TLS layers. */ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, - const void *buf, size_t len, const SSL *ssl, - struct connectdata *conn) + const void *buf, size_t len, SSL *ssl, + void *userp) { struct SessionHandle *data; const char *msg_name, *tls_rt_name; char ssl_buf[1024]; - int ver, msg_type, txt_len; + char unknown[32]; + int msg_type, txt_len; + const char *verstr = NULL; + struct connectdata *conn = userp; if(!conn || !conn->data || !conn->data->set.fdebug || (direction != 0 && direction != 1)) return; data = conn->data; - ssl_ver >>= 8; - ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' : - ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?'); - /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL - * always pass-up content-type as 0. But the interesting message-type - * is at 'buf[0]'. - */ - if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0) - tls_rt_name = tls_rt_type(content_type); - else - tls_rt_name = ""; + switch(ssl_ver) { +#ifdef SSL2_VERSION /* removed in recent versions */ + case SSL2_VERSION: + verstr = "SSLv2"; + break; +#endif +#ifdef SSL3_VERSION + case SSL3_VERSION: + verstr = "SSLv3"; + break; +#endif + case TLS1_VERSION: + verstr = "TLSv1.0"; + break; +#ifdef TLS1_1_VERSION + case TLS1_1_VERSION: + verstr = "TLSv1.1"; + break; +#endif +#ifdef TLS1_2_VERSION + case TLS1_2_VERSION: + verstr = "TLSv1.2"; + break; +#endif + case 0: + break; + default: + snprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); + verstr = unknown; + break; + } + + if(ssl_ver) { + /* the info given when the version is zero is not that useful for us */ + + ssl_ver >>= 8; /* check the upper 8 bits only below */ - msg_type = *(char*)buf; - msg_name = ssl_msg_type(ssl_ver, msg_type); + /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL + * always pass-up content-type as 0. But the interesting message-type + * is at 'buf[0]'. + */ + if(ssl_ver == SSL3_VERSION_MAJOR && content_type) + tls_rt_name = tls_rt_type(content_type); + else + tls_rt_name = ""; - txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n", - ver, tls_rt_name, msg_name, msg_type); - Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL); + msg_type = *(char*)buf; + msg_name = ssl_msg_type(ssl_ver, msg_type); + + txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n", + verstr, direction?"OUT":"IN", + tls_rt_name, msg_name, msg_type); + Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL); + } Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL); @@ -1403,7 +1562,7 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, } #endif -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL /* ====================================================== */ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME @@ -1412,26 +1571,44 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, # define use_sni(x) Curl_nop_stmt #endif -#ifdef USE_NGHTTP2 - +/* Check for OpenSSL 1.0.2 which has ALPN support. */ #undef HAS_ALPN -#if defined(HAVE_SSL_CTX_SET_ALPN_PROTOS) && \ - defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) +#if OPENSSL_VERSION_NUMBER >= 0x10002000L \ + && !defined(OPENSSL_NO_TLSEXT) # define HAS_ALPN 1 #endif -#if !defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || \ - defined(OPENSSL_NO_NEXTPROTONEG) -# if !defined(HAS_ALPN) -# error http2 builds require OpenSSL with NPN or ALPN support -# endif +/* Check for OpenSSL 1.0.1 which has NPN support. */ +#undef HAS_NPN +#if OPENSSL_VERSION_NUMBER >= 0x10001000L \ + && !defined(OPENSSL_NO_TLSEXT) \ + && !defined(OPENSSL_NO_NEXTPROTONEG) +# define HAS_NPN 1 #endif +#ifdef HAS_NPN /* * in is a list of lenght prefixed strings. this function has to select * the protocol we want to use from the list and write its string into out. */ + +static int +select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const char *key, unsigned int keylen) +{ + unsigned int i; + for(i = 0; i + keylen <= inlen; i += in[i] + 1) { + if(memcmp(&in[i + 1], key, keylen) == 0) { + *out = (unsigned char *) &in[i + 1]; + *outlen = in[i]; + return 0; + } + } + return -1; +} + static int select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, @@ -1439,37 +1616,43 @@ select_next_proto_cb(SSL *ssl, void *arg) { struct connectdata *conn = (struct connectdata*) arg; - int retval = nghttp2_select_next_protocol(out, outlen, in, inlen); + (void)ssl; - if(retval == 1) { +#ifdef USE_NGHTTP2 + if(conn->data->set.httpversion == CURL_HTTP_VERSION_2_0 && + !select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN)) { infof(conn->data, "NPN, negotiated HTTP2 (%s)\n", NGHTTP2_PROTO_VERSION_ID); - conn->negnpn = NPN_HTTP2; + conn->negnpn = CURL_HTTP_VERSION_2_0; + return SSL_TLSEXT_ERR_OK; } - else if(retval == 0) { +#endif + + if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1, + ALPN_HTTP_1_1_LENGTH)) { infof(conn->data, "NPN, negotiated HTTP1.1\n"); - conn->negnpn = NPN_HTTP1_1; - } - else { - infof(conn->data, "NPN, no overlap, use HTTP1.1\n", - NGHTTP2_PROTO_VERSION_ID); - *out = (unsigned char*)"http/1.1"; - *outlen = sizeof("http/1.1") - 1; - conn->negnpn = NPN_HTTP1_1; + conn->negnpn = CURL_HTTP_VERSION_1_1; + return SSL_TLSEXT_ERR_OK; } + infof(conn->data, "NPN, no overlap, use HTTP1.1\n"); + *out = (unsigned char *)ALPN_HTTP_1_1; + *outlen = ALPN_HTTP_1_1_LENGTH; + conn->negnpn = CURL_HTTP_VERSION_1_1; + return SSL_TLSEXT_ERR_OK; } -#endif +#endif /* HAS_NPN */ static const char * -get_ssl_version_txt(SSL_SESSION *session) +get_ssl_version_txt(SSL *ssl) { - if(NULL == session) + if(!ssl) return ""; - switch(session->ssl_version) { + switch(SSL_version(ssl)) { #if OPENSSL_VERSION_NUMBER >= 0x1000100FL case TLS1_2_VERSION: return "TLSv1.2"; @@ -1486,17 +1669,14 @@ get_ssl_version_txt(SSL_SESSION *session) return "unknown"; } - -static CURLcode -ossl_connect_step1(struct connectdata *conn, - int sockindex) +static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; char *ciphers; struct SessionHandle *data = conn->data; - SSL_METHOD_QUAL SSL_METHOD *req_method=NULL; - void *ssl_sessionid=NULL; - X509_LOOKUP *lookup=NULL; + SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; + void *ssl_sessionid = NULL; + X509_LOOKUP *lookup = NULL; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; long ctx_options; @@ -1508,9 +1688,6 @@ ossl_connect_step1(struct connectdata *conn, struct in_addr addr; #endif #endif -#ifdef HAS_ALPN - unsigned char protocols[128]; -#endif DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); @@ -1529,7 +1706,12 @@ ossl_connect_step1(struct connectdata *conn, case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: /* it will be handled later with the context options */ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) + req_method = TLS_client_method(); +#else req_method = SSLv23_client_method(); +#endif use_sni(TRUE); break; case CURL_SSLVERSION_SSLv2: @@ -1546,6 +1728,10 @@ ossl_connect_step1(struct connectdata *conn, break; #endif case CURL_SSLVERSION_SSLv3: +#ifdef OPENSSL_NO_SSL3_METHOD + failf(data, "OpenSSL was built without SSLv3 support"); + return CURLE_NOT_BUILT_IN; +#else #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) return CURLE_SSL_CONNECT_ERROR; @@ -1553,6 +1739,7 @@ ossl_connect_step1(struct connectdata *conn, req_method = SSLv3_client_method(); use_sni(FALSE); break; +#endif } if(connssl->ctx) @@ -1571,16 +1758,9 @@ ossl_connect_step1(struct connectdata *conn, #ifdef SSL_CTRL_SET_MSG_CALLBACK if(data->set.fdebug && data->set.verbose) { - /* the SSL trace callback is only used for verbose logging so we only - inform about failures of setting it */ - if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK, - (void (*)(void))ssl_tls_trace)) { - infof(data, "SSL: couldn't set callback!\n"); - } - else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, - conn)) { - infof(data, "SSL: couldn't set callback argument!\n"); - } + /* the SSL trace callback is only used for verbose logging */ + SSL_CTX_set_msg_callback(connssl->ctx, ssl_tls_trace); + SSL_CTX_set_msg_callback_arg(connssl->ctx, conn); } #endif @@ -1593,7 +1773,7 @@ ossl_connect_step1(struct connectdata *conn, The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to disable "rfc4507bis session ticket support". rfc4507bis was later turned - into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077 + into the proper RFC5077 it seems: https://tools.ietf.org/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 @@ -1613,7 +1793,7 @@ ossl_connect_step1(struct connectdata *conn, this option regardless of OpenSSL version and SSL_OP_ALL definition. OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability - (http://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to + (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around despite the fact that SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit @@ -1643,17 +1823,12 @@ ossl_connect_step1(struct connectdata *conn, #endif switch(data->set.ssl.version) { - case CURL_SSLVERSION_DEFAULT: - ctx_options |= SSL_OP_NO_SSLv2; + case CURL_SSLVERSION_SSLv3: #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { infof(data, "Set version TLSv1.x for SRP authorisation\n"); - ctx_options |= SSL_OP_NO_SSLv3; } #endif - break; - - case CURL_SSLVERSION_SSLv3: ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_TLSv1; #if OPENSSL_VERSION_NUMBER >= 0x1000100FL @@ -1662,6 +1837,7 @@ ossl_connect_step1(struct connectdata *conn, #endif break; + case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3; @@ -1710,33 +1886,36 @@ ossl_connect_step1(struct connectdata *conn, SSL_CTX_set_options(connssl->ctx, ctx_options); -#ifdef USE_NGHTTP2 - if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { - if(data->set.ssl_enable_npn) { - SSL_CTX_set_next_proto_select_cb(connssl->ctx, select_next_proto_cb, - conn); - } +#ifdef HAS_NPN + if(data->set.ssl_enable_npn) + SSL_CTX_set_next_proto_select_cb(connssl->ctx, select_next_proto_cb, conn); +#endif #ifdef HAS_ALPN - if(data->set.ssl_enable_alpn) { - protocols[0] = NGHTTP2_PROTO_VERSION_ID_LEN; - memcpy(&protocols[1], NGHTTP2_PROTO_VERSION_ID, - NGHTTP2_PROTO_VERSION_ID_LEN); + if(data->set.ssl_enable_alpn) { + int cur = 0; + unsigned char protocols[128]; - protocols[NGHTTP2_PROTO_VERSION_ID_LEN+1] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[NGHTTP2_PROTO_VERSION_ID_LEN+2], ALPN_HTTP_1_1, - ALPN_HTTP_1_1_LENGTH); - - /* expects length prefixed preference ordered list of protocols in wire - * format - */ - SSL_CTX_set_alpn_protos(connssl->ctx, protocols, - NGHTTP2_PROTO_VERSION_ID_LEN + ALPN_HTTP_1_1_LENGTH + 2); +#ifdef USE_NGHTTP2 + if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { + protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN; - infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID, - ALPN_HTTP_1_1); + memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN); + cur += NGHTTP2_PROTO_VERSION_ID_LEN; + infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } #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\n", ALPN_HTTP_1_1); + + /* expects length prefixed preference ordered list of protocols in wire + * format + */ + SSL_CTX_set_alpn_protos(connssl->ctx, protocols, cur); } #endif @@ -1759,6 +1938,7 @@ ossl_connect_step1(struct connectdata *conn, failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } + infof(data, "Cipher selection: %s\n", ciphers); #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { @@ -1768,7 +1948,7 @@ ossl_connect_step1(struct connectdata *conn, failf(data, "Unable to set SRP user name"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!SSL_CTX_set_srp_password(connssl->ctx,data->set.ssl.password)) { + if(!SSL_CTX_set_srp_password(connssl->ctx, data->set.ssl.password)) { failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -1790,7 +1970,7 @@ ossl_connect_step1(struct connectdata *conn, data->set.str[STRING_SSL_CAPATH])) { if(data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ - failf(data,"error setting certificate verify locations:\n" + failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", data->set.str[STRING_SSL_CAFILE]? data->set.str[STRING_SSL_CAFILE]: "none", @@ -1824,9 +2004,9 @@ ossl_connect_step1(struct connectdata *conn, lookup=X509_STORE_add_lookup(SSL_CTX_get_cert_store(connssl->ctx), X509_LOOKUP_file()); if(!lookup || - (!X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE], + (!X509_load_crl_file(lookup, data->set.str[STRING_SSL_CRLFILE], X509_FILETYPE_PEM)) ) { - failf(data,"error loading CRL file: %s", + failf(data, "error loading CRL file: %s", data->set.str[STRING_SSL_CRLFILE]); return CURLE_SSL_CRL_BADFILE; } @@ -1841,21 +2021,35 @@ ossl_connect_step1(struct connectdata *conn, data->set.str[STRING_SSL_CRLFILE]: "none"); } + /* Try building a chain using issuers in the trusted store first to avoid + problems with server-sent legacy intermediates. + Newer versions of OpenSSL do alternate chain checking by default which + gives us the same fix without as much of a performance hit (slight), so we + prefer that if available. + https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest + */ +#if defined(X509_V_FLAG_TRUSTED_FIRST) && !defined(X509_V_FLAG_NO_ALT_CHAINS) + if(data->set.ssl.verifypeer) { + X509_STORE_set_flags(SSL_CTX_get_cert_store(connssl->ctx), + X509_V_FLAG_TRUSTED_FIRST); + } +#endif + /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(connssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, - cert_verify_callback); + NULL); /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { - retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx, - data->set.ssl.fsslctxp); - if(retcode) { - failf(data,"error signaled by ssl ctx callback"); - return retcode; + result = (*data->set.ssl.fsslctx)(data, connssl->ctx, + data->set.ssl.fsslctxp); + if(result) { + failf(data, "error signaled by ssl ctx callback"); + return result; } } @@ -1867,6 +2061,13 @@ ossl_connect_step1(struct connectdata *conn, failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } + +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_IS_BORINGSSL) + if(data->set.ssl.verifystatus) + SSL_set_tlsext_status_type(connssl->handle, TLSEXT_STATUSTYPE_ocsp); +#endif + SSL_set_connect_state(connssl->handle); connssl->server_cert = 0x0; @@ -1887,7 +2088,7 @@ ossl_connect_step1(struct connectdata *conn, /* we got a session id, use it! */ if(!SSL_set_session(connssl->handle, ssl_sessionid)) { failf(data, "SSL: SSL_set_session failed: %s", - ERR_error_string(ERR_get_error(),NULL)); + ERR_error_string(ERR_get_error(), NULL)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ @@ -1897,16 +2098,16 @@ ossl_connect_step1(struct connectdata *conn, /* pass the raw socket into the SSL layers */ if(!SSL_set_fd(connssl->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed: %s", - ERR_error_string(ERR_get_error(),NULL)); + ERR_error_string(ERR_get_error(), NULL)); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_2; + return CURLE_OK; } -static CURLcode -ossl_connect_step2(struct connectdata *conn, int sockindex) +static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; int err; @@ -1936,10 +2137,9 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) else { /* untreated error */ unsigned long errdetail; - char error_buffer[256]; /* OpenSSL documents that this must be at least - 256 bytes long. */ - CURLcode rc; - const char *cert_problem = NULL; + char error_buffer[256]=""; /* OpenSSL documents that this must be at + least 256 bytes long. */ + CURLcode result; long lerr; connssl->connecting_state = ssl_connect_2; /* the connection failed, @@ -1962,7 +2162,7 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) SSL routines: SSL3_GET_SERVER_CERTIFICATE: certificate verify failed */ - rc = CURLE_SSL_CACERT; + result = CURLE_SSL_CACERT; lerr = SSL_get_verify_result(connssl->handle); if(lerr != X509_V_OK) { @@ -1971,12 +2171,13 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) X509_verify_cert_error_string(lerr)); } else - cert_problem = "SSL certificate problem, verify that the CA cert is" - " OK."; - + /* strcpy() is fine here as long as the string fits within + error_buffer */ + strcpy(error_buffer, + "SSL certificate problem, check your CA cert"); break; default: - rc = CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; SSL_strerror(errdetail, error_buffer, sizeof(error_buffer)); break; } @@ -1987,15 +2188,16 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) * (RST connection etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ - if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { + if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { failf(data, "Unknown SSL protocol error in connection to %s:%ld ", conn->host.name, conn->remote_port); - return rc; + return result; } + /* Could be a CERT problem */ + failf(data, "%s", error_buffer); - failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); - return rc; + return result; } } else { @@ -2003,9 +2205,9 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) connssl->connecting_state = ssl_connect_3; /* Informational message */ - infof (data, "SSL connection using %s / %s\n", - get_ssl_version_txt(SSL_get_session(connssl->handle)), - SSL_get_cipher(connssl->handle)); + infof(data, "SSL connection using %s / %s\n", + get_ssl_version_txt(connssl->handle), + SSL_get_cipher(connssl->handle)); #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was @@ -2018,18 +2220,20 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) if(len != 0) { infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol); +#ifdef USE_NGHTTP2 if(len == NGHTTP2_PROTO_VERSION_ID_LEN && - memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len) == 0) { - conn->negnpn = NPN_HTTP2; + !memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len)) { + conn->negnpn = CURL_HTTP_VERSION_2_0; } - else if(len == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1, - neg_protocol, ALPN_HTTP_1_1_LENGTH) == 0) { - conn->negnpn = NPN_HTTP1_1; + else +#endif + if(len == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) { + conn->negnpn = CURL_HTTP_VERSION_1_1; } } - else { + else infof(data, "ALPN, server did not agree to a protocol\n"); - } } #endif @@ -2082,7 +2286,7 @@ static void pubkey_show(struct SessionHandle *data, #define print_pubkey_BN(_type, _name, _num) \ do { \ - if(pubkey->pkey._type->_name != NULL) { \ + if(pubkey->pkey._type->_name) { \ int len = BN_num_bytes(pubkey->pkey._type->_name); \ if(len < CERTBUFFERSIZE) { \ BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)bufp); \ @@ -2123,7 +2327,7 @@ static int X509V3_ext(struct SessionHandle *data, X509_EXTENSION_get_critical(ext)?"(critical)":""); if(!X509V3_EXT_print(bio_out, ext, 0, 0)) - M_ASN1_OCTET_STRING_print(bio_out, ext->value); + ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); BIO_get_mem_ptr(bio_out, &biomem); @@ -2160,6 +2364,7 @@ static void X509_signature(struct SessionHandle *data, char buf[1024]; char *ptr = buf; int i; + for(i=0; i<sig->length; i++) ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]); @@ -2182,7 +2387,6 @@ static void dumpcert(struct SessionHandle *data, X509 *x, int numcert) "Cert", biomem->data, biomem->length); BIO_free(bio_out); - } /* @@ -2196,6 +2400,7 @@ static CURLcode get_cert_chain(struct connectdata *conn, struct ssl_connect_data *connssl) { + CURLcode result; STACK_OF(X509) *sk; int i; char *bufp; @@ -2213,9 +2418,11 @@ static CURLcode get_cert_chain(struct connectdata *conn, } numcerts = sk_X509_num(sk); - if(Curl_ssl_init_certinfo(data, numcerts)) { + + result = Curl_ssl_init_certinfo(data, numcerts); + if(result) { free(bufp); - return CURLE_OUT_OF_MEMORY; + return result; } infof(data, "--- Certificate chain\n"); @@ -2250,28 +2457,22 @@ static CURLcode get_cert_chain(struct connectdata *conn, Curl_ssl_push_certinfo(data, i, "Version", bufp); /* hex */ num=X509_get_serialNumber(x); - if(num->length <= 4) { - value = ASN1_INTEGER_get(num); - infof(data," Serial Number: %ld (0x%lx)\n", value, value); - snprintf(bufp, CERTBUFFERSIZE, "%lx", value); - } - else { + { int left = CERTBUFFERSIZE; ptr = bufp; - *ptr++ = 0; - if(num->type == V_ASN1_NEG_INTEGER) + if(num->type == V_ASN1_NEG_INTEGER) { *ptr++='-'; + left--; + } - for(j=0; (j<num->length) && (left>=4); j++) { - /* TODO: length restrictions */ - snprintf(ptr, 3, "%02x%c",num->data[j], - ((j+1 == num->length)?'\n':':')); - ptr += 3; - left-=4; + for(j=0; (j<num->length) && (left>=3); j++) { + snprintf(ptr, left, "%02x", num->data[j]); + ptr += 2; + left -= 2; } if(num->length) - infof(data," Serial Number: %s\n", bufp); + infof(data, " Serial Number: %s\n", bufp); else bufp[0]=0; } @@ -2357,6 +2558,65 @@ static CURLcode get_cert_chain(struct connectdata *conn, } /* + * Heavily modified from: + * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL + */ +static CURLcode pkp_pin_peer_pubkey(X509* cert, const char *pinnedpubkey) +{ + /* Scratch */ + int len1 = 0, len2 = 0; + unsigned char *buff1 = NULL, *temp = NULL; + + /* Result is returned to caller */ + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + + /* if a path wasn't specified, don't pin */ + if(!pinnedpubkey) + return CURLE_OK; + + if(!cert) + return result; + + do { + /* Begin Gyrations to get the subjectPublicKeyInfo */ + /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ + + /* https://groups.google.com/group/mailing.openssl.users/browse_thread + /thread/d61858dae102c6c7 */ + len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); + if(len1 < 1) + break; /* failed */ + + /* https://www.openssl.org/docs/crypto/buffer.html */ + buff1 = temp = OPENSSL_malloc(len1); + if(!buff1) + break; /* failed */ + + /* https://www.openssl.org/docs/crypto/d2i_X509.html */ + len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp); + + /* + * These checks are verifying we got back the same values as when we + * sized the buffer. It's pretty weak since they should always be the + * same. But it gives us something to test. + */ + if((len1 != len2) || !temp || ((temp - buff1) != len1)) + break; /* failed */ + + /* End Gyrations */ + + /* The one good exit point */ + result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1); + } while(0); + + /* https://www.openssl.org/docs/crypto/buffer.html */ + if(buff1) + OPENSSL_free(buff1); + + return result; +} + +/* * Get the server cert, verify it and show it etc, only call failf() if the * 'strict' argument is TRUE as otherwise all this is for informational * purposes only! @@ -2368,7 +2628,7 @@ static CURLcode servercert(struct connectdata *conn, struct ssl_connect_data *connssl, bool strict) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; int rc; long lerr; ASN1_TIME *certdate; @@ -2376,6 +2636,7 @@ static CURLcode servercert(struct connectdata *conn, X509 *issuer; FILE *fp; char *buffer = data->state.buffer; + const char *ptr; if(data->set.ssl.certinfo) /* we've been asked to gather certificate info! */ @@ -2387,7 +2648,8 @@ static CURLcode servercert(struct connectdata *conn, failf(data, "SSL: couldn't get peer certificate!"); return CURLE_PEER_FAILED_VERIFICATION; } - infof (data, "Server certificate:\n"); + + infof(data, "Server certificate:\n"); rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert), buffer, BUFSIZE); @@ -2402,11 +2664,11 @@ static CURLcode servercert(struct connectdata *conn, infof(data, "\t expire date: %s\n", buffer); if(data->set.ssl.verifyhost) { - retcode = verifyhost(conn, connssl->server_cert); - if(retcode) { + result = verifyhost(conn, connssl->server_cert); + if(result) { X509_free(connssl->server_cert); connssl->server_cert = NULL; - return retcode; + return result; } } @@ -2415,7 +2677,7 @@ static CURLcode servercert(struct connectdata *conn, if(rc) { if(strict) failf(data, "SSL: couldn't get X509-issuer name!"); - retcode = CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; } else { infof(data, "\t issuer: %s\n", buffer); @@ -2425,7 +2687,7 @@ static CURLcode servercert(struct connectdata *conn, /* e.g. match issuer name with provided issuer certificate */ if(data->set.str[STRING_SSL_ISSUERCERT]) { - fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r"); + fp = fopen(data->set.str[STRING_SSL_ISSUERCERT], FOPEN_READTEXT); if(!fp) { if(strict) failf(data, "SSL: Unable to open issuer cert (%s)", @@ -2434,7 +2696,8 @@ static CURLcode servercert(struct connectdata *conn, connssl->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL); + + issuer = PEM_read_X509(fp, NULL, ZERO_NULL, NULL); if(!issuer) { if(strict) failf(data, "SSL: Unable to read issuer cert (%s)", @@ -2444,8 +2707,10 @@ static CURLcode servercert(struct connectdata *conn, fclose(fp); return CURLE_SSL_ISSUER_ERROR; } + fclose(fp); - if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) { + + if(X509_check_issued(issuer, connssl->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", data->set.str[STRING_SSL_ISSUERCERT]); @@ -2454,13 +2719,15 @@ static CURLcode servercert(struct connectdata *conn, connssl->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } + infof(data, "\t SSL certificate issuer check ok (%s)\n", data->set.str[STRING_SSL_ISSUERCERT]); X509_free(issuer); } - lerr = data->set.ssl.certverifyresult= + lerr = data->set.ssl.certverifyresult = SSL_get_verify_result(connssl->handle); + if(data->set.ssl.certverifyresult != X509_V_OK) { if(data->set.ssl.verifypeer) { /* We probably never reach this, because SSL_connect() will fail @@ -2468,7 +2735,7 @@ static CURLcode servercert(struct connectdata *conn, if(strict) failf(data, "SSL certificate verify result: %s (%ld)", X509_verify_cert_error_string(lerr), lerr); - retcode = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t SSL certificate verify result: %s (%ld)," @@ -2479,46 +2746,52 @@ static CURLcode servercert(struct connectdata *conn, infof(data, "\t SSL certificate verify ok.\n"); } +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_IS_BORINGSSL) + if(data->set.ssl.verifystatus) { + result = verifystatus(conn, connssl); + if(result) { + X509_free(connssl->server_cert); + connssl->server_cert = NULL; + return result; + } + } +#endif + + if(!strict) + /* when not strict, we don't bother about the verify cert problems */ + result = CURLE_OK; + + ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + if(!result && ptr) { + result = pkp_pin_peer_pubkey(connssl->server_cert, ptr); + if(result) + failf(data, "SSL: public key does not match pinned public key!"); + } + X509_free(connssl->server_cert); connssl->server_cert = NULL; connssl->connecting_state = ssl_connect_done; - return retcode; + return result; } - -static CURLcode -ossl_connect_step3(struct connectdata *conn, - int sockindex) +static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex) { - CURLcode retcode = CURLE_OK; - void *old_ssl_sessionid=NULL; + CURLcode result = CURLE_OK; + void *old_ssl_sessionid = NULL; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - int incache; + bool incache; SSL_SESSION *our_ssl_sessionid; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); -#ifdef HAVE_SSL_GET1_SESSION our_ssl_sessionid = SSL_get1_session(connssl->handle); - /* SSL_get1_session() will increment the reference - count and the session will stay in memory until explicitly freed with - SSL_SESSION_free(3), regardless of its state. - This function was introduced in openssl 0.9.5a. */ -#else - our_ssl_sessionid = SSL_get_session(connssl->handle); - - /* if SSL_get1_session() is unavailable, use SSL_get_session(). - This is an inferior option because the session can be flushed - at any time by openssl. It is included only so curl compiles - under versions of openssl < 0.9.5a. - - WARNING: How curl behaves if it's session is flushed is - untested. - */ -#endif + /* SSL_get1_session() will increment the reference count and the session + will stay in memory until explicitly freed with SSL_SESSION_free(3), + regardless of its state. */ incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL)); if(incache) { @@ -2528,15 +2801,15 @@ ossl_connect_step3(struct connectdata *conn, incache = FALSE; } } + if(!incache) { - retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, - 0 /* unknown size */); - if(retcode) { + result = Curl_ssl_addsessionid(conn, our_ssl_sessionid, + 0 /* unknown size */); + if(result) { failf(data, "failed to store ssl session"); - return retcode; + return result; } } -#ifdef HAVE_SSL_GET1_SESSION else { /* Session was incache, so refcount already incremented earlier. * Avoid further increments with each SSL_get1_session() call. @@ -2544,7 +2817,6 @@ ossl_connect_step3(struct connectdata *conn, */ SSL_SESSION_free(our_ssl_sessionid); } -#endif /* * We check certificates to authenticate the server; otherwise we risk @@ -2553,26 +2825,24 @@ ossl_connect_step3(struct connectdata *conn, * operations. */ - if(!data->set.ssl.verifypeer && !data->set.ssl.verifyhost) - (void)servercert(conn, connssl, FALSE); - else - retcode = servercert(conn, connssl, TRUE); + result = servercert(conn, connssl, + (data->set.ssl.verifypeer || data->set.ssl.verifyhost)); - if(CURLE_OK == retcode) + if(!result) connssl->connecting_state = ssl_connect_done; - return retcode; + + return result; } static Curl_recv ossl_recv; static Curl_send ossl_send; -static CURLcode -ossl_connect_common(struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) +static CURLcode ossl_connect_common(struct connectdata *conn, + int sockindex, + bool nonblocking, + bool *done) { - CURLcode retcode; + CURLcode result; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; @@ -2585,7 +2855,7 @@ ossl_connect_common(struct connectdata *conn, return CURLE_OK; } - if(ssl_connect_1==connssl->connecting_state) { + if(ssl_connect_1 == connssl->connecting_state) { /* Find out how much more time we're allowed */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -2594,9 +2864,10 @@ ossl_connect_common(struct connectdata *conn, failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } - retcode = ossl_connect_step1(conn, sockindex); - if(retcode) - return retcode; + + result = ossl_connect_step1(conn, sockindex); + if(result) + return result; } while(ssl_connect_2 == connssl->connecting_state || @@ -2613,8 +2884,8 @@ ossl_connect_common(struct connectdata *conn, } /* 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) { + 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; @@ -2647,23 +2918,22 @@ ossl_connect_common(struct connectdata *conn, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - retcode = ossl_connect_step2(conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; + result = ossl_connect_step2(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) { - retcode = ossl_connect_step3(conn, sockindex); - if(retcode) - return retcode; + if(ssl_connect_3 == connssl->connecting_state) { + result = ossl_connect_step3(conn, sockindex); + if(result) + return result; } - if(ssl_connect_done==connssl->connecting_state) { + if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; conn->recv[sockindex] = ossl_recv; conn->send[sockindex] = ossl_send; @@ -2678,32 +2948,28 @@ ossl_connect_common(struct connectdata *conn, return CURLE_OK; } -CURLcode -Curl_ossl_connect_nonblocking(struct connectdata *conn, - int sockindex, - bool *done) +CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn, + int sockindex, + bool *done) { return ossl_connect_common(conn, sockindex, TRUE, done); } -CURLcode -Curl_ossl_connect(struct connectdata *conn, - int sockindex) +CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done = FALSE; - retcode = ossl_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + result = ossl_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; DEBUGASSERT(done); return CURLE_OK; } -bool Curl_ossl_data_pending(const struct connectdata *conn, - int connindex) +bool Curl_ossl_data_pending(const struct connectdata *conn, int connindex) { if(conn->ssl[connindex].handle) /* SSL is in use */ @@ -2798,7 +3064,7 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ default: /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ - /* http://www.openssl.org/docs/crypto/ERR_get_error.html */ + /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ sslerror = ERR_get_error(); if((nread < 0) || sslerror) { /* If the return code was negative or there actually is an error in the @@ -2821,8 +3087,11 @@ size_t Curl_ossl_version(char *buffer, size_t size) to OpenSSL in all other aspects */ return snprintf(buffer, size, "yassl/%s", YASSL_VERSION); #else /* YASSL_VERSION */ +#ifdef OPENSSL_IS_BORINGSSL + return snprintf(buffer, size, "BoringSSL"); +#else /* OPENSSL_IS_BORINGSSL */ -#if(SSLEAY_VERSION_NUMBER >= 0x905000) +#if(OPENSSL_VERSION_NUMBER >= 0x905000) { char sub[3]; unsigned long ssleay_value; @@ -2850,47 +3119,44 @@ size_t Curl_ossl_version(char *buffer, size_t size) } return snprintf(buffer, size, "%s/%lx.%lx.%lx%s", -#ifdef OPENSSL_IS_BORINGSSL - "BoringSSL" -#else #ifdef LIBRESSL_VERSION_NUMBER "LibreSSL" #else "OpenSSL" #endif -#endif , (ssleay_value>>28)&0xf, (ssleay_value>>20)&0xff, (ssleay_value>>12)&0xff, sub); } -#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */ +#else /* OPENSSL_VERSION_NUMBER is less than 0.9.5 */ -#if(SSLEAY_VERSION_NUMBER >= 0x900000) +#if(OPENSSL_VERSION_NUMBER >= 0x900000) return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx", - (SSLEAY_VERSION_NUMBER>>28)&0xff, - (SSLEAY_VERSION_NUMBER>>20)&0xff, - (SSLEAY_VERSION_NUMBER>>12)&0xf); + (OPENSSL_VERSION_NUMBER>>28)&0xff, + (OPENSSL_VERSION_NUMBER>>20)&0xff, + (OPENSSL_VERSION_NUMBER>>12)&0xf); -#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */ +#else /* (OPENSSL_VERSION_NUMBER >= 0x900000) */ { char sub[2]; sub[1]='\0'; - if(SSLEAY_VERSION_NUMBER&0x0f) { - sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; + if(OPENSSL_VERSION_NUMBER&0x0f) { + sub[0]=(OPENSSL_VERSION_NUMBER&0x0f) + 'a' -1; } else sub[0]='\0'; return snprintf(buffer, size, "SSL/%x.%x.%x%s", - (SSLEAY_VERSION_NUMBER>>12)&0xff, - (SSLEAY_VERSION_NUMBER>>8)&0xf, - (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); + (OPENSSL_VERSION_NUMBER>>12)&0xff, + (OPENSSL_VERSION_NUMBER>>8)&0xf, + (OPENSSL_VERSION_NUMBER>>4)&0xf, sub); } -#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */ -#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */ +#endif /* (OPENSSL_VERSION_NUMBER >= 0x900000) */ +#endif /* OPENSSL_VERSION_NUMBER is less than 0.9.5 */ +#endif /* OPENSSL_IS_BORINGSSL */ #endif /* YASSL_VERSION */ } @@ -2898,8 +3164,9 @@ size_t Curl_ossl_version(char *buffer, size_t size) int Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy, size_t length) { - if(data) + if(data) { Curl_ossl_seed(data); /* Initiate the seed if not already done */ + } RAND_bytes(entropy, curlx_uztosi(length)); return 0; /* 0 as in no problem */ } @@ -2915,4 +3182,28 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */ MD5_Update(&MD5pw, tmp, tmplen); MD5_Final(md5sum, &MD5pw); } -#endif /* USE_SSLEAY */ + +#ifndef OPENSSL_NO_SHA256 +void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum /* output */, + size_t unused) +{ + SHA256_CTX SHA256pw; + (void)unused; + SHA256_Init(&SHA256pw); + SHA256_Update(&SHA256pw, tmp, tmplen); + SHA256_Final(sha256sum, &SHA256pw); +} +#endif + +bool Curl_ossl_cert_status_request(void) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ + !defined(OPENSSL_IS_BORINGSSL) + return TRUE; +#else + return FALSE; +#endif +} +#endif /* USE_OPENSSL */ diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 1a55ffc..a1f347a 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -24,7 +24,7 @@ #include "curl_setup.h" -#ifdef USE_SSLEAY +#ifdef USE_OPENSSL /* * This header should only be needed to get included by vtls.c and openssl.c */ @@ -41,7 +41,7 @@ void Curl_ossl_close(struct connectdata *conn, int sockindex); /* tell OpenSSL to close down all open information regarding connections (and thus session ID caching etc) */ -int Curl_ossl_close_all(struct SessionHandle *data); +void Curl_ossl_close_all(struct SessionHandle *data); /* Sets an OpenSSL engine */ CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine); @@ -72,9 +72,24 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */ size_t tmplen, unsigned char *md5sum /* output */, size_t unused); +void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *sha256sum /* output */, + size_t unused); + +bool Curl_ossl_cert_status_request(void); + +/* Set the API backend definition to OpenSSL */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_OPENSSL + +/* this backend supports the CAPATH option */ +#define have_curlssl_ca_path 1 -/* this backend provides these functions: */ -#define have_curlssl_md5sum 1 +/* this backend supports CURLOPT_CERTINFO */ +#define have_curlssl_certinfo 1 + +/* this backend suppots CURLOPT_SSL_CTX_* */ +#define have_curlssl_ssl_ctx 1 /* API setup for OpenSSL */ #define curlssl_init Curl_ossl_init @@ -93,9 +108,13 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */ #define curlssl_data_pending(x,y) Curl_ossl_data_pending(x,y) #define curlssl_random(x,y,z) Curl_ossl_random(x,y,z) #define curlssl_md5sum(a,b,c,d) Curl_ossl_md5sum(a,b,c,d) -#define CURL_SSL_BACKEND CURLSSLBACKEND_OPENSSL +#ifndef OPENSSL_NO_SHA256 +#define curlssl_sha256sum(a,b,c,d) Curl_ossl_sha256sum(a,b,c,d) +#endif +#define curlssl_cert_status_request() Curl_ossl_cert_status_request() -#define DEFAULT_CIPHER_SELECTION "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4" +#define DEFAULT_CIPHER_SELECTION \ + "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" -#endif /* USE_SSLEAY */ +#endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/lib/vtls/polarssl.c b/lib/vtls/polarssl.c index 5332b92..066c055 100644 --- a/lib/vtls/polarssl.c +++ b/lib/vtls/polarssl.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,9 +55,7 @@ #include "select.h" #include "rawstr.h" #include "polarssl_threadlock.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -120,11 +118,8 @@ static void polarssl_debug(void *context, int level, const char *line) #endif /* ALPN for http2? */ -#ifdef USE_NGHTTP2 -# undef HAS_ALPN -# ifdef POLARSSL_SSL_ALPN -# define HAS_ALPN -# endif +#ifdef POLARSSL_SSL_ALPN +# define HAS_ALPN #endif static Curl_recv polarssl_recv; @@ -287,24 +282,38 @@ polarssl_connect_step1(struct connectdata *conn, } switch(data->set.ssl.version) { + default: + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3, + SSL_MINOR_VERSION_1); + break; case CURL_SSLVERSION_SSLv3: ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0); + ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3, + SSL_MINOR_VERSION_0); infof(data, "PolarSSL: Forced min. SSL Version to be SSLv3\n"); break; case CURL_SSLVERSION_TLSv1_0: ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1); + ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3, + SSL_MINOR_VERSION_1); infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.0\n"); break; case CURL_SSLVERSION_TLSv1_1: ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_2); + ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3, + SSL_MINOR_VERSION_2); infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.1\n"); break; case CURL_SSLVERSION_TLSv1_2: ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3); + ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3, + SSL_MINOR_VERSION_3); infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.2\n"); break; } @@ -345,15 +354,23 @@ polarssl_connect_step1(struct connectdata *conn, } #ifdef HAS_ALPN - if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { - if(data->set.ssl_enable_alpn) { - static const char* protocols[] = { - NGHTTP2_PROTO_VERSION_ID, ALPN_HTTP_1_1, NULL - }; - ssl_set_alpn_protocols(&connssl->ssl, protocols); - infof(data, "ALPN, offering %s, %s\n", protocols[0], - protocols[1]); + if(data->set.ssl_enable_alpn) { + static const char* protocols[3]; + int cur = 0; + +#ifdef USE_NGHTTP2 + if(data->set.httpversion == CURL_HTTP_VERSION_2_0) { + protocols[cur++] = NGHTTP2_PROTO_VERSION_ID; + infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } +#endif + + protocols[cur++] = ALPN_HTTP_1_1; + infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); + + protocols[cur] = NULL; + + ssl_set_alpn_protocols(&connssl->ssl, protocols); } #endif @@ -375,47 +392,37 @@ polarssl_connect_step2(struct connectdata *conn, struct ssl_connect_data* connssl = &conn->ssl[sockindex]; char buffer[1024]; -#ifdef HAS_ALPN - const char* next_protocol; -#endif - char errorbuf[128]; errorbuf[0] = 0; conn->recv[sockindex] = polarssl_recv; conn->send[sockindex] = polarssl_send; - for(;;) { - if(!(ret = ssl_handshake(&connssl->ssl))) - break; - else if(ret != POLARSSL_ERR_NET_WANT_READ && - ret != POLARSSL_ERR_NET_WANT_WRITE) { -#ifdef POLARSSL_ERROR_C - error_strerror(ret, errorbuf, sizeof(errorbuf)); -#endif /* POLARSSL_ERROR_C */ - failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s", - -ret, errorbuf); + ret = ssl_handshake(&connssl->ssl); - return CURLE_SSL_CONNECT_ERROR; - } - else { - if(ret == POLARSSL_ERR_NET_WANT_READ) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - if(ret == POLARSSL_ERR_NET_WANT_WRITE) { - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_OK; - } - failf(data, "SSL_connect failed with error %d.", ret); - return CURLE_SSL_CONNECT_ERROR; + switch(ret) { + case 0: + break; - } + case POLARSSL_ERR_NET_WANT_READ: + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_OK; + + case POLARSSL_ERR_NET_WANT_WRITE: + connssl->connecting_state = ssl_connect_2_writing; + return CURLE_OK; + + default: +#ifdef POLARSSL_ERROR_C + error_strerror(ret, errorbuf, sizeof(errorbuf)); +#endif /* POLARSSL_ERROR_C */ + failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CONNECT_ERROR; } infof(data, "PolarSSL: Handshake complete, cipher is %s\n", - ssl_get_ciphersuite(&conn->ssl[sockindex].ssl) - ); + ssl_get_ciphersuite(&conn->ssl[sockindex].ssl) ); ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl); @@ -448,22 +455,24 @@ polarssl_connect_step2(struct connectdata *conn, #ifdef HAS_ALPN if(data->set.ssl_enable_alpn) { - next_protocol = ssl_get_alpn_protocol(&connssl->ssl); + const char *next_protocol = ssl_get_alpn_protocol(&connssl->ssl); if(next_protocol != NULL) { infof(data, "ALPN, server accepted to use %s\n", next_protocol); - if(strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID, +#ifdef USE_NGHTTP2 + if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN)) { - conn->negnpn = NPN_HTTP2; + conn->negnpn = CURL_HTTP_VERSION_2_0; } - else if(strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = NPN_HTTP1_1; + else +#endif + if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) { + conn->negnpn = CURL_HTTP_VERSION_1_1; } } - else { + else infof(data, "ALPN, server did not agree to a protocol\n"); - } } #endif @@ -477,12 +486,12 @@ static CURLcode polarssl_connect_step3(struct connectdata *conn, int sockindex) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct SessionHandle *data = conn->data; void *old_ssl_sessionid = NULL; - ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ; - int incache; + ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn; + bool incache; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -495,23 +504,21 @@ polarssl_connect_step3(struct connectdata *conn, incache = FALSE; } } + if(!incache) { void *new_session = malloc(sizeof(ssl_session)); if(new_session) { - memcpy(new_session, our_ssl_sessionid, - sizeof(ssl_session)); + memcpy(new_session, our_ssl_sessionid, sizeof(ssl_session)); - retcode = Curl_ssl_addsessionid(conn, new_session, - sizeof(ssl_session)); - } - else { - retcode = CURLE_OUT_OF_MEMORY; + result = Curl_ssl_addsessionid(conn, new_session, sizeof(ssl_session)); } + else + result = CURLE_OUT_OF_MEMORY; - if(retcode) { + if(result) { failf(data, "failed to store ssl session"); - return retcode; + return result; } } @@ -540,11 +547,6 @@ static ssize_t polarssl_send(struct connectdata *conn, return ret; } -void Curl_polarssl_close_all(struct SessionHandle *data) -{ - (void)data; -} - void Curl_polarssl_close(struct connectdata *conn, int sockindex) { rsa_free(&conn->ssl[sockindex].rsa); @@ -585,11 +587,15 @@ void Curl_polarssl_session_free(void *ptr) free(ptr); } +/* 1.3.10 was the first rebranded version. All new releases (in 1.3 branch and + higher) will be mbed TLS branded.. */ + size_t Curl_polarssl_version(char *buffer, size_t size) { unsigned int version = version_get_number(); - return snprintf(buffer, size, "PolarSSL/%d.%d.%d", version>>24, - (version>>16)&0xff, (version>>8)&0xff); + return snprintf(buffer, size, "%s/%d.%d.%d", + version >= 0x01030A00?"mbedTLS":"PolarSSL", + version>>24, (version>>16)&0xff, (version>>8)&0xff); } static CURLcode @@ -598,7 +604,7 @@ polarssl_connect_common(struct connectdata *conn, bool nonblocking, bool *done) { - CURLcode retcode; + CURLcode result; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; @@ -611,7 +617,7 @@ polarssl_connect_common(struct connectdata *conn, return CURLE_OK; } - if(ssl_connect_1==connssl->connecting_state) { + if(ssl_connect_1 == connssl->connecting_state) { /* Find out how much more time we're allowed */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -620,9 +626,10 @@ polarssl_connect_common(struct connectdata *conn, failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } - retcode = polarssl_connect_step1(conn, sockindex); - if(retcode) - return retcode; + + result = polarssl_connect_step1(conn, sockindex); + if(result) + return result; } while(ssl_connect_2 == connssl->connecting_state || @@ -639,8 +646,8 @@ polarssl_connect_common(struct connectdata *conn, } /* 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) { + 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; @@ -674,22 +681,22 @@ polarssl_connect_common(struct connectdata *conn, * ensuring that a client using select() or epoll() will always * have a valid fdset to wait on. */ - retcode = polarssl_connect_step2(conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; + result = polarssl_connect_step2(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) { - retcode = polarssl_connect_step3(conn, sockindex); - if(retcode) - return retcode; + if(ssl_connect_3 == connssl->connecting_state) { + result = polarssl_connect_step3(conn, sockindex); + if(result) + return result; } - if(ssl_connect_done==connssl->connecting_state) { + if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; conn->recv[sockindex] = polarssl_recv; conn->send[sockindex] = polarssl_send; @@ -717,12 +724,12 @@ CURLcode Curl_polarssl_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done = FALSE; - retcode = polarssl_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + result = polarssl_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; DEBUGASSERT(done); diff --git a/lib/vtls/polarssl.h b/lib/vtls/polarssl.h index 9ab7e47..f980dbb 100644 --- a/lib/vtls/polarssl.h +++ b/lib/vtls/polarssl.h @@ -8,6 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -36,10 +37,6 @@ CURLcode Curl_polarssl_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done); -/* tell PolarSSL to close down all open information regarding connections (and - thus session ID caching etc) */ -void Curl_polarssl_close_all(struct SessionHandle *data); - /* close a SSL connection */ void Curl_polarssl_close(struct connectdata *conn, int sockindex); @@ -47,27 +44,32 @@ void Curl_polarssl_session_free(void *ptr); size_t Curl_polarssl_version(char *buffer, size_t size); int Curl_polarssl_shutdown(struct connectdata *conn, int sockindex); +/* Set the API backend definition to PolarSSL */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_POLARSSL + +/* this backend supports the CAPATH option */ +#define have_curlssl_ca_path 1 + /* API setup for PolarSSL */ #define curlssl_init() polarssl_init() #define curlssl_cleanup() polarssl_cleanup() #define curlssl_connect Curl_polarssl_connect #define curlssl_connect_nonblocking Curl_polarssl_connect_nonblocking #define curlssl_session_free(x) Curl_polarssl_session_free(x) -#define curlssl_close_all Curl_polarssl_close_all +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_polarssl_close #define curlssl_shutdown(x,y) 0 -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_polarssl_version -#define curlssl_check_cxn(x) (x=x, -1) -#define curlssl_data_pending(x,y) (x=x, y=y, 0) -#define CURL_SSL_BACKEND CURLSSLBACKEND_POLARSSL +#define curlssl_check_cxn(x) ((void)x, -1) +#define curlssl_data_pending(x,y) ((void)x, (void)y, 0) /* This might cause libcurl to use a weeker random! TODO: implement proper use of Polarssl's CTR-DRBG or HMAC-DRBG and use that */ -#define curlssl_random(x,y,z) (x=x, y=y, z=z, CURLE_NOT_BUILT_IN) +#define curlssl_random(x,y,z) ((void)x, (void)y, (void)z, CURLE_NOT_BUILT_IN) #endif /* USE_POLARSSL */ #endif /* HEADER_CURL_POLARSSL_H */ diff --git a/lib/vtls/polarssl_threadlock.c b/lib/vtls/polarssl_threadlock.c index ad18715..62abf43 100644 --- a/lib/vtls/polarssl_threadlock.c +++ b/lib/vtls/polarssl_threadlock.c @@ -36,10 +36,7 @@ #endif #include "polarssl_threadlock.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" diff --git a/lib/vtls/qssl.c b/lib/vtls/qssl.c deleted file mode 100644 index 4c32053..0000000 --- a/lib/vtls/qssl.c +++ /dev/null @@ -1,527 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.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_QSOSSL - -#include <qsossl.h> - -#ifdef HAVE_LIMITS_H -# include <limits.h> -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "qssl.h" -#include "vtls.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "x509asn1.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - - -int Curl_qsossl_init(void) - -{ - /* Nothing to do here. We must have connection data to initialize ssl, so - * defer. - */ - - return 1; -} - - -void Curl_qsossl_cleanup(void) - -{ - /* Nothing to do. */ -} - - -static CURLcode Curl_qsossl_init_session(struct SessionHandle * data) - -{ - int rc; - char * certname; - SSLInit initstr; - SSLInitApp initappstr; - - /* Initialize the job for SSL according to the current parameters. - * QsoSSL offers two ways to do it: SSL_Init_Application() that uses an - * application identifier to select certificates in the main certificate - * store, and SSL_Init() that uses named keyring files and a password. - * It is not possible to have different keyrings for the CAs and the - * local certificate. We thus use the certificate name to identify the - * keyring if given, else the CA file name. - * If the key file name is given, it is taken as the password for the - * keyring in certificate file. - * We first try to SSL_Init_Application(), then SSL_Init() if it failed. - */ - - certname = data->set.str[STRING_CERT]; - - if(!certname) { - certname = data->set.str[STRING_SSL_CAFILE]; - - if(!certname) - return CURLE_OK; /* Use previous setup. */ - } - - memset((char *) &initappstr, 0, sizeof initappstr); - initappstr.applicationID = certname; - initappstr.applicationIDLen = strlen(certname); - initappstr.protocol = SSL_VERSION_CURRENT; /* TLSV1 compat. SSLV[23]. */ - initappstr.sessionType = SSL_REGISTERED_AS_CLIENT; - rc = SSL_Init_Application(&initappstr); - - if(rc == SSL_ERROR_NOT_REGISTERED) { - initstr.keyringFileName = certname; - initstr.keyringPassword = data->set.str[STRING_KEY]; - initstr.cipherSuiteList = NULL; /* Use default. */ - initstr.cipherSuiteListLen = 0; - rc = SSL_Init(&initstr); - } - - switch (rc) { - - case 0: /* No error. */ - break; - - case SSL_ERROR_IO: - failf(data, "SSL_Init() I/O error: %s", strerror(errno)); - return CURLE_SSL_CONNECT_ERROR; - - case SSL_ERROR_BAD_CIPHER_SUITE: - return CURLE_SSL_CIPHER; - - case SSL_ERROR_KEYPASSWORD_EXPIRED: - case SSL_ERROR_NOT_REGISTERED: - return CURLE_SSL_CONNECT_ERROR; - - case SSL_ERROR_NO_KEYRING: - return CURLE_SSL_CACERT; - - case SSL_ERROR_CERT_EXPIRED: - return CURLE_SSL_CERTPROBLEM; - - default: - failf(data, "SSL_Init(): %s", SSL_Strerror(rc, NULL)); - return CURLE_SSL_CONNECT_ERROR; - } - - return CURLE_OK; -} - - -static CURLcode Curl_qsossl_create(struct connectdata * conn, int sockindex) - -{ - SSLHandle * h; - struct ssl_connect_data * connssl = &conn->ssl[sockindex]; - - h = SSL_Create(conn->sock[sockindex], SSL_ENCRYPT); - - if(!h) { - failf(conn->data, "SSL_Create() I/O error: %s", strerror(errno)); - return CURLE_SSL_CONNECT_ERROR; - } - - connssl->handle = h; - return CURLE_OK; -} - - -static int Curl_qsossl_trap_cert(SSLHandle * h) - -{ - return 1; /* Accept certificate. */ -} - - -static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex) - -{ - int rc; - struct SessionHandle * data = conn->data; - struct ssl_connect_data * connssl = &conn->ssl[sockindex]; - SSLHandle * h = connssl->handle; - long timeout_ms; - - h->exitPgm = data->set.ssl.verifypeer? NULL: Curl_qsossl_trap_cert; - - /* figure out how long time we should wait at maximum */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* time-out, bail out, go home */ - failf(data, "Connection time-out"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* SSL_Handshake() timeout resolution is second, so round up. */ - h->timeout = (timeout_ms + 1000 - 1) / 1000; - - /* Set-up protocol. */ - - switch (data->set.ssl.version) { - - default: - case CURL_SSLVERSION_DEFAULT: - h->protocol = SSL_VERSION_CURRENT; /* TLSV1 compat. SSLV[23]. */ - break; - - case CURL_SSLVERSION_TLSv1: - h->protocol = TLS_VERSION_1; - break; - - case CURL_SSLVERSION_SSLv2: - h->protocol = SSL_VERSION_2; - break; - - case CURL_SSLVERSION_SSLv3: - h->protocol = SSL_VERSION_3; - break; - - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - failf(data, "TLS minor version cannot be set"); - return CURLE_SSL_CONNECT_ERROR; - } - - h->peerCert = NULL; - h->peerCertLen = 0; - rc = SSL_Handshake(h, SSL_HANDSHAKE_AS_CLIENT); - - switch (rc) { - - case 0: /* No error. */ - break; - - case SSL_ERROR_BAD_CERTIFICATE: - case SSL_ERROR_BAD_CERT_SIG: - case SSL_ERROR_NOT_TRUSTED_ROOT: - return CURLE_PEER_FAILED_VERIFICATION; - - case SSL_ERROR_BAD_CIPHER_SUITE: - case SSL_ERROR_NO_CIPHERS: - return CURLE_SSL_CIPHER; - - case SSL_ERROR_CERTIFICATE_REJECTED: - case SSL_ERROR_CERT_EXPIRED: - case SSL_ERROR_NO_CERTIFICATE: - return CURLE_SSL_CERTPROBLEM; - - case SSL_ERROR_IO: - failf(data, "SSL_Handshake() I/O error: %s", strerror(errno)); - return CURLE_SSL_CONNECT_ERROR; - - default: - failf(data, "SSL_Handshake(): %s", SSL_Strerror(rc, NULL)); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Verify host. */ - rc = Curl_verifyhost(conn, h->peerCert, h->peerCert + h->peerCertLen); - if(rc != CURLE_OK) - return rc; - - /* Gather certificate info. */ - if(data->set.ssl.certinfo) { - if(Curl_ssl_init_certinfo(data, 1)) - return CURLE_OUT_OF_MEMORY; - if(h->peerCert) { - rc = Curl_extract_certinfo(conn, 0, h->peerCert, - h->peerCert + h->peerCertLen); - if(rc != CURLE_OK) - return rc; - } - } - - return CURLE_OK; -} - - -static Curl_recv qsossl_recv; -static Curl_send qsossl_send; - -CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex) - -{ - struct SessionHandle * data = conn->data; - struct ssl_connect_data * connssl = &conn->ssl[sockindex]; - int rc; - - rc = Curl_qsossl_init_session(data); - - if(rc == CURLE_OK) { - rc = Curl_qsossl_create(conn, sockindex); - - if(rc == CURLE_OK) { - rc = Curl_qsossl_handshake(conn, sockindex); - if(rc != CURLE_OK) - SSL_Destroy(connssl->handle); - } - } - - if(rc == CURLE_OK) { - conn->recv[sockindex] = qsossl_recv; - conn->send[sockindex] = qsossl_send; - connssl->state = ssl_connection_complete; - } - else { - connssl->handle = NULL; - connssl->use = FALSE; - connssl->state = ssl_connection_none; - } - - return rc; -} - - -static int Curl_qsossl_close_one(struct ssl_connect_data * conn, - struct SessionHandle * data) - -{ - int rc; - - if(!conn->handle) - return 0; - - rc = SSL_Destroy(conn->handle); - - if(rc) { - if(rc == SSL_ERROR_IO) { - failf(data, "SSL_Destroy() I/O error: %s", strerror(errno)); - return -1; - } - - /* An SSL error. */ - failf(data, "SSL_Destroy() returned error %s", SSL_Strerror(rc, NULL)); - return -1; - } - - conn->handle = NULL; - return 0; -} - - -void Curl_qsossl_close(struct connectdata *conn, int sockindex) - -{ - struct SessionHandle *data = conn->data; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - - if(connssl->use) - (void) Curl_qsossl_close_one(connssl, data); -} - - -int Curl_qsossl_close_all(struct SessionHandle * data) - -{ - /* Unimplemented. */ - (void) data; - return 0; -} - - -int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex) - -{ - struct ssl_connect_data * connssl = &conn->ssl[sockindex]; - struct SessionHandle *data = conn->data; - ssize_t nread; - int what; - int rc; - char buf[120]; - - if(!connssl->handle) - return 0; - - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; - - if(Curl_qsossl_close_one(connssl, data)) - return -1; - - rc = 0; - - what = Curl_socket_ready(conn->sock[sockindex], - CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); - - for(;;) { - if(what < 0) { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - rc = -1; - break; - } - - if(!what) { /* timeout */ - failf(data, "SSL shutdown timeout"); - break; - } - - /* Something to read, let's do it and hope that it is the close - notify alert from the server. No way to SSL_Read now, so use read(). */ - - nread = read(conn->sock[sockindex], buf, sizeof(buf)); - - if(nread < 0) { - failf(data, "read: %s", strerror(errno)); - rc = -1; - } - - if(nread <= 0) - break; - - what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0); - } - - return rc; -} - - -static ssize_t qsossl_send(struct connectdata * conn, int sockindex, - const void * mem, size_t len, CURLcode * curlcode) - -{ - /* SSL_Write() is said to return 'int' while write() and send() returns - 'size_t' */ - int rc; - - rc = SSL_Write(conn->ssl[sockindex].handle, (void *) mem, (int) len); - - if(rc < 0) { - switch(rc) { - - case SSL_ERROR_BAD_STATE: - /* The operation did not complete; the same SSL I/O function - should be called again later. This is basically an EWOULDBLOCK - equivalent. */ - *curlcode = CURLE_AGAIN; - return -1; - - case SSL_ERROR_IO: - switch (errno) { - case EWOULDBLOCK: - case EINTR: - *curlcode = CURLE_AGAIN; - return -1; - } - - failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno)); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - /* An SSL error. */ - failf(conn->data, "SSL_Write() returned error %s", - SSL_Strerror(rc, NULL)); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - return (ssize_t) rc; /* number of bytes */ -} - - -static ssize_t qsossl_recv(struct connectdata * conn, int num, char * buf, - size_t buffersize, CURLcode * curlcode) - -{ - char error_buffer[120]; /* OpenSSL documents that this must be at - least 120 bytes long. */ - unsigned long sslerror; - int buffsize; - int nread; - - buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - nread = SSL_Read(conn->ssl[num].handle, buf, buffsize); - - if(nread < 0) { - /* failed SSL_read */ - - switch (nread) { - - case SSL_ERROR_BAD_STATE: - /* there's data pending, re-invoke SSL_Read(). */ - *curlcode = CURLE_AGAIN; - return -1; - - case SSL_ERROR_IO: - switch (errno) { - case EWOULDBLOCK: - *curlcode = CURLE_AGAIN; - return -1; - } - - failf(conn->data, "SSL_Read() I/O error: %s", strerror(errno)); - *curlcode = CURLE_RECV_ERROR; - return -1; - - default: - failf(conn->data, "SSL read error: %s", SSL_Strerror(nread, NULL)); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - } - return (ssize_t) nread; -} - - -size_t Curl_qsossl_version(char * buffer, size_t size) - -{ - strncpy(buffer, "IBM OS/400 SSL", size); - return strlen(buffer); -} - - -int Curl_qsossl_check_cxn(struct connectdata * cxn) - -{ - int err; - int errlen; - - /* The only thing that can be tested here is at the socket level. */ - - if(!cxn->ssl[FIRSTSOCKET].handle) - return 0; /* connection has been closed */ - - err = 0; - errlen = sizeof err; - - if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR, - (unsigned char *) &err, &errlen) || - errlen != sizeof err || err) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -#endif /* USE_QSOSSL */ diff --git a/lib/vtls/qssl.h b/lib/vtls/qssl.h deleted file mode 100644 index 9764eec..0000000 --- a/lib/vtls/qssl.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef HEADER_CURL_QSSL_H -#define HEADER_CURL_QSSL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.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" - -/* - * This header should only be needed to get included by vtls.c and qssl.c - */ - -#include "urldata.h" - -#ifdef USE_QSOSSL -int Curl_qsossl_init(void); -void Curl_qsossl_cleanup(void); -CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex); -void Curl_qsossl_close(struct connectdata *conn, int sockindex); -int Curl_qsossl_close_all(struct SessionHandle * data); -int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex); - -size_t Curl_qsossl_version(char * buffer, size_t size); -int Curl_qsossl_check_cxn(struct connectdata * cxn); - -/* API setup for QsoSSL */ -#define curlssl_init Curl_qsossl_init -#define curlssl_cleanup Curl_qsossl_cleanup -#define curlssl_connect Curl_qsossl_connect - -/* No session handling for QsoSSL */ -#define curlssl_session_free(x) Curl_nop_stmt -#define curlssl_close_all Curl_qsossl_close_all -#define curlssl_close Curl_qsossl_close -#define curlssl_shutdown(x,y) Curl_qsossl_shutdown(x,y) -#define curlssl_set_engine(x,y) CURLE_NOT_BUILT_IN -#define curlssl_set_engine_default(x) CURLE_NOT_BUILT_IN -#define curlssl_engines_list(x) NULL -#define curlssl_version Curl_qsossl_version -#define curlssl_check_cxn(x) Curl_qsossl_check_cxn(x) -#define curlssl_data_pending(x,y) 0 -#define CURL_SSL_BACKEND CURLSSLBACKEND_QSOSSL -#endif /* USE_QSOSSL */ - -#endif /* HEADER_CURL_QSSL_H */ diff --git a/lib/vtls/curl_schannel.c b/lib/vtls/schannel.c index e4e595e..2174e21 100644 --- a/lib/vtls/curl_schannel.c +++ b/lib/vtls/schannel.c @@ -5,9 +5,9 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de> + * Copyright (C) 2012 - 2015, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -38,19 +38,6 @@ * Thanks for code and inspiration! */ -/* - * TODO list for TLS/SSL implementation: - * - implement client certificate authentication - * - implement custom server certificate validation - * - implement cipher/algorithm option - * - * Related articles on MSDN: - * - Getting a Certificate for Schannel - * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx - * - Specifying Schannel Ciphers and Cipher Strengths - * http://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx - */ - #include "curl_setup.h" #ifdef USE_SCHANNEL @@ -60,7 +47,7 @@ #endif #include "curl_sspi.h" -#include "curl_schannel.h" +#include "schannel.h" #include "vtls.h" #include "sendf.h" #include "connect.h" /* for the connect timeout */ @@ -69,10 +56,7 @@ #include "inet_pton.h" /* for IP addr SNI check */ #include "curl_multibyte.h" #include "warnless.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -121,13 +105,13 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) struct in6_addr addr6; #endif TCHAR *host_name; - CURLcode code; + CURLcode result; infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n", conn->host.name, conn->remote_port); /* check for an existing re-usable credential handle */ - if(!Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)) { + if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) { connssl->cred = old_cred; infof(data, "schannel: re-using existing credential handle\n"); } @@ -141,60 +125,64 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) /* certificate validation on CE doesn't seem to work right; we'll do it following a more manual process. */ schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; + SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; #else - schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | - SCH_CRED_REVOCATION_CHECK_CHAIN; + schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; + if(data->set.ssl_no_revoke) + schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + else + schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; #endif - infof(data, "schannel: checking server certificate revocation\n"); + if(data->set.ssl_no_revoke) + infof(data, "schannel: disabled server certificate revocation " + "checks\n"); + else + infof(data, "schannel: checking server certificate revocation\n"); } else { schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - infof(data, "schannel: disable server certificate revocation checks\n"); + SCH_CRED_IGNORE_NO_REVOCATION_CHECK | + SCH_CRED_IGNORE_REVOCATION_OFFLINE; + infof(data, "schannel: disabled server certificate revocation checks\n"); } if(!data->set.ssl.verifyhost) { schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; infof(data, "schannel: verifyhost setting prevents Schannel from " - "comparing the supplied target name with the subject " - "names in server certificates. Also disables SNI.\n"); + "comparing the supplied target name with the subject " + "names in server certificates. Also disables SNI.\n"); } switch(data->set.ssl.version) { - case CURL_SSLVERSION_TLSv1: - schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | - SP_PROT_TLS1_1_CLIENT | - SP_PROT_TLS1_2_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_0: - schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_1: - schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_2: - schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; - break; - case CURL_SSLVERSION_SSLv3: - schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; - break; - case CURL_SSLVERSION_SSLv2: - schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT; - break; - default: - schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | - SP_PROT_TLS1_1_CLIENT | - SP_PROT_TLS1_2_CLIENT | - SP_PROT_SSL3_CLIENT; - break; + default: + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | + SP_PROT_TLS1_1_CLIENT | + SP_PROT_TLS1_2_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_0: + schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_1: + schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT; + break; + case CURL_SSLVERSION_TLSv1_2: + schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; + break; + case CURL_SSLVERSION_SSLv3: + schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; + break; + case CURL_SSLVERSION_SSLv2: + schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT; + break; } /* allocate memory for the re-usable credential handle */ connssl->cred = (struct curl_schannel_cred *) - malloc(sizeof(struct curl_schannel_cred)); + malloc(sizeof(struct curl_schannel_cred)); if(!connssl->cred) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; @@ -202,9 +190,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) memset(connssl->cred, 0, sizeof(struct curl_schannel_cred)); /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */ - sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, - SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL, - &connssl->cred->cred_handle, &connssl->cred->time_stamp); + sspi_status = + s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, + SECPKG_CRED_OUTBOUND, NULL, + &schannel_cred, NULL, NULL, + &connssl->cred->cred_handle, + &connssl->cred->time_stamp); if(sspi_status != SEC_E_OK) { if(sspi_status == SEC_E_WRONG_PRINCIPAL) @@ -233,12 +224,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) /* setup request flags */ connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_STREAM; + ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; /* allocate memory for the security context handle */ connssl->ctxt = (struct curl_schannel_ctxt *) - malloc(sizeof(struct curl_schannel_ctxt)); + malloc(sizeof(struct curl_schannel_ctxt)); if(!connssl->ctxt) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; @@ -273,10 +264,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) "sending %lu bytes...\n", outbuf.cbBuffer); /* send initial handshake data which is now stored in output buffer */ - code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, - outbuf.cbBuffer, &written); + result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, + outbuf.cbBuffer, &written); s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) { + if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { failf(data, "schannel: failed to send initial handshake data: " "sent %zd of %lu bytes", written, outbuf.cbBuffer); return CURLE_SSL_CONNECT_ERROR; @@ -285,6 +276,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) infof(data, "schannel: sent initial handshake data: " "sent %zd bytes\n", written); + connssl->recv_unrecoverable_err = CURLE_OK; + connssl->recv_sspi_close_notify = false; + connssl->recv_connection_closed = false; + /* continue to second handshake step */ connssl->connecting_state = ssl_connect_2; @@ -298,13 +293,15 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) ssize_t nread = -1, written = -1; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SecBuffer outbuf[2]; + unsigned char *reallocated_buffer; + size_t reallocated_length; + SecBuffer outbuf[3]; SecBufferDesc outbuf_desc; SecBuffer inbuf[2]; SecBufferDesc inbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; TCHAR *host_name; - CURLcode code; + CURLcode result; bool doread; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; @@ -315,6 +312,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) if(!connssl->cred || !connssl->ctxt) return CURLE_SSL_CONNECT_ERROR; + /* buffer to store previously received and decrypted data */ + if(connssl->decdata_buffer == NULL) { + connssl->decdata_offset = 0; + connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + connssl->decdata_buffer = malloc(connssl->decdata_length); + if(connssl->decdata_buffer == NULL) { + failf(data, "schannel: unable to allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + } + /* buffer to store previously received and encrypted data */ if(connssl->encdata_buffer == NULL) { connssl->encdata_offset = 0; @@ -330,31 +338,38 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) if(connssl->encdata_length - connssl->encdata_offset < CURL_SCHANNEL_BUFFER_FREE_SIZE) { /* increase internal encrypted data buffer */ - connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR; - connssl->encdata_buffer = realloc(connssl->encdata_buffer, - connssl->encdata_length); + reallocated_length = connssl->encdata_offset + + CURL_SCHANNEL_BUFFER_FREE_SIZE; + reallocated_buffer = realloc(connssl->encdata_buffer, + reallocated_length); - if(connssl->encdata_buffer == NULL) { + if(reallocated_buffer == NULL) { failf(data, "schannel: unable to re-allocate memory"); return CURLE_OUT_OF_MEMORY; } + else { + connssl->encdata_buffer = reallocated_buffer; + connssl->encdata_length = reallocated_length; + } } for(;;) { if(doread) { /* read encrypted handshake data from socket */ - code = Curl_read_plain(conn->sock[sockindex], - (char *) (connssl->encdata_buffer + connssl->encdata_offset), - connssl->encdata_length - connssl->encdata_offset, - &nread); - if(code == CURLE_AGAIN) { + result = Curl_read_plain(conn->sock[sockindex], + (char *) (connssl->encdata_buffer + + connssl->encdata_offset), + connssl->encdata_length - + connssl->encdata_offset, + &nread); + if(result == CURLE_AGAIN) { if(connssl->connecting_state != ssl_connect_2_writing) connssl->connecting_state = ssl_connect_2_reading; infof(data, "schannel: failed to receive handshake, " "need more data\n"); return CURLE_OK; } - else if((code != CURLE_OK) || (nread == 0)) { + else if((result != CURLE_OK) || (nread == 0)) { failf(data, "schannel: failed to receive handshake, " "SSL/TLS connection failed"); return CURLE_SSL_CONNECT_ERROR; @@ -365,7 +380,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) } infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", - connssl->encdata_offset, connssl->encdata_length); + connssl->encdata_offset, connssl->encdata_length); /* setup input buffers */ InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset), @@ -376,7 +391,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) /* setup output buffers */ InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0); InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0); - InitSecBufferDesc(&outbuf_desc, outbuf, 2); + InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0); + InitSecBufferDesc(&outbuf_desc, outbuf, 3); if(inbuf[0].pvBuffer == NULL) { failf(data, "schannel: unable to allocate memory"); @@ -410,19 +426,31 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) return CURLE_OK; } + /* If the server has requested a client certificate, attempt to continue + the handshake without one. This will allow connections to servers which + request a client certificate but do not require it. */ + if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && + !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { + connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + connssl->connecting_state = ssl_connect_2_writing; + infof(data, "schannel: a client certificate has been requested\n"); + return CURLE_OK; + } + /* check if the handshake needs to be continued */ if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { - for(i = 0; i < 2; i++) { + for(i = 0; i < 3; i++) { /* search for handshake tokens that need to be send */ if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { infof(data, "schannel: sending next handshake data: " "sending %lu bytes...\n", outbuf[i].cbBuffer); /* send handshake token to server */ - code = Curl_write_plain(conn, conn->sock[sockindex], - outbuf[i].pvBuffer, outbuf[i].cbBuffer, - &written); - if((code != CURLE_OK) || (outbuf[i].cbBuffer != (size_t)written)) { + result = Curl_write_plain(conn, conn->sock[sockindex], + outbuf[i].pvBuffer, outbuf[i].cbBuffer, + &written); + if((result != CURLE_OK) || + (outbuf[i].cbBuffer != (size_t) written)) { failf(data, "schannel: failed to send next handshake data: " "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); return CURLE_SSL_CONNECT_ERROR; @@ -449,21 +477,21 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer); /* - There are two cases where we could be getting extra data here: - 1) If we're renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data - (not handshake data) in an extra buffer at this point. - 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a - connection and this extra data is part of the handshake. - We should process the data immediately; waiting for the socket to - be ready may fail since the server is done sending handshake data. - */ + There are two cases where we could be getting extra data here: + 1) If we're renegotiating a connection and the handshake is already + complete (from the server perspective), it can encrypted app data + (not handshake data) in an extra buffer at this point. + 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a + connection and this extra data is part of the handshake. + We should process the data immediately; waiting for the socket to + be ready may fail since the server is done sending handshake data. + */ /* check if the remaining data is less than the total amount and therefore begins after the already processed data */ if(connssl->encdata_offset > inbuf[1].cbBuffer) { memmove(connssl->encdata_buffer, (connssl->encdata_buffer + connssl->encdata_offset) - - inbuf[1].cbBuffer, inbuf[1].cbBuffer); + inbuf[1].cbBuffer, inbuf[1].cbBuffer); connssl->encdata_offset = inbuf[1].cbBuffer; if(sspi_status == SEC_I_CONTINUE_NEEDED) { doread = FALSE; @@ -502,11 +530,11 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) static CURLcode schannel_connect_step3(struct connectdata *conn, int sockindex) { - CURLcode retcode = CURLE_OK; + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct curl_schannel_cred *old_cred = NULL; - int incache; + bool incache; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -539,20 +567,21 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) } /* save the current session data for possible re-use */ - incache = !(Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)); + incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)); if(incache) { if(old_cred != connssl->cred) { infof(data, "schannel: old credential handle is stale, removing\n"); - Curl_ssl_delsessionid(conn, (void*)old_cred); + Curl_ssl_delsessionid(conn, (void *)old_cred); incache = FALSE; } } + if(!incache) { - retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred, - sizeof(struct curl_schannel_cred)); - if(retcode) { + result = Curl_ssl_addsessionid(conn, (void *)connssl->cred, + sizeof(struct curl_schannel_cred)); + if(result) { failf(data, "schannel: failed to store credential handle"); - return retcode; + return result; } else { connssl->cred->cached = TRUE; @@ -569,7 +598,7 @@ static CURLcode schannel_connect_common(struct connectdata *conn, int sockindex, bool nonblocking, bool *done) { - CURLcode retcode; + CURLcode result; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; @@ -592,9 +621,9 @@ schannel_connect_common(struct connectdata *conn, int sockindex, return CURLE_OPERATION_TIMEDOUT; } - retcode = schannel_connect_step1(conn, sockindex); - if(retcode) - return retcode; + result = schannel_connect_step1(conn, sockindex); + if(result) + return result; } while(ssl_connect_2 == connssl->connecting_state || @@ -646,19 +675,19 @@ schannel_connect_common(struct connectdata *conn, int sockindex, * ensuring that a client using select() or epoll() will always * have a valid fdset to wait on. */ - retcode = schannel_connect_step2(conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; + result = schannel_connect_step2(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) { - retcode = schannel_connect_step3(conn, sockindex); - if(retcode) - return retcode; + result = schannel_connect_step3(conn, sockindex); + if(result) + return result; } if(ssl_connect_done == connssl->connecting_state) { @@ -687,14 +716,14 @@ schannel_send(struct connectdata *conn, int sockindex, SecBuffer outbuf[4]; SecBufferDesc outbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode code; + CURLcode result; /* check if the maximum stream sizes were queried */ if(connssl->stream_sizes.cbMaximumMessage == 0) { sspi_status = s_pSecFn->QueryContextAttributes( - &connssl->ctxt->ctxt_handle, - SECPKG_ATTR_STREAM_SIZES, - &connssl->stream_sizes); + &connssl->ctxt->ctxt_handle, + SECPKG_ATTR_STREAM_SIZES, + &connssl->stream_sizes); if(sspi_status != SEC_E_OK) { *err = CURLE_SEND_ERROR; return -1; @@ -709,8 +738,8 @@ schannel_send(struct connectdata *conn, int sockindex, /* calculate the complete message length and allocate a buffer for it */ data_len = connssl->stream_sizes.cbHeader + len + - connssl->stream_sizes.cbTrailer; - data = (unsigned char*) malloc(data_len); + connssl->stream_sizes.cbTrailer; + data = (unsigned char *) malloc(data_len); if(data == NULL) { *err = CURLE_OUT_OF_MEMORY; return -1; @@ -742,19 +771,19 @@ schannel_send(struct connectdata *conn, int sockindex, len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; /* - It's important to send the full message which includes the header, - encrypted payload, and trailer. Until the client receives all the - data a coherent message has not been delivered and the client - can't read any of it. - - If we wanted to buffer the unwritten encrypted bytes, we would - tell the client that all data it has requested to be sent has been - sent. The unwritten encrypted bytes would be the first bytes to - send on the next invocation. - Here's the catch with this - if we tell the client that all the - bytes have been sent, will the client call this method again to - send the buffered data? Looking at who calls this function, it - seems the answer is NO. + It's important to send the full message which includes the header, + encrypted payload, and trailer. Until the client receives all the + data a coherent message has not been delivered and the client + can't read any of it. + + If we wanted to buffer the unwritten encrypted bytes, we would + tell the client that all data it has requested to be sent has been + sent. The unwritten encrypted bytes would be the first bytes to + send on the next invocation. + Here's the catch with this - if we tell the client that all the + bytes have been sent, will the client call this method again to + send the buffered data? Looking at who calls this function, it + seems the answer is NO. */ /* send entire message or fail */ @@ -793,12 +822,12 @@ schannel_send(struct connectdata *conn, int sockindex, } /* socket is writable */ - code = Curl_write_plain(conn, conn->sock[sockindex], data + written, - len - written, &this_write); - if(code == CURLE_AGAIN) + result = Curl_write_plain(conn, conn->sock[sockindex], data + written, + len - written, &this_write); + if(result == CURLE_AGAIN) continue; - else if(code != CURLE_OK) { - *err = code; + else if(result != CURLE_OK) { + *err = result; written = -1; break; } @@ -828,71 +857,112 @@ schannel_recv(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err) { size_t size = 0; - ssize_t nread = 0, ret = -1; - CURLcode retcode; + ssize_t nread = -1; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + unsigned char *reallocated_buffer; + size_t reallocated_length; bool done = FALSE; SecBuffer inbuf[4]; SecBufferDesc inbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; + /* we want the length of the encrypted buffer to be at least large enough + that it can hold all the bytes requested and some TLS record overhead. */ + size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; + + /**************************************************************************** + * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup. + * The pattern for return error is set *err, optional infof, goto cleanup. + * + * Our priority is to always return as much decrypted data to the caller as + * possible, even if an error occurs. The state of the decrypted buffer must + * always be valid. Transfer of decrypted data to the caller's buffer is + * handled in the cleanup. + */ infof(data, "schannel: client wants to read %zu bytes\n", len); *err = CURLE_OK; - /* buffer to store previously received and decrypted data */ - if(connssl->decdata_buffer == NULL) { - connssl->decdata_offset = 0; - connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - connssl->decdata_buffer = malloc(connssl->decdata_length); - if(connssl->decdata_buffer == NULL) { - failf(data, "schannel: unable to allocate memory"); - *err = CURLE_OUT_OF_MEMORY; - return -1; - } + if(len && len <= connssl->decdata_offset) { + infof(data, "schannel: enough decrypted data is already available\n"); + goto cleanup; } + else if(connssl->recv_unrecoverable_err) { + *err = connssl->recv_unrecoverable_err; + infof(data, "schannel: an unrecoverable error occurred in a prior call\n"); + goto cleanup; + } + else if(connssl->recv_sspi_close_notify) { + /* once a server has indicated shutdown there is no more encrypted data */ + infof(data, "schannel: server indicated shutdown in a prior call\n"); + goto cleanup; + } + else if(!len) { + /* It's debatable what to return when !len. Regardless we can't return + immediately because there may be data to decrypt (in the case we want to + decrypt all encrypted cached data) so handle !len later in cleanup. + */ + ; /* do nothing */ + } + else if(!connssl->recv_connection_closed) { + /* increase enc buffer in order to fit the requested amount of data */ + size = connssl->encdata_length - connssl->encdata_offset; + if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || + connssl->encdata_length < min_encdata_length) { + reallocated_length = connssl->encdata_offset + + CURL_SCHANNEL_BUFFER_FREE_SIZE; + if(reallocated_length < min_encdata_length) { + reallocated_length = min_encdata_length; + } + reallocated_buffer = realloc(connssl->encdata_buffer, + reallocated_length); + if(reallocated_buffer == NULL) { + *err = CURLE_OUT_OF_MEMORY; + failf(data, "schannel: unable to re-allocate memory"); + goto cleanup; + } - /* increase buffer in order to fit the requested amount of data */ - while(connssl->encdata_length - connssl->encdata_offset < - CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) { - /* increase internal encrypted data buffer */ - connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR; - connssl->encdata_buffer = realloc(connssl->encdata_buffer, - connssl->encdata_length); - - if(connssl->encdata_buffer == NULL) { - failf(data, "schannel: unable to re-allocate memory"); - *err = CURLE_OUT_OF_MEMORY; - return -1; + connssl->encdata_buffer = reallocated_buffer; + connssl->encdata_length = reallocated_length; + size = connssl->encdata_length - connssl->encdata_offset; + infof(data, "schannel: encdata_buffer resized %zu\n", + connssl->encdata_length); } - } - /* read encrypted data from socket */ - infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", - connssl->encdata_offset, connssl->encdata_length); - size = connssl->encdata_length - connssl->encdata_offset; - if(size > 0) { + infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", + connssl->encdata_offset, connssl->encdata_length); + + /* read encrypted data from socket */ *err = Curl_read_plain(conn->sock[sockindex], - (char *) (connssl->encdata_buffer + connssl->encdata_offset), + (char *)(connssl->encdata_buffer + + connssl->encdata_offset), size, &nread); - /* check for received data */ - if(*err != CURLE_OK) - ret = -1; - else { - if(nread > 0) - /* increase encrypted data buffer offset */ - connssl->encdata_offset += nread; - ret = nread; + if(*err) { + nread = -1; + if(*err == CURLE_AGAIN) + infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n"); + else if(*err == CURLE_RECV_ERROR) + infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n"); + else + infof(data, "schannel: Curl_read_plain returned error %d\n", *err); + } + else if(nread == 0) { + connssl->recv_connection_closed = true; + infof(data, "schannel: server closed the connection\n"); + } + else if(nread > 0) { + connssl->encdata_offset += (size_t)nread; + infof(data, "schannel: encrypted data got %zd\n", nread); } - infof(data, "schannel: encrypted data got %zd\n", ret); } infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", connssl->encdata_offset, connssl->encdata_length); - /* check if we still have some data in our buffers */ + /* decrypt loop */ while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK && - connssl->decdata_offset < len) { + (!len || connssl->decdata_offset < len || + connssl->recv_connection_closed)) { /* prepare data buffer for DecryptMessage call */ InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer, curlx_uztoul(connssl->encdata_offset)); @@ -901,25 +971,18 @@ schannel_recv(struct connectdata *conn, int sockindex, InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0); InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, inbuf, 4); /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */ sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle, &inbuf_desc, 0, NULL); - /* check if we need more data */ - if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - infof(data, "schannel: failed to decrypt data, need more data\n"); - *err = CURLE_AGAIN; - return -1; - } - /* check if everything went fine (server may want to renegotiate - context) */ + or shutdown the connection context) */ if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || - sspi_status == SEC_I_CONTEXT_EXPIRED) { - /* check for successfully decrypted data */ + sspi_status == SEC_I_CONTEXT_EXPIRED) { + /* check for successfully decrypted data, even before actual + renegotiation or shutdown of the connection context */ if(inbuf[1].BufferType == SECBUFFER_DATA) { infof(data, "schannel: decrypted data length: %lu\n", inbuf[1].cbBuffer); @@ -927,23 +990,28 @@ schannel_recv(struct connectdata *conn, int sockindex, /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; - while(connssl->decdata_length - connssl->decdata_offset < size || - connssl->decdata_length < len) { + if(connssl->decdata_length - connssl->decdata_offset < size || + connssl->decdata_length < len) { /* increase internal decrypted data buffer */ - connssl->decdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR; - connssl->decdata_buffer = realloc(connssl->decdata_buffer, - connssl->decdata_length); - - if(connssl->decdata_buffer == NULL) { - failf(data, "schannel: unable to re-allocate memory"); + reallocated_length = connssl->decdata_offset + size; + /* make sure that the requested amount of data fits */ + if(reallocated_length < len) { + reallocated_length = len; + } + reallocated_buffer = realloc(connssl->decdata_buffer, + reallocated_length); + if(reallocated_buffer == NULL) { *err = CURLE_OUT_OF_MEMORY; - return -1; + failf(data, "schannel: unable to re-allocate memory"); + goto cleanup; } + connssl->decdata_buffer = reallocated_buffer; + connssl->decdata_length = reallocated_length; } /* copy decrypted data to internal buffer */ size = inbuf[1].cbBuffer; - if(size > 0) { + if(size) { memcpy(connssl->decdata_buffer + connssl->decdata_offset, inbuf[1].pvBuffer, size); connssl->decdata_offset += size; @@ -961,81 +1029,151 @@ schannel_recv(struct connectdata *conn, int sockindex, /* check if the remaining data is less than the total amount * and therefore begins after the already processed data - */ + */ if(connssl->encdata_offset > inbuf[3].cbBuffer) { /* move remaining encrypted data forward to the beginning of buffer */ memmove(connssl->encdata_buffer, (connssl->encdata_buffer + connssl->encdata_offset) - - inbuf[3].cbBuffer, inbuf[3].cbBuffer); + inbuf[3].cbBuffer, inbuf[3].cbBuffer); connssl->encdata_offset = inbuf[3].cbBuffer; } infof(data, "schannel: encrypted data cached: offset %zu length %zu\n", connssl->encdata_offset, connssl->encdata_length); } - else{ + else { /* reset encrypted buffer offset, because there is no data remaining */ connssl->encdata_offset = 0; } - } - /* check if server wants to renegotiate the connection context */ - if(sspi_status == SEC_I_RENEGOTIATE) { - infof(data, "schannel: remote party requests SSL/TLS renegotiation\n"); - - /* begin renegotiation */ - infof(data, "schannel: renegotiating SSL/TLS connection\n"); - connssl->state = ssl_connection_negotiating; - connssl->connecting_state = ssl_connect_2_writing; - retcode = schannel_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - *err = retcode; - else { - infof(data, "schannel: SSL/TLS connection renegotiated\n"); + /* check if server wants to renegotiate the connection context */ + if(sspi_status == SEC_I_RENEGOTIATE) { + infof(data, "schannel: remote party requests renegotiation\n"); + if(*err && *err != CURLE_AGAIN) { + infof(data, "schannel: can't renogotiate, an error is pending\n"); + goto cleanup; + } + if(connssl->encdata_offset) { + *err = CURLE_RECV_ERROR; + infof(data, "schannel: can't renogotiate, " + "encrypted data available\n"); + goto cleanup; + } + /* begin renegotiation */ + infof(data, "schannel: renegotiating SSL/TLS connection\n"); + connssl->state = ssl_connection_negotiating; + connssl->connecting_state = ssl_connect_2_writing; + *err = schannel_connect_common(conn, sockindex, FALSE, &done); + if(*err) { + infof(data, "schannel: renegotiation failed\n"); + goto cleanup; + } /* now retry receiving data */ - return schannel_recv(conn, sockindex, buf, len, err); + sspi_status = SEC_E_OK; + infof(data, "schannel: SSL/TLS connection renegotiated\n"); + continue; } + /* check if the server closed the connection */ + else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { + /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not + returned so we have to work around that in cleanup. */ + connssl->recv_sspi_close_notify = true; + if(!connssl->recv_connection_closed) { + connssl->recv_connection_closed = true; + infof(data, "schannel: server closed the connection\n"); + } + goto cleanup; + } + } + else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { + if(!*err) + *err = CURLE_AGAIN; + infof(data, "schannel: failed to decrypt data, need more data\n"); + goto cleanup; + } + else { + *err = CURLE_RECV_ERROR; + infof(data, "schannel: failed to read data from server: %s\n", + Curl_sspi_strerror(conn, sspi_status)); + goto cleanup; } } + infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", + connssl->encdata_offset, connssl->encdata_length); + infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n", connssl->decdata_offset, connssl->decdata_length); - /* copy requested decrypted data to supplied buffer */ +cleanup: + /* Warning- there is no guarantee the encdata state is valid at this point */ + infof(data, "schannel: schannel_recv cleanup\n"); + + /* Error if the connection has closed without a close_notify. + Behavior here is a matter of debate. We don't want to be vulnerable to a + truncation attack however there's some browser precedent for ignoring the + close_notify for compatibility reasons. + Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't + return close_notify. In that case if the connection was closed we assume it + was graceful (close_notify) since there doesn't seem to be a way to tell. + */ + if(len && !connssl->decdata_offset && connssl->recv_connection_closed && + !connssl->recv_sspi_close_notify) { + BOOL isWin2k; + ULONGLONG cm; + OSVERSIONINFOEX osver; + + memset(&osver, 0, sizeof(osver)); + osver.dwOSVersionInfoSize = sizeof(osver); + osver.dwMajorVersion = 5; + + cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_EQUAL); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); + + isWin2k = VerifyVersionInfo(&osver, + (VER_MAJORVERSION | VER_MINORVERSION | + VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR), + cm); + + if(isWin2k && sspi_status == SEC_E_OK) + connssl->recv_sspi_close_notify = true; + else { + *err = CURLE_RECV_ERROR; + infof(data, "schannel: server closed abruptly (missing close_notify)\n"); + } + } + + /* Any error other than CURLE_AGAIN is an unrecoverable error. */ + if(*err && *err != CURLE_AGAIN) + connssl->recv_unrecoverable_err = *err; + size = len < connssl->decdata_offset ? len : connssl->decdata_offset; - if(size > 0) { + if(size) { memcpy(buf, connssl->decdata_buffer, size); - ret = size; - - /* move remaining decrypted data forward to the beginning of buffer */ memmove(connssl->decdata_buffer, connssl->decdata_buffer + size, connssl->decdata_offset - size); connssl->decdata_offset -= size; - infof(data, "schannel: decrypted data returned %zd\n", size); + infof(data, "schannel: decrypted data returned %zu\n", size); infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n", connssl->decdata_offset, connssl->decdata_length); - } - - /* check if the server closed the connection */ - if(ret <= 0 && ( /* special check for Windows 2000 Professional */ - sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK && - connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) { - infof(data, "schannel: server closed the connection\n"); *err = CURLE_OK; - return 0; + return (ssize_t)size; } - /* check if something went wrong and we need to return an error */ - if(ret < 0 && sspi_status != SEC_E_OK) { - infof(data, "schannel: failed to read data from server: %s\n", - Curl_sspi_strerror(conn, sspi_status)); - *err = CURLE_RECV_ERROR; - return -1; - } + if(!*err && !connssl->recv_connection_closed) + *err = CURLE_AGAIN; + + /* It's debatable what to return when !len. We could return whatever error we + got from decryption but instead we override here so the return is consistent. + */ + if(!len) + *err = CURLE_OK; - return ret; + return *err ? -1 : 0; } CURLcode @@ -1048,12 +1186,12 @@ Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex, CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex) { - CURLcode retcode; + CURLcode result; bool done = FALSE; - retcode = schannel_connect_common(conn, sockindex, FALSE, &done); - if(retcode) - return retcode; + result = schannel_connect_common(conn, sockindex, FALSE, &done); + if(result) + return result; DEBUGASSERT(done); @@ -1095,7 +1233,7 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) SECURITY_STATUS sspi_status; SecBuffer outbuf; SecBufferDesc outbuf_desc; - CURLcode code; + CURLcode result; TCHAR *host_name; DWORD dwshut = SCHANNEL_SHUTDOWN; @@ -1118,56 +1256,56 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) InitSecBufferDesc(&outbuf_desc, &outbuf, 1); sspi_status = s_pSecFn->InitializeSecurityContext( - &connssl->cred->cred_handle, - &connssl->ctxt->ctxt_handle, - host_name, - connssl->req_flags, - 0, - 0, - NULL, - 0, - &connssl->ctxt->ctxt_handle, - &outbuf_desc, - &connssl->ret_flags, - &connssl->ctxt->time_stamp); + &connssl->cred->cred_handle, + &connssl->ctxt->ctxt_handle, + host_name, + connssl->req_flags, + 0, + 0, + NULL, + 0, + &connssl->ctxt->ctxt_handle, + &outbuf_desc, + &connssl->ret_flags, + &connssl->ctxt->time_stamp); Curl_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; - code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, - outbuf.cbBuffer, &written); + result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, + outbuf.cbBuffer, &written); s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) { + if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { infof(data, "schannel: failed to send close msg: %s" - " (bytes written: %zd)\n", curl_easy_strerror(code), written); + " (bytes written: %zd)\n", curl_easy_strerror(result), written); } } + } - /* free SSPI Schannel API security context handle */ - if(connssl->ctxt) { - infof(data, "schannel: clear security context handle\n"); - s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle); - Curl_safefree(connssl->ctxt); - } + /* free SSPI Schannel API security context handle */ + if(connssl->ctxt) { + infof(data, "schannel: clear security context handle\n"); + s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle); + Curl_safefree(connssl->ctxt); + } - /* free SSPI Schannel API credential handle */ - if(connssl->cred) { - /* decrement the reference counter of the credential/session handle */ - if(connssl->cred->refcount > 0) { - connssl->cred->refcount--; - infof(data, "schannel: decremented credential handle refcount = %d\n", - connssl->cred->refcount); - } + /* free SSPI Schannel API credential handle */ + if(connssl->cred) { + /* decrement the reference counter of the credential/session handle */ + if(connssl->cred->refcount > 0) { + connssl->cred->refcount--; + infof(data, "schannel: decremented credential handle refcount = %d\n", + connssl->cred->refcount); + } - /* if the handle was not cached and the refcount is zero */ - if(!connssl->cred->cached && connssl->cred->refcount == 0) { - infof(data, "schannel: clear credential handle\n"); - s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle); - Curl_safefree(connssl->cred); - } + /* if the handle was not cached and the refcount is zero */ + if(!connssl->cred->cached && connssl->cred->refcount == 0) { + infof(data, "schannel: clear credential handle\n"); + s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle); + Curl_safefree(connssl->cred); } } @@ -1192,9 +1330,14 @@ void Curl_schannel_session_free(void *ptr) { struct curl_schannel_cred *cred = ptr; - if(cred && cred->cached && cred->refcount == 0) { - s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); - Curl_safefree(cred); + if(cred && cred->cached) { + if(cred->refcount == 0) { + s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + Curl_safefree(cred); + } + else { + cred->cached = FALSE; + } } } @@ -1262,7 +1405,8 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex) NULL, pCertContextServer->hCertStore, &ChainPara, - 0, + (data->set.ssl_no_revoke ? 0 : + CERT_CHAIN_REVOCATION_CHECK_CHAIN), NULL, &pChainContext)) { failf(data, "schannel: CertGetCertificateChain failed: %s", @@ -1273,21 +1417,24 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex) if(result == CURLE_OK) { CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; - DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED| - CERT_TRUST_REVOCATION_STATUS_UNKNOWN); + DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; if(dwTrustErrorMask) { - if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) + if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) + failf(data, "schannel: CertGetCertificateChain trust error" + " CERT_TRUST_IS_REVOKED"); + else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_PARTIAL_CHAIN"); - if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) + " CERT_TRUST_IS_PARTIAL_CHAIN"); + else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_UNTRUSTED_ROOT"); - if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) + " CERT_TRUST_IS_UNTRUSTED_ROOT"); + else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_NOT_TIME_VALID"); - failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", - dwTrustErrorMask); + " CERT_TRUST_IS_NOT_TIME_VALID"); + else + failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", + dwTrustErrorMask); result = CURLE_PEER_FAILED_VERIFICATION; } } @@ -1303,6 +1450,14 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex) cert_hostname.const_tchar_ptr = cert_hostname_buff; hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name); + /* TODO: Fix this for certificates with multiple alternative names. + Right now we're only asking for the first preferred alternative name. + Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG + (if WinCE supports that?) and run this section in a loop for each. + https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx + curl: (51) schannel: CertGetNameString() certificate hostname + (.google.com) did not match connection (google.com) + */ len = CertGetNameString(pCertContextServer, CERT_NAME_DNS_TYPE, 0, diff --git a/lib/vtls/curl_schannel.h b/lib/vtls/schannel.h index 700516b..5329584 100644 --- a/lib/vtls/curl_schannel.h +++ b/lib/vtls/schannel.h @@ -8,7 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al. - * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -72,30 +72,9 @@ #define SECBUFFER_ALERT 17 #endif -#ifndef ISC_RET_REPLAY_DETECT -#define ISC_RET_REPLAY_DETECT 0x00000004 -#endif - -#ifndef ISC_RET_SEQUENCE_DETECT -#define ISC_RET_SEQUENCE_DETECT 0x00000008 -#endif - -#ifndef ISC_RET_CONFIDENTIALITY -#define ISC_RET_CONFIDENTIALITY 0x00000010 -#endif - -#ifndef ISC_RET_ALLOCATED_MEMORY -#define ISC_RET_ALLOCATED_MEMORY 0x00000100 -#endif - -#ifndef ISC_RET_STREAM -#define ISC_RET_STREAM 0x00008000 -#endif - - +/* Both schannel buffer sizes must be > 0 */ #define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 #define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 -#define CURL_SCHANNEL_BUFFER_STEP_FACTOR 2 CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex); @@ -115,22 +94,24 @@ size_t Curl_schannel_version(char *buffer, size_t size); int Curl_schannel_random(unsigned char *entropy, size_t length); +/* Set the API backend definition to Schannel */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_SCHANNEL + /* API setup for Schannel */ #define curlssl_init Curl_schannel_init #define curlssl_cleanup Curl_schannel_cleanup #define curlssl_connect Curl_schannel_connect #define curlssl_connect_nonblocking Curl_schannel_connect_nonblocking #define curlssl_session_free Curl_schannel_session_free -#define curlssl_close_all(x) (x=x, CURLE_NOT_BUILT_IN) +#define curlssl_close_all(x) ((void)x) #define curlssl_close Curl_schannel_close #define curlssl_shutdown Curl_schannel_shutdown -#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN) -#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN) -#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN) +#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN) +#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL) #define curlssl_version Curl_schannel_version -#define curlssl_check_cxn(x) (x=x, -1) +#define curlssl_check_cxn(x) ((void)x, -1) #define curlssl_data_pending Curl_schannel_data_pending -#define CURL_SSL_BACKEND CURLSSLBACKEND_SCHANNEL #define curlssl_random(x,y,z) ((void)x, Curl_schannel_random(y,z)) #endif /* USE_SCHANNEL */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 88511b8..01bbc61 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,7 +31,6 @@ Curl_ossl_ - prefix for OpenSSL ones Curl_gtls_ - prefix for GnuTLS ones Curl_nss_ - prefix for NSS ones - Curl_qssl_ - prefix for QsoSSL ones Curl_gskit_ - prefix for GSKit ones Curl_polarssl_ - prefix for PolarSSL ones Curl_cyassl_ - prefix for CyaSSL ones @@ -60,29 +59,20 @@ #include "urldata.h" #include "vtls.h" /* generic SSL protos etc */ -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "nssg.h" /* NSS versions */ -#include "qssl.h" /* QSOSSL versions */ -#include "gskit.h" /* Global Secure ToolKit versions */ -#include "polarssl.h" /* PolarSSL versions */ -#include "axtls.h" /* axTLS versions */ -#include "cyassl.h" /* CyaSSL versions */ -#include "curl_schannel.h" /* Schannel SSPI version */ -#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */ #include "slist.h" #include "sendf.h" #include "rawstr.h" #include "url.h" -#include "curl_memory.h" #include "progress.h" #include "share.h" #include "timeval.h" +#include "curl_md5.h" +#include "warnless.h" +#include "curl_base64.h" +#include "curl_printf.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* convenience macro to check if this handle is using a shared SSL session */ @@ -193,7 +183,7 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc) unsigned int Curl_rand(struct SessionHandle *data) { - unsigned int r; + unsigned int r = 0; static unsigned int randseed; static bool seeded = FALSE; @@ -286,47 +276,66 @@ void Curl_ssl_cleanup(void) } } +static bool ssl_prefs_check(struct SessionHandle *data) +{ + /* check for CURLOPT_SSLVERSION invalid parameter value */ + if((data->set.ssl.version < 0) + || (data->set.ssl.version >= CURL_SSLVERSION_LAST)) { + failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION"); + return FALSE; + } + return TRUE; +} + CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex) { - CURLcode res; + CURLcode result; + + if(!ssl_prefs_check(conn->data)) + return CURLE_SSL_CONNECT_ERROR; + /* mark this is being ssl-enabled from here on. */ conn->ssl[sockindex].use = TRUE; conn->ssl[sockindex].state = ssl_connection_negotiating; - res = curlssl_connect(conn, sockindex); + result = curlssl_connect(conn, sockindex); - if(!res) + if(!result) Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */ - return res; + return result; } CURLcode Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done) { - CURLcode res; + CURLcode result; + + if(!ssl_prefs_check(conn->data)) + return CURLE_SSL_CONNECT_ERROR; + /* mark this is being ssl requested from here on. */ conn->ssl[sockindex].use = TRUE; #ifdef curlssl_connect_nonblocking - res = curlssl_connect_nonblocking(conn, sockindex, done); + result = curlssl_connect_nonblocking(conn, sockindex, done); #else *done = TRUE; /* fallback to BLOCKING */ - res = curlssl_connect(conn, sockindex); + result = curlssl_connect(conn, sockindex); #endif /* non-blocking connect support */ - if(!res && *done) + if(!result && *done) Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */ - return res; + return result; } /* * Check if there's a session ID for the given connection in the cache, and if * there's one suitable, it is provided. Returns TRUE when no entry matched. */ -int Curl_ssl_getsessionid(struct connectdata *conn, - void **ssl_sessionid, - size_t *idsize) /* set 0 if unknown */ +bool Curl_ssl_getsessionid(struct connectdata *conn, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */ { struct curl_ssl_session *check; struct SessionHandle *data = conn->data; @@ -473,9 +482,8 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn, store->sessionid = ssl_sessionid; store->idsize = idsize; store->age = *general_age; /* set current age */ - if(store->name) /* free it if there's one already present */ - free(store->name); + free(store->name); store->name = clone_host; /* clone host name */ store->remote_port = conn->remote_port; /* port number */ @@ -601,34 +609,37 @@ void Curl_ssl_free_certinfo(struct SessionHandle *data) { int i; struct curl_certinfo *ci = &data->info.certs; + if(ci->num_of_certs) { /* free all individual lists used */ for(i=0; i<ci->num_of_certs; i++) { curl_slist_free_all(ci->certinfo[i]); ci->certinfo[i] = NULL; } + free(ci->certinfo); /* free the actual array too */ ci->certinfo = NULL; ci->num_of_certs = 0; } } -int Curl_ssl_init_certinfo(struct SessionHandle * data, - int num) +CURLcode Curl_ssl_init_certinfo(struct SessionHandle *data, int num) { - struct curl_certinfo * ci = &data->info.certs; - struct curl_slist * * table; + struct curl_certinfo *ci = &data->info.certs; + struct curl_slist **table; - /* Initialize the certificate information structures. Return 0 if OK, else 1. - */ + /* Free any previous certificate information structures */ Curl_ssl_free_certinfo(data); - ci->num_of_certs = num; + + /* Allocate the required certificate information structures */ table = calloc((size_t) num, sizeof(struct curl_slist *)); if(!table) - return 1; + return CURLE_OUT_OF_MEMORY; + ci->num_of_certs = num; ci->certinfo = table; - return 0; + + return CURLE_OK; } /* @@ -643,7 +654,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data, struct curl_certinfo * ci = &data->info.certs; char * output; struct curl_slist * nl; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; size_t labellen = strlen(label); size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */ @@ -664,11 +675,11 @@ CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data, if(!nl) { free(output); curl_slist_free_all(ci->certinfo[certnum]); - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } ci->certinfo[certnum] = nl; - return res; + return result; } /* @@ -692,14 +703,260 @@ int Curl_ssl_random(struct SessionHandle *data, return curlssl_random(data, entropy, length); } -#ifdef have_curlssl_md5sum -void Curl_ssl_md5sum(unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *md5sum, /* output */ - size_t md5len) +/* + * Public key pem to der conversion + */ + +static CURLcode pubkey_pem_to_der(const char *pem, + unsigned char **der, size_t *der_len) +{ + char *stripped_pem, *begin_pos, *end_pos; + size_t pem_count, stripped_pem_count = 0, pem_len; + CURLcode result; + + /* if no pem, exit. */ + if(!pem) + return CURLE_BAD_CONTENT_ENCODING; + + begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----"); + if(!begin_pos) + return CURLE_BAD_CONTENT_ENCODING; + + pem_count = begin_pos - pem; + /* Invalid if not at beginning AND not directly following \n */ + if(0 != pem_count && '\n' != pem[pem_count - 1]) + return CURLE_BAD_CONTENT_ENCODING; + + /* 26 is length of "-----BEGIN PUBLIC KEY-----" */ + pem_count += 26; + + /* Invalid if not directly following \n */ + end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----"); + if(!end_pos) + return CURLE_BAD_CONTENT_ENCODING; + + pem_len = end_pos - pem; + + stripped_pem = malloc(pem_len - pem_count + 1); + if(!stripped_pem) + return CURLE_OUT_OF_MEMORY; + + /* + * Here we loop through the pem array one character at a time between the + * correct indices, and place each character that is not '\n' or '\r' + * into the stripped_pem array, which should represent the raw base64 string + */ + while(pem_count < pem_len) { + if('\n' != pem[pem_count] && '\r' != pem[pem_count]) + stripped_pem[stripped_pem_count++] = pem[pem_count]; + ++pem_count; + } + /* Place the null terminator in the correct place */ + stripped_pem[stripped_pem_count] = '\0'; + + result = Curl_base64_decode(stripped_pem, der, der_len); + + Curl_safefree(stripped_pem); + + return result; +} + +/* + * Generic pinned public key check. + */ + +CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey, + const unsigned char *pubkey, size_t pubkeylen) { + FILE *fp; + unsigned char *buf = NULL, *pem_ptr = NULL; + long filesize; + size_t size, pem_len; + CURLcode pem_read; + CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; +#ifdef curlssl_sha256sum + size_t pinkeylen; + char *pinkeycopy, *begin_pos, *end_pos; + unsigned char *sha256sumdigest = NULL, *expectedsha256sumdigest = NULL; +#endif + + /* if a path wasn't specified, don't pin */ + if(!pinnedpubkey) + return CURLE_OK; + if(!pubkey || !pubkeylen) + return result; + +#ifdef curlssl_sha256sum + /* only do this if pinnedpubkey starts with "sha256//", length 8 */ + if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { + /* compute sha256sum of public key */ + sha256sumdigest = malloc(SHA256_DIGEST_LENGTH); + if(!sha256sumdigest) + return CURLE_OUT_OF_MEMORY; + curlssl_sha256sum(pubkey, pubkeylen, + sha256sumdigest, SHA256_DIGEST_LENGTH); + + /* it starts with sha256//, copy so we can modify it */ + pinkeylen = strlen(pinnedpubkey) + 1; + pinkeycopy = malloc(pinkeylen); + if(!pinkeycopy) { + Curl_safefree(sha256sumdigest); + return CURLE_OUT_OF_MEMORY; + } + memcpy(pinkeycopy, pinnedpubkey, pinkeylen); + /* point begin_pos to the copy, and start extracting keys */ + begin_pos = pinkeycopy; + do { + end_pos = strstr(begin_pos, ";sha256//"); + /* + * if there is an end_pos, null terminate, + * otherwise it'll go to the end of the original string + */ + if(end_pos) + end_pos[0] = '\0'; + + /* decode base64 pinnedpubkey, 8 is length of "sha256//" */ + pem_read = Curl_base64_decode(begin_pos + 8, + &expectedsha256sumdigest, &size); + /* if not valid base64, don't bother comparing or freeing */ + if(!pem_read) { + /* compare sha256 digests directly */ + if(SHA256_DIGEST_LENGTH == size && + !memcmp(sha256sumdigest, expectedsha256sumdigest, + SHA256_DIGEST_LENGTH)) { + result = CURLE_OK; + Curl_safefree(expectedsha256sumdigest); + break; + } + Curl_safefree(expectedsha256sumdigest); + } + + /* + * change back the null-terminator we changed earlier, + * and look for next begin + */ + if(end_pos) { + end_pos[0] = ';'; + begin_pos = strstr(end_pos, "sha256//"); + } + } while(end_pos && begin_pos); + Curl_safefree(sha256sumdigest); + Curl_safefree(pinkeycopy); + return result; + } +#endif + + fp = fopen(pinnedpubkey, "rb"); + if(!fp) + return result; + + do { + /* Determine the file's size */ + if(fseek(fp, 0, SEEK_END)) + break; + filesize = ftell(fp); + if(fseek(fp, 0, SEEK_SET)) + break; + if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE) + break; + + /* + * if the size of our certificate is bigger than the file + * size then it can't match + */ + size = curlx_sotouz((curl_off_t) filesize); + if(pubkeylen > size) + break; + + /* + * Allocate buffer for the pinned key + * With 1 additional byte for null terminator in case of PEM key + */ + buf = malloc(size + 1); + if(!buf) + break; + + /* Returns number of elements read, which should be 1 */ + if((int) fread(buf, size, 1, fp) != 1) + break; + + /* If the sizes are the same, it can't be base64 encoded, must be der */ + if(pubkeylen == size) { + if(!memcmp(pubkey, buf, pubkeylen)) + result = CURLE_OK; + break; + } + + /* + * Otherwise we will assume it's PEM and try to decode it + * after placing null terminator + */ + buf[size] = '\0'; + pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); + /* if it wasn't read successfully, exit */ + if(pem_read) + break; + + /* + * if the size of our certificate doesn't match the size of + * the decoded file, they can't be the same, otherwise compare + */ + if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) + result = CURLE_OK; + } while(0); + + Curl_safefree(buf); + Curl_safefree(pem_ptr); + fclose(fp); + + return result; +} + +#ifndef CURL_DISABLE_CRYPTO_AUTH +CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *md5sum, /* output */ + size_t md5len) +{ +#ifdef curlssl_md5sum curlssl_md5sum(tmp, tmplen, md5sum, md5len); +#else + MD5_context *MD5pw; + + (void) md5len; + + MD5pw = Curl_MD5_init(Curl_DIGEST_MD5); + if(!MD5pw) + return CURLE_OUT_OF_MEMORY; + Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen)); + Curl_MD5_final(MD5pw, md5sum); +#endif + return CURLE_OK; +} +#endif + +/* + * Check whether the SSL backend supports the status_request extension. + */ +bool Curl_ssl_cert_status_request(void) +{ +#ifdef curlssl_cert_status_request + return curlssl_cert_status_request(); +#else + return FALSE; +#endif } + +/* + * Check whether the SSL backend supports false start. + */ +bool Curl_ssl_false_start(void) +{ +#ifdef curlssl_false_start + return curlssl_false_start(); +#else + return FALSE; #endif +} #endif /* USE_SSL */ diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index e21fdef..2349e5b 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,10 +23,28 @@ ***************************************************************************/ #include "curl_setup.h" +#include "openssl.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS versions */ +#include "nssg.h" /* NSS versions */ +#include "gskit.h" /* Global Secure ToolKit versions */ +#include "polarssl.h" /* PolarSSL versions */ +#include "axtls.h" /* axTLS versions */ +#include "cyassl.h" /* CyaSSL versions */ +#include "schannel.h" /* Schannel SSPI version */ +#include "darwinssl.h" /* SecureTransport (Darwin) version */ + +#ifndef MAX_PINNED_PUBKEY_SIZE +#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ +#endif + #ifndef MD5_DIGEST_LENGTH #define MD5_DIGEST_LENGTH 16 /* fixed size */ #endif +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 /* fixed size */ +#endif + /* see http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 */ #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" @@ -68,7 +86,7 @@ int Curl_ssl_check_cxn(struct connectdata *conn); /* Certificate information list handling. */ void Curl_ssl_free_certinfo(struct SessionHandle *data); -int Curl_ssl_init_certinfo(struct SessionHandle * data, int num); +CURLcode Curl_ssl_init_certinfo(struct SessionHandle * data, int num); CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle * data, int certnum, const char * label, const char * value, size_t valuelen); @@ -78,9 +96,9 @@ CURLcode Curl_ssl_push_certinfo(struct SessionHandle * data, int certnum, /* Functions to be used by SSL library adaptation functions */ /* extract a session ID */ -int Curl_ssl_getsessionid(struct connectdata *conn, - void **ssl_sessionid, - size_t *idsize) /* set 0 if unknown */; +bool Curl_ssl_getsessionid(struct connectdata *conn, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */; /* add a new session ID */ CURLcode Curl_ssl_addsessionid(struct connectdata *conn, void *ssl_sessionid, @@ -94,18 +112,24 @@ void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid); in */ int Curl_ssl_random(struct SessionHandle *data, unsigned char *buffer, size_t length); -void Curl_ssl_md5sum(unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *md5sum, /* output */ - size_t md5len); +CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *md5sum, /* output */ + size_t md5len); +/* Check pinned public key. */ +CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey, + const unsigned char *pubkey, size_t pubkeylen); -#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ +bool Curl_ssl_cert_status_request(void); -#ifdef have_curlssl_md5sum -#define HAVE_CURL_SSL_MD5SUM -#endif +bool Curl_ssl_false_start(void); + +#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ #else +/* Set the API backend definition to none */ +#define CURL_SSL_BACKEND CURLSSLBACKEND_NONE + /* When SSL support is not present, just define away these function calls */ #define Curl_ssl_init() 1 #define Curl_ssl_cleanup() Curl_nop_stmt @@ -125,8 +149,9 @@ void Curl_ssl_md5sum(unsigned char *tmp, /* input */ #define Curl_ssl_free_certinfo(x) Curl_nop_stmt #define Curl_ssl_connect_nonblocking(x,y,z) CURLE_NOT_BUILT_IN #define Curl_ssl_kill_session(x) Curl_nop_stmt -#define Curl_ssl_random(x,y,z) CURLE_NOT_BUILT_IN -#define CURL_SSL_BACKEND CURLSSLBACKEND_NONE +#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) +#define Curl_ssl_cert_status_request() FALSE +#define Curl_ssl_false_start() FALSE #endif #endif /* HEADER_CURL_VTLS_H */ diff --git a/lib/wildcard.c b/lib/wildcard.c index 7130d5e..6f55839 100644 --- a/lib/wildcard.c +++ b/lib/wildcard.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,10 +25,7 @@ #include "wildcard.h" #include "llist.h" #include "fileinfo.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -62,15 +59,10 @@ void Curl_wildcard_dtor(struct WildcardData *wc) wc->filelist = NULL; } - if(wc->path) { - free(wc->path); - wc->path = NULL; - } - - if(wc->pattern) { - free(wc->pattern); - wc->pattern = NULL; - } + free(wc->path); + wc->path = NULL; + free(wc->pattern); + wc->pattern = NULL; wc->customptr = NULL; wc->state = CURLWC_INIT; diff --git a/lib/x509asn1.c b/lib/x509asn1.c index 1f87155..a3dfd64 100644 --- a/lib/x509asn1.c +++ b/lib/x509asn1.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,8 @@ #include "curl_setup.h" -#if defined(USE_QSOSSL) || defined(USE_GSKIT) || defined(USE_NSS) +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_CYASSL) #include <curl/curl.h> #include "urldata.h" @@ -33,10 +34,7 @@ #include "inet_pton.h" #include "curl_base64.h" #include "x509asn1.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -122,6 +120,7 @@ const char * Curl_getASN1Element(curl_asn1Element * elem, return (const char *) NULL; /* Process header byte. */ + elem->header = beg; b = (unsigned char) *beg++; elem->constructed = (b & 0x20) != 0; elem->class = (b >> 6) & 3; @@ -211,7 +210,6 @@ static const char * octet2str(const char * beg, const char * end) } 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. */ @@ -300,8 +298,10 @@ utf8asn1str(char * * to, int type, const char * from, const char * end) 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++; } @@ -539,8 +539,6 @@ static const char * UTime2str(const char * beg, const char * end) const char * Curl_ASN1tostr(curl_asn1Element * elem, int type) { - static const char zero = '\0'; - /* Convert an ASN.1 element to a printable string. Return the dynamically allocated string, or NULL if an error occurs. */ @@ -561,7 +559,7 @@ const char * Curl_ASN1tostr(curl_asn1Element * elem, int type) case CURL_ASN1_OCTET_STRING: return octet2str(elem->beg, elem->end); case CURL_ASN1_NULL: - return strdup(&zero); + return strdup(""); case CURL_ASN1_OBJECT_IDENTIFIER: return OID2str(elem->beg, elem->end, TRUE); case CURL_ASN1_UTC_TIME: @@ -682,6 +680,7 @@ void Curl_parseX509(curl_X509certificate * cert, Syntax is assumed to have already been checked by the SSL backend. See RFC 5280. */ + cert->certificate.header = NULL; cert->certificate.beg = beg; cert->certificate.end = end; @@ -701,6 +700,7 @@ void Curl_parseX509(curl_X509certificate * cert, 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 = Curl_getASN1Element(&elem, beg, end); @@ -720,15 +720,19 @@ void Curl_parseX509(curl_X509certificate * cert, /* Get subject. */ beg = Curl_getASN1Element(&cert->subject, beg, end); /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ - beg = Curl_getASN1Element(&elem, beg, end); + beg = Curl_getASN1Element(&cert->subjectPublicKeyInfo, beg, end); ccp = Curl_getASN1Element(&cert->subjectPublicKeyAlgorithm, - elem.beg, elem.end); - Curl_getASN1Element(&cert->subjectPublicKey, ccp, elem.end); + cert->subjectPublicKeyInfo.beg, + cert->subjectPublicKeyInfo.end); + Curl_getASN1Element(&cert->subjectPublicKey, ccp, + cert->subjectPublicKeyInfo.end); /* 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 = Curl_getASN1Element(&elem, beg, end); @@ -771,6 +775,7 @@ static const char * dumpAlgo(curl_asn1Element * param, /* Get algorithm parameters and return algorithm name. */ beg = Curl_getASN1Element(&oid, beg, end); + param->header = NULL; param->tag = 0; param->beg = param->end = end; if(beg < end) @@ -816,7 +821,7 @@ static void do_pubkey(struct SessionHandle * data, int certnum, /* Compute key length. */ for(q = elem.beg; !*q && q < elem.end; q++) ; - len = (elem.end - q) * 8; + len = (unsigned long)((elem.end - q) * 8); if(len) for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) len--; @@ -871,7 +876,7 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, char * cp1; size_t cl1; char * cp2; - CURLcode cc; + CURLcode result; unsigned long version; size_t i; size_t j; @@ -985,11 +990,11 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, free((char *) ccp); /* Generate PEM certificate. */ - cc = Curl_base64_encode(data, cert.certificate.beg, - cert.certificate.end - cert.certificate.beg, - &cp1, &cl1); - if(cc != CURLE_OK) - return cc; + 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 <max 64 base64 characters>\n @@ -1019,9 +1024,9 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, return CURLE_OK; } -#endif /* USE_QSOSSL or USE_GSKIT or USE_NSS */ +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_CYASSL */ -#if defined(USE_QSOSSL) || defined(USE_GSKIT) +#if defined(USE_GSKIT) static const char * checkOID(const char * beg, const char * end, const char * oid) @@ -1111,8 +1116,7 @@ CURLcode Curl_verifyhost(struct connectdata * conn, if(len > 0) if(strlen(dnsname) == (size_t) len) i = Curl_cert_hostcheck((const char *) dnsname, conn->host.name); - if(dnsname) - free(dnsname); + free(dnsname); if(!i) return CURLE_PEER_FAILED_VERIFICATION; matched = i; @@ -1140,6 +1144,7 @@ CURLcode Curl_verifyhost(struct connectdata * conn, } /* 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 @@ -1180,4 +1185,4 @@ CURLcode Curl_verifyhost(struct connectdata * conn, return CURLE_PEER_FAILED_VERIFICATION; } -#endif /* USE_QSOSSL or USE_GSKIT */ +#endif /* USE_GSKIT */ diff --git a/lib/x509asn1.h b/lib/x509asn1.h index 1741d6d..eb23e50 100644 --- a/lib/x509asn1.h +++ b/lib/x509asn1.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,7 +25,8 @@ #include "curl_setup.h" -#if defined(USE_QSOSSL) || defined(USE_GSKIT) || defined(USE_NSS) +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_CYASSL) #include "urldata.h" @@ -76,8 +77,9 @@ /* ASN.1 parsed element. */ typedef struct { + const char * header; /* Pointer to header byte. */ const char * beg; /* Pointer to element data. */ - const char * end; /* Pointer to 1st byte after 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. */ @@ -102,6 +104,7 @@ typedef struct { curl_asn1Element notBefore; curl_asn1Element notAfter; curl_asn1Element subject; + curl_asn1Element subjectPublicKeyInfo; curl_asn1Element subjectPublicKeyAlgorithm; curl_asn1Element subjectPublicKey; curl_asn1Element issuerUniqueID; @@ -125,5 +128,5 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, int certnum, CURLcode Curl_verifyhost(struct connectdata * conn, const char * beg, const char * end); -#endif /* USE_QSOSSL or USE_GSKIT or USE_NSS */ +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_CYASSL */ #endif /* HEADER_CURL_X509ASN1_H */ |