summaryrefslogtreecommitdiff
path: root/libs/libssh2/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2019-02-08 13:53:20 +0300
committerGeorge Hazan <ghazan@miranda.im>2019-02-08 13:53:20 +0300
commit9e3fe90d27add4dbca66c534e3a557502192d9bc (patch)
tree2b153bcb23cefe334415546e4043d5c8bc88377e /libs/libssh2/src
parenta41c3238e5dadf27751c5b0e50a5dbe6c52b3804 (diff)
libssh2 added to Miranda to support SFTP transfers in libcurl
Diffstat (limited to 'libs/libssh2/src')
-rw-r--r--libs/libssh2/src/CMakeLists.txt423
-rw-r--r--libs/libssh2/src/Makefile.am68
-rw-r--r--libs/libssh2/src/Makefile.in804
-rw-r--r--libs/libssh2/src/NMakefile30
-rw-r--r--libs/libssh2/src/agent.c811
-rw-r--r--libs/libssh2/src/channel.c2621
-rw-r--r--libs/libssh2/src/channel.h141
-rw-r--r--libs/libssh2/src/comp.c366
-rw-r--r--libs/libssh2/src/comp.h45
-rw-r--r--libs/libssh2/src/crypt.c336
-rw-r--r--libs/libssh2/src/crypto.h150
-rw-r--r--libs/libssh2/src/global.c78
-rw-r--r--libs/libssh2/src/hostkey.c573
-rw-r--r--libs/libssh2/src/keepalive.c99
-rw-r--r--libs/libssh2/src/kex.c2794
-rw-r--r--libs/libssh2/src/knownhost.c1245
-rw-r--r--libs/libssh2/src/libgcrypt.c627
-rw-r--r--libs/libssh2/src/libgcrypt.h183
-rw-r--r--libs/libssh2/src/libssh2.pc.in17
-rw-r--r--libs/libssh2/src/libssh2_config.h50
-rw-r--r--libs/libssh2/src/libssh2_config.h.in261
-rw-r--r--libs/libssh2/src/libssh2_config_cmake.h.in105
-rw-r--r--libs/libssh2/src/libssh2_priv.h1066
-rw-r--r--libs/libssh2/src/mac.c414
-rw-r--r--libs/libssh2/src/mac.h67
-rw-r--r--libs/libssh2/src/mbedtls.c606
-rw-r--r--libs/libssh2/src/mbedtls.h371
-rw-r--r--libs/libssh2/src/misc.c645
-rw-r--r--libs/libssh2/src/misc.h96
-rw-r--r--libs/libssh2/src/openssl.c1192
-rw-r--r--libs/libssh2/src/openssl.h293
-rw-r--r--libs/libssh2/src/packet.c1267
-rw-r--r--libs/libssh2/src/packet.h76
-rw-r--r--libs/libssh2/src/pem.c324
-rw-r--r--libs/libssh2/src/publickey.c1059
-rw-r--r--libs/libssh2/src/scp.c1126
-rw-r--r--libs/libssh2/src/session.c1794
-rw-r--r--libs/libssh2/src/session.h93
-rw-r--r--libs/libssh2/src/sftp.c3466
-rw-r--r--libs/libssh2/src/sftp.h237
-rw-r--r--libs/libssh2/src/stdafx.cxx2
-rw-r--r--libs/libssh2/src/stdafx.h1
-rw-r--r--libs/libssh2/src/transport.c891
-rw-r--r--libs/libssh2/src/transport.h87
-rw-r--r--libs/libssh2/src/userauth.c1917
-rw-r--r--libs/libssh2/src/userauth.h50
-rw-r--r--libs/libssh2/src/version.c54
-rw-r--r--libs/libssh2/src/wincng.c2076
-rw-r--r--libs/libssh2/src/wincng.h551
49 files changed, 31648 insertions, 0 deletions
diff --git a/libs/libssh2/src/CMakeLists.txt b/libs/libssh2/src/CMakeLists.txt
new file mode 100644
index 0000000000..6401acff24
--- /dev/null
+++ b/libs/libssh2/src/CMakeLists.txt
@@ -0,0 +1,423 @@
+# Copyright (c) 2014 Alexander Lamaison <alexander.lamaison@gmail.com>
+#
+# Redistribution and use in source and binary forms,
+# with or without modification, are permitted provided
+# that the following conditions are met:
+#
+# Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# Neither the name of the copyright holder nor the names
+# of any other contributors may be used to endorse or
+# promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+# OF SUCH DAMAGE.
+
+include(CheckFunctionExists)
+include(CheckSymbolExists)
+include(CheckFunctionExistsMayNeedLibrary)
+include(CheckIncludeFiles)
+include(CheckTypeSize)
+include(CheckSymbolExists)
+include(CheckNonblockingSocketSupport)
+include(SocketLibraries)
+
+## Cryptography backend choice
+
+set(CRYPTO_BACKEND
+ ""
+ CACHE
+ STRING
+ "The backend to use for cryptography: OpenSSL, Libgcrypt or WinCNG, mbedTLS
+or empty to try any available")
+
+# If the crypto backend was given, rather than searching for the first
+# we are able to find, the find_package commands must abort configuration
+# and report to the user.
+if(CRYPTO_BACKEND)
+ set(SPECIFIC_CRYPTO_REQUIREMENT REQUIRED)
+endif()
+
+if(CRYPTO_BACKEND STREQUAL "OpenSSL" OR NOT CRYPTO_BACKEND)
+
+ find_package(OpenSSL ${SPECIFIC_CRYPTO_REQUIREMENT})
+
+ if(OPENSSL_FOUND)
+ set(CRYPTO_BACKEND "OpenSSL")
+ set(CRYPTO_SOURCES openssl.c openssl.h)
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS LIBSSH2_OPENSSL)
+ list(APPEND PRIVATE_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
+ list(APPEND LIBRARIES ${OPENSSL_LIBRARIES})
+ list(APPEND PC_REQUIRES_PRIVATE libssl libcrypto)
+
+ if (WIN32)
+ # Statically linking to OpenSSL requires crypt32 for some Windows APIs.
+ # This should really be handled by FindOpenSSL.cmake.
+ list(APPEND LIBRARIES crypt32)
+ list(APPEND PC_LIBS -lcrypt32)
+
+ find_file(DLL_LIBEAY32
+ NAMES libeay32.dll crypto.dll
+ HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}
+ PATH_SUFFIXES bin)
+ if (NOT DLL_LIBEAY32)
+ message(WARNING
+ "Unable to find OpenSSL libeay32 DLL, executables may not run")
+ endif()
+
+ find_file(DLL_SSLEAY32
+ NAMES ssleay32.dll ssl.dll
+ HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}
+ PATH_SUFFIXES bin)
+ if (NOT DLL_SSLEAY32)
+ message(WARNING
+ "Unable to find OpenSSL ssleay32 DLL, executables may not run")
+ endif()
+
+ if(DLL_LIBEAY32 AND DLL_SSLEAY32)
+ list(APPEND _RUNTIME_DEPENDENCIES ${DLL_LIBEAY32} ${DLL_SSLEAY32})
+ endif()
+ endif()
+
+ # Not all OpenSSL have AES-CTR functions.
+ set(SAVE_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
+ check_function_exists(EVP_aes_128_ctr HAVE_EVP_AES_128_CTR)
+ set(CMAKE_REQUIRED_LIBRARIES ${SAVE_CMAKE_REQUIRED_LIBRARIES})
+ endif()
+endif()
+
+if(CRYPTO_BACKEND STREQUAL "Libgcrypt" OR NOT CRYPTO_BACKEND)
+
+ find_package(Libgcrypt ${SPECIFIC_CRYPTO_REQUIREMENT})
+
+ if(LIBGCRYPT_FOUND)
+ set(CRYPTO_BACKEND "Libgcrypt")
+ set(CRYPTO_SOURCES libgcrypt.c libgcrypt.h)
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS LIBSSH2_LIBGCRYPT)
+ list(APPEND PRIVATE_INCLUDE_DIRECTORIES ${LIBGCRYPT_INCLUDE_DIRS})
+ list(APPEND LIBRARIES ${LIBGCRYPT_LIBRARIES})
+ list(APPEND PC_LIBS -lgcrypt)
+ endif()
+endif()
+
+if(CRYPTO_BACKEND STREQUAL "WinCNG" OR NOT CRYPTO_BACKEND)
+
+ # The check actually compiles the header. This requires windows.h.
+ check_include_files("windows.h;bcrypt.h" HAVE_BCRYPT_H)
+
+ if(HAVE_BCRYPT_H)
+ set(CRYPTO_BACKEND "WinCNG")
+ set(CRYPTO_SOURCES wincng.c wincng.h)
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS LIBSSH2_WINCNG)
+
+ set(HAVE_LIBCRYPT32 TRUE)
+ list(APPEND LIBRARIES bcrypt)
+ list(APPEND PC_LIBS -lbcrypt)
+
+ check_include_files(ntdef.h HAVE_NTDEF_H)
+ check_include_files(ntstatus.h HAVE_NTSTATUS_H)
+
+ # Reading keys from files is optional and depends on Wincrypt
+ check_include_files("windows.h;wincrypt.h" HAVE_WINCRYPT_H)
+
+ if(HAVE_WINCRYPT_H)
+ list(APPEND LIBRARIES crypt32)
+ list(APPEND PC_LIBS -lcrypt32)
+ endif()
+
+ elseif(${SPECIFIC_CRYPTO_REQUIREMENT} STREQUAL ${REQUIRED})
+ message(FATAL_ERROR "WinCNG not available")
+ endif()
+endif()
+
+if(CRYPTO_BACKEND STREQUAL "mbedTLS" OR NOT CRYPTO_BACKEND)
+
+ find_package(mbedTLS ${SPECIFIC_CRYPTO_REQUIREMENT})
+
+ if(MBEDTLS_FOUND)
+ set(CRYPTO_BACKEND "mbedTLS")
+ set(CRYPTO_SOURCES mbedtls.c mbedtls.h)
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS LIBSSH2_MBEDTLS)
+ list(APPEND PRIVATE_INCLUDE_DIRECTORIES ${MBEDTLS_INCLUDE_DIR})
+ list(APPEND LIBRARIES ${MBEDTLS_LIBRARIES})
+ list(APPEND PC_LIBS -lmbedcrypto)
+ link_directories(${MBEDTLS_LIBRARY_DIR})
+ endif()
+endif()
+
+if(NOT CRYPTO_BACKEND)
+ message(FATAL_ERROR "No suitable cryptography backend found.")
+endif()
+
+## Library definition
+
+include(GNUInstallDirs)
+set(SOURCES
+ ${CRYPTO_SOURCES}
+ agent.c
+ channel.c
+ channel.h
+ comp.c
+ comp.h
+ crypt.c
+ crypto.h
+ global.c
+ hostkey.c
+ keepalive.c
+ kex.c
+ knownhost.c
+ libssh2_priv.h
+ mac.c
+ mac.h
+ misc.c
+ misc.h
+ packet.c
+ packet.h
+ pem.c
+ publickey.c
+ scp.c
+ session.c
+ session.h
+ sftp.c
+ sftp.h
+ transport.c
+ transport.h
+ userauth.c
+ userauth.h
+ version.c)
+
+if(WIN32)
+ list(APPEND SOURCES ${PROJECT_SOURCE_DIR}/win32/libssh2.rc)
+endif()
+
+add_library(libssh2 ${SOURCES})
+# we want it to be called libssh2 on all platforms
+set_target_properties(libssh2 PROPERTIES PREFIX "")
+
+target_compile_definitions(libssh2 PRIVATE ${PRIVATE_COMPILE_DEFINITIONS})
+target_include_directories(libssh2
+ PRIVATE ${PRIVATE_INCLUDE_DIRECTORIES}
+ PUBLIC
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>)
+
+## Options
+
+option(CLEAR_MEMORY "Enable clearing of memory before being freed" ON)
+if(CLEAR_MEMORY)
+ add_definitions(-DLIBSSH2_CLEAR_MEMORY)
+endif(CLEAR_MEMORY)
+
+add_feature_info("Shared library" BUILD_SHARED_LIBS
+ "creating libssh2 as a shared library (.so/.dll)")
+
+option(ENABLE_ZLIB_COMPRESSION "Use zlib for compression")
+add_feature_info(Compression ENABLE_ZLIB_COMPRESSION
+ "using zlib for compression")
+if(ENABLE_ZLIB_COMPRESSION)
+ find_package(ZLIB REQUIRED)
+
+ target_include_directories(libssh2 PRIVATE ${ZLIB_INCLUDE_DIRS})
+ list(APPEND LIBRARIES ${ZLIB_LIBRARIES})
+ list(APPEND PC_REQUIRES_PRIVATE zlib)
+ if(ZLIB_FOUND)
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2_HAVE_ZLIB=1)
+ endif()
+endif()
+
+option(ENABLE_CRYPT_NONE "Permit \"none\" cipher -- NOT RECOMMENDED")
+add_feature_info("\"none\" cipher" ENABLE_CRYPT_NONE "")
+if(ENABLE_CRYPT_NONE)
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2_CRYPT_NONE=1)
+endif()
+
+option(ENABLE_MAC_NONE "Permit \"none\" MAC -- NOT RECOMMMENDED")
+add_feature_info("\"none\" MAC" ENABLE_MAC_NONE "")
+if(ENABLE_MAC_NONE)
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2_MAC_NONE=1)
+endif()
+
+option(ENABLE_GEX_NEW
+ "Enable diffie-hellman-group-exchange-sha1 method" ON)
+add_feature_info("diffie-hellman-group-exchange-sha1" ENABLE_GEX_NEW
+ "\"new\" diffie-hellman-group-exchange-sha1 method")
+if(ENABLE_GEX_NEW)
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2_DH_GEX_NEW=1)
+endif()
+
+# Enable debugging logging by default if the user configured a debug build
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(DEBUG_LOGGING_DEFAULT ON)
+else()
+ set(DEBUG_LOGGING_DEFAULT OFF)
+endif()
+option(ENABLE_DEBUG_LOGGING "log execution with debug trace"
+ ${DEBUG_LOGGING_DEFAULT})
+add_feature_info(Logging ENABLE_DEBUG_LOGGING
+ "Logging of execution with debug trace")
+if(ENABLE_DEBUG_LOGGING)
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2DEBUG)
+endif()
+
+## Platform checks
+check_include_files(unistd.h HAVE_UNISTD_H)
+check_include_files(inttypes.h HAVE_INTTYPES_H)
+check_include_files(stdlib.h HAVE_STDLIB_H)
+check_include_files(sys/select.h HAVE_SYS_SELECT_H)
+
+check_include_files(sys/uio.h HAVE_SYS_UIO_H)
+check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
+check_include_files(sys/ioctl.h HAVE_SYS_IOCTL_H)
+check_include_files(sys/time.h HAVE_SYS_TIME_H)
+check_include_files(sys/un.h HAVE_SYS_UN_H)
+check_include_files(windows.h HAVE_WINDOWS_H)
+check_include_files(ws2tcpip.h HAVE_WS2TCPIP_H)
+check_include_files(winsock2.h HAVE_WINSOCK2_H)
+
+check_type_size("long long" LONGLONG)
+
+if(HAVE_SYS_TIME_H)
+ check_symbol_exists(gettimeofday sys/time.h HAVE_GETTIMEOFDAY)
+else()
+ check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
+endif()
+if(HAVE_STDLIB_H)
+ check_symbol_exists(strtoll stdlib.h HAVE_STRTOLL)
+else()
+ check_function_exists(strtoll HAVE_STRTOLL)
+endif()
+if (NOT HAVE_STRTOLL)
+ # Try _strtoi64 if strtoll isn't available
+ check_symbol_exists(_strtoi64 stdlib.h HAVE_STRTOI64)
+endif()
+check_symbol_exists(snprintf stdio.h HAVE_SNPRINTF)
+
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+ ${CMAKE_SYSTEM_NAME} STREQUAL "Interix")
+ # poll() does not work on these platforms
+ #
+ # Interix: "does provide poll(), but the implementing developer must
+ # have been in a bad mood, because poll() only works on the /proc
+ # filesystem here"
+ #
+ # Mac OS X's poll has funny behaviors, like:
+ # not being able to do poll on no fildescriptors (10.3?)
+ # not being able to poll on some files (like anything in /dev)
+ # not having reliable timeout support
+ # inconsistent return of POLLHUP where other implementations give POLLIN
+ message("poll use is disabled on this platform")
+else()
+ check_function_exists(poll HAVE_POLL)
+endif()
+
+append_needed_socket_libraries(LIBRARIES)
+
+# Non-blocking socket support tests. Must be after after library tests to
+# link correctly
+set(SAVE_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+set(CMAKE_REQUIRED_LIBRARIES ${LIBRARIES})
+check_nonblocking_socket_support()
+set(CMAKE_REQUIRED_LIBRARIES ${SAVE_CMAKE_REQUIRED_LIBRARIES})
+
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/libssh2_config_cmake.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/libssh2_config.h)
+# to find generated header
+target_include_directories(libssh2 PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+
+# Check for the OS.
+# Daniel's note: this should not be necessary and we need to work to
+# get this removed.
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2_WIN32)
+elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+ target_compile_definitions(libssh2 PRIVATE LIBSSH2_DARWIN)
+endif()
+
+if(CMAKE_VERSION VERSION_LESS "2.8.12")
+ # Fall back to over-linking dependencies
+ target_link_libraries(libssh2 ${LIBRARIES})
+else()
+ target_link_libraries(libssh2 PRIVATE ${LIBRARIES})
+endif()
+
+## Installation
+
+install(FILES
+ ${PROJECT_SOURCE_DIR}/include/libssh2.h
+ ${PROJECT_SOURCE_DIR}/include/libssh2_publickey.h
+ ${PROJECT_SOURCE_DIR}/include/libssh2_sftp.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+install(TARGETS libssh2
+ EXPORT Libssh2Config
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+if(BUILD_SHARED_LIBS)
+ list(APPEND _RUNTIME_DEPENDENCIES $<TARGET_FILE:libssh2>)
+endif()
+
+set(RUNTIME_DEPENDENCIES ${_RUNTIME_DEPENDENCIES} CACHE INTERNAL
+ "Files that must be in the same directory as the executables at runtime.")
+
+# Package config
+
+## During package installation, install Libssh2Config.cmake
+install(EXPORT Libssh2Config
+ NAMESPACE Libssh2::
+ DESTINATION lib/cmake/libssh2)
+
+## During build, register directly from build tree
+# create Libssh2Config.cmake
+export(TARGETS libssh2 NAMESPACE Libssh2:: FILE Libssh2Config.cmake)
+export(PACKAGE Libssh2) # register it
+
+## Export a .pc file for client projects not using CMaek
+if(PC_REQUIRES_PRIVATE)
+ string(REPLACE ";" "," PC_REQUIRES_PRIVATE "${PC_REQUIRES_PRIVATE}")
+endif()
+if(PC_LIBS)
+ string(REPLACE ";" " " PC_LIBS "${PC_LIBS}")
+endif()
+configure_file(libssh2.pc.in libssh2.pc @ONLY)
+install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/libssh2.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+## Versioning
+
+set_target_properties(libssh2 PROPERTIES
+ SOVERSION 1
+ VERSION 1.0.1)
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/Libssh2ConfigVersion.cmake
+ VERSION "${LIBSSH2_VERSION_MAJOR}.${LIBSSH2_VERSION_MINOR}.${LIBSSH2_VERSION_PATCH}"
+ COMPATIBILITY SameMajorVersion)
+install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/Libssh2ConfigVersion.cmake
+ DESTINATION lib/cmake/libssh2)
diff --git a/libs/libssh2/src/Makefile.am b/libs/libssh2/src/Makefile.am
new file mode 100644
index 0000000000..c14dc7cb3d
--- /dev/null
+++ b/libs/libssh2/src/Makefile.am
@@ -0,0 +1,68 @@
+# $Id: Makefile.am,v 1.21 2009/05/07 17:21:56 bagder Exp $
+AUTOMAKE_OPTIONS = foreign nostdinc
+
+# Get the CRYPTO_CSOURCES and CRYPTO_HHEADERS defines
+if OPENSSL
+include ../Makefile.OpenSSL.inc
+endif
+if LIBGCRYPT
+include ../Makefile.libgcrypt.inc
+endif
+if WINCNG
+include ../Makefile.WinCNG.inc
+endif
+if OS400QC3
+include ../Makefile.os400qc3.inc
+endif
+if MBEDTLS
+include ../Makefile.mbedTLS.inc
+endif
+
+# Makefile.inc provides the CSOURCES and HHEADERS defines
+include ../Makefile.inc
+
+libssh2_la_SOURCES = $(CSOURCES) $(HHEADERS)
+
+EXTRA_DIST = libssh2_config.h.in libssh2_config_cmake.h.in libssh2.pc.in
+EXTRA_DIST += CMakeLists.txt NMakefile
+
+lib_LTLIBRARIES = libssh2.la
+
+# srcdir/include for the shipped headers
+# builddir/src for the generated config header when building out of the source
+# tree
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/src
+
+VERSION=-version-info 1:1:0
+
+# This flag accepts an argument of the form current[:revision[:age]]. So,
+# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to
+# 1.
+#
+# If either revision or age are omitted, they default to 0. Also note that age
+# must be less than or equal to the current interface number.
+#
+# Here are a set of rules to help you update your library version information:
+#
+# 1.Start with version information of 0:0:0 for each libtool library.
+#
+# 2.Update the version information only immediately before a public release of
+# your software. More frequent updates are unnecessary, and only guarantee
+# that the current interface number gets larger faster.
+#
+# 3.If the library source code has changed at all since the last update, then
+# increment revision (c:r+1:a)
+#
+# 4.If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0. (c+1:r=0:a)
+#
+# 5.If any interfaces have been added since the last public release, then
+# increment age. (c:r:a+1)
+#
+# 6.If any interfaces have been removed since the last public release, then
+# set age to 0. (c:r:a=0)
+#
+
+libssh2_la_LDFLAGS = $(VERSION) -no-undefined \
+ -export-symbols-regex '^libssh2_.*' \
+ $(LTLIBGCRYPT) $(LTLIBSSL) $(LTLIBZ)
diff --git a/libs/libssh2/src/Makefile.in b/libs/libssh2/src/Makefile.in
new file mode 100644
index 0000000000..9e59967eeb
--- /dev/null
+++ b/libs/libssh2/src/Makefile.in
@@ -0,0 +1,804 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = libssh2_config.h \
+ $(top_builddir)/example/libssh2_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libssh2_la_LIBADD =
+am__libssh2_la_SOURCES_DIST = channel.c comp.c crypt.c hostkey.c kex.c \
+ mac.c misc.c packet.c publickey.c scp.c session.c sftp.c \
+ userauth.c transport.c version.c knownhost.c agent.c \
+ libgcrypt.c mbedtls.c openssl.c os400qc3.c wincng.c pem.c \
+ keepalive.c global.c libssh2_priv.h libgcrypt.h mbedtls.h \
+ openssl.h os400qc3.h wincng.h transport.h channel.h comp.h \
+ mac.h misc.h packet.h userauth.h session.h sftp.h crypto.h
+@LIBGCRYPT_FALSE@@MBEDTLS_FALSE@@OPENSSL_FALSE@@OS400QC3_FALSE@@WINCNG_TRUE@am__objects_1 = wincng.lo
+@LIBGCRYPT_FALSE@@MBEDTLS_FALSE@@OPENSSL_FALSE@@OS400QC3_TRUE@am__objects_1 = os400qc3.lo
+@LIBGCRYPT_FALSE@@MBEDTLS_FALSE@@OPENSSL_TRUE@am__objects_1 = \
+@LIBGCRYPT_FALSE@@MBEDTLS_FALSE@@OPENSSL_TRUE@ openssl.lo
+@LIBGCRYPT_FALSE@@MBEDTLS_TRUE@am__objects_1 = mbedtls.lo
+@LIBGCRYPT_TRUE@am__objects_1 = libgcrypt.lo
+am__objects_2 = channel.lo comp.lo crypt.lo hostkey.lo kex.lo mac.lo \
+ misc.lo packet.lo publickey.lo scp.lo session.lo sftp.lo \
+ userauth.lo transport.lo version.lo knownhost.lo agent.lo \
+ $(am__objects_1) pem.lo keepalive.lo global.lo
+am__objects_3 =
+am__objects_4 = $(am__objects_3)
+am_libssh2_la_OBJECTS = $(am__objects_2) $(am__objects_4)
+libssh2_la_OBJECTS = $(am_libssh2_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libssh2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libssh2_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libssh2_la_SOURCES)
+DIST_SOURCES = $(am__libssh2_la_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
+ $(LISP)libssh2_config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/../Makefile.OpenSSL.inc \
+ $(srcdir)/../Makefile.WinCNG.inc $(srcdir)/../Makefile.inc \
+ $(srcdir)/../Makefile.libgcrypt.inc \
+ $(srcdir)/../Makefile.mbedTLS.inc \
+ $(srcdir)/../Makefile.os400qc3.inc $(srcdir)/Makefile.in \
+ $(srcdir)/libssh2_config.h.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+HAVE_LIBBCRYPT = @HAVE_LIBBCRYPT@
+HAVE_LIBCRYPT32 = @HAVE_LIBCRYPT32@
+HAVE_LIBGCRYPT = @HAVE_LIBGCRYPT@
+HAVE_LIBMBEDTLS = @HAVE_LIBMBEDTLS@
+HAVE_LIBSSL = @HAVE_LIBSSL@
+HAVE_LIBZ = @HAVE_LIBZ@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBBCRYPT = @LIBBCRYPT@
+LIBBCRYPT_PREFIX = @LIBBCRYPT_PREFIX@
+LIBCRYPT32 = @LIBCRYPT32@
+LIBCRYPT32_PREFIX = @LIBCRYPT32_PREFIX@
+LIBGCRYPT = @LIBGCRYPT@
+LIBGCRYPT_PREFIX = @LIBGCRYPT_PREFIX@
+LIBMBEDTLS = @LIBMBEDTLS@
+LIBMBEDTLS_PREFIX = @LIBMBEDTLS_PREFIX@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSREQUIRED = @LIBSREQUIRED@
+LIBSSH2VER = @LIBSSH2VER@
+LIBSSL = @LIBSSL@
+LIBSSL_PREFIX = @LIBSSL_PREFIX@
+LIBTOOL = @LIBTOOL@
+LIBZ = @LIBZ@
+LIBZ_PREFIX = @LIBZ_PREFIX@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBBCRYPT = @LTLIBBCRYPT@
+LTLIBCRYPT32 = @LTLIBCRYPT32@
+LTLIBGCRYPT = @LTLIBGCRYPT@
+LTLIBMBEDTLS = @LTLIBMBEDTLS@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBSSL = @LTLIBSSL@
+LTLIBZ = @LTLIBZ@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSHD = @SSHD@
+STRIP = @STRIP@
+VERSION = -version-info 1:1:0
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# $Id: Makefile.am,v 1.21 2009/05/07 17:21:56 bagder Exp $
+AUTOMAKE_OPTIONS = foreign nostdinc
+@LIBGCRYPT_TRUE@CRYPTO_CSOURCES = libgcrypt.c
+@MBEDTLS_TRUE@CRYPTO_CSOURCES = mbedtls.c
+@OPENSSL_TRUE@CRYPTO_CSOURCES = openssl.c
+@OS400QC3_TRUE@CRYPTO_CSOURCES = os400qc3.c
+@WINCNG_TRUE@CRYPTO_CSOURCES = wincng.c
+@LIBGCRYPT_TRUE@CRYPTO_HHEADERS = libgcrypt.h
+@MBEDTLS_TRUE@CRYPTO_HHEADERS = mbedtls.h
+@OPENSSL_TRUE@CRYPTO_HHEADERS = openssl.h
+@OS400QC3_TRUE@CRYPTO_HHEADERS = os400qc3.h
+@WINCNG_TRUE@CRYPTO_HHEADERS = wincng.h
+CSOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c misc.c \
+ packet.c publickey.c scp.c session.c sftp.c userauth.c transport.c \
+ version.c knownhost.c agent.c $(CRYPTO_CSOURCES) pem.c keepalive.c global.c
+
+HHEADERS = libssh2_priv.h $(CRYPTO_HHEADERS) transport.h channel.h comp.h \
+ mac.h misc.h packet.h userauth.h session.h sftp.h crypto.h
+
+
+# Get the CRYPTO_CSOURCES and CRYPTO_HHEADERS defines
+
+# Makefile.inc provides the CSOURCES and HHEADERS defines
+libssh2_la_SOURCES = $(CSOURCES) $(HHEADERS)
+EXTRA_DIST = libssh2_config.h.in libssh2_config_cmake.h.in \
+ libssh2.pc.in CMakeLists.txt NMakefile
+lib_LTLIBRARIES = libssh2.la
+
+# srcdir/include for the shipped headers
+# builddir/src for the generated config header when building out of the source
+# tree
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/src
+
+# This flag accepts an argument of the form current[:revision[:age]]. So,
+# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to
+# 1.
+#
+# If either revision or age are omitted, they default to 0. Also note that age
+# must be less than or equal to the current interface number.
+#
+# Here are a set of rules to help you update your library version information:
+#
+# 1.Start with version information of 0:0:0 for each libtool library.
+#
+# 2.Update the version information only immediately before a public release of
+# your software. More frequent updates are unnecessary, and only guarantee
+# that the current interface number gets larger faster.
+#
+# 3.If the library source code has changed at all since the last update, then
+# increment revision (c:r+1:a)
+#
+# 4.If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0. (c+1:r=0:a)
+#
+# 5.If any interfaces have been added since the last public release, then
+# increment age. (c:r:a+1)
+#
+# 6.If any interfaces have been removed since the last public release, then
+# set age to 0. (c:r:a=0)
+#
+libssh2_la_LDFLAGS = $(VERSION) -no-undefined \
+ -export-symbols-regex '^libssh2_.*' \
+ $(LTLIBGCRYPT) $(LTLIBSSL) $(LTLIBZ)
+
+all: libssh2_config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/../Makefile.OpenSSL.inc $(srcdir)/../Makefile.libgcrypt.inc $(srcdir)/../Makefile.WinCNG.inc $(srcdir)/../Makefile.os400qc3.inc $(srcdir)/../Makefile.mbedTLS.inc $(srcdir)/../Makefile.inc $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+$(srcdir)/../Makefile.OpenSSL.inc $(srcdir)/../Makefile.libgcrypt.inc $(srcdir)/../Makefile.WinCNG.inc $(srcdir)/../Makefile.os400qc3.inc $(srcdir)/../Makefile.mbedTLS.inc $(srcdir)/../Makefile.inc $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+libssh2_config.h: stamp-h1
+ @test -f $@ || rm -f stamp-h1
+ @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/libssh2_config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status src/libssh2_config.h
+$(srcdir)/libssh2_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f libssh2_config.h stamp-h1
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libssh2.la: $(libssh2_la_OBJECTS) $(libssh2_la_DEPENDENCIES) $(EXTRA_libssh2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libssh2_la_LINK) -rpath $(libdir) $(libssh2_la_OBJECTS) $(libssh2_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agent.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostkey.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keepalive.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knownhost.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgcrypt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbedtls.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os400qc3.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pem.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/publickey.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transport.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userauth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wincng.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) libssh2_config.h
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-hdr distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libs/libssh2/src/NMakefile b/libs/libssh2/src/NMakefile
new file mode 100644
index 0000000000..0c4853f1df
--- /dev/null
+++ b/libs/libssh2/src/NMakefile
@@ -0,0 +1,30 @@
+!include "win32/config.mk"
+
+!include "win32/objects.mk"
+
+CFLAGS=$(CFLAGS)
+
+AR = lib
+ARFLAGS = -nologo /LTCG
+
+RESOURCE=$(INTDIR)\libssh2.res
+DLL=libssh2$(SUFFIX).dll
+STATICLIB=$(INTDIR)\libssh2.lib
+
+!if "$(BUILD_STATIC_LIB)" == ""
+all: $(DLL)
+!else
+all: $(STATICLIB)
+!endif
+
+$(DLL): $(OBJECTS) $(RESOURCE)
+ $(CC) -o $(DLL) $(DLLFLAGS) $(OBJECTS) $(RESOURCE) $(LIBS)
+
+$(STATICLIB): $(OBJECTS)
+ $(AR) $(ARFLAGS) -out:$@ $(OBJECTS)
+
+$(RESOURCE): win32\libssh2.rc
+ $(RC) $(RCFLAGS) /Fo"$@" $?
+
+!include "win32/rules.mk"
+
diff --git a/libs/libssh2/src/agent.c b/libs/libssh2/src/agent.c
new file mode 100644
index 0000000000..c2ba422b65
--- /dev/null
+++ b/libs/libssh2/src/agent.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2009 by Daiki Ueno
+ * Copyright (C) 2010-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include "misc.h"
+#include <errno.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#else
+/* Use the existence of sys/un.h as a test if Unix domain socket is
+ supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually
+ support them. */
+#undef PF_UNIX
+#endif
+#include "userauth.h"
+#include "session.h"
+
+/* Requests from client to agent for protocol 1 key operations */
+#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
+#define SSH_AGENTC_RSA_CHALLENGE 3
+#define SSH_AGENTC_ADD_RSA_IDENTITY 7
+#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
+#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9
+#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
+
+/* Requests from client to agent for protocol 2 key operations */
+#define SSH2_AGENTC_REQUEST_IDENTITIES 11
+#define SSH2_AGENTC_SIGN_REQUEST 13
+#define SSH2_AGENTC_ADD_IDENTITY 17
+#define SSH2_AGENTC_REMOVE_IDENTITY 18
+#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19
+#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25
+
+/* Key-type independent requests from client to agent */
+#define SSH_AGENTC_ADD_SMARTCARD_KEY 20
+#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21
+#define SSH_AGENTC_LOCK 22
+#define SSH_AGENTC_UNLOCK 23
+#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
+
+/* Generic replies from agent to client */
+#define SSH_AGENT_FAILURE 5
+#define SSH_AGENT_SUCCESS 6
+
+/* Replies from agent to client for protocol 1 key operations */
+#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
+#define SSH_AGENT_RSA_RESPONSE 4
+
+/* Replies from agent to client for protocol 2 key operations */
+#define SSH2_AGENT_IDENTITIES_ANSWER 12
+#define SSH2_AGENT_SIGN_RESPONSE 14
+
+/* Key constraint identifiers */
+#define SSH_AGENT_CONSTRAIN_LIFETIME 1
+#define SSH_AGENT_CONSTRAIN_CONFIRM 2
+
+/* non-blocking mode on agent connection is not yet implemented, but
+ for future use. */
+typedef enum {
+ agent_NB_state_init = 0,
+ agent_NB_state_request_created,
+ agent_NB_state_request_length_sent,
+ agent_NB_state_request_sent,
+ agent_NB_state_response_length_received,
+ agent_NB_state_response_received
+} agent_nonblocking_states;
+
+typedef struct agent_transaction_ctx {
+ unsigned char *request;
+ size_t request_len;
+ unsigned char *response;
+ size_t response_len;
+ agent_nonblocking_states state;
+} *agent_transaction_ctx_t;
+
+typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent);
+typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent,
+ agent_transaction_ctx_t transctx);
+typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent);
+
+struct agent_publickey {
+ struct list_node node;
+
+ /* this is the struct we expose externally */
+ struct libssh2_agent_publickey external;
+};
+
+struct agent_ops {
+ agent_connect_func connect;
+ agent_transact_func transact;
+ agent_disconnect_func disconnect;
+};
+
+struct _LIBSSH2_AGENT
+{
+ LIBSSH2_SESSION *session; /* the session this "belongs to" */
+
+ libssh2_socket_t fd;
+
+ struct agent_ops *ops;
+
+ struct agent_transaction_ctx transctx;
+ struct agent_publickey *identity;
+ struct list_head head; /* list of public keys */
+};
+
+#ifdef PF_UNIX
+static int
+agent_connect_unix(LIBSSH2_AGENT *agent)
+{
+ const char *path;
+ struct sockaddr_un s_un;
+
+ path = getenv("SSH_AUTH_SOCK");
+ if (!path)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
+ "no auth sock variable");
+
+ agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (agent->fd < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET,
+ "failed creating socket");
+
+ s_un.sun_family = AF_UNIX;
+ strncpy (s_un.sun_path, path, sizeof s_un.sun_path);
+ s_un.sun_path[sizeof(s_un.sun_path)-1]=0; /* make sure there's a trailing
+ zero */
+ if (connect(agent->fd, (struct sockaddr*)(&s_un), sizeof s_un) != 0) {
+ close (agent->fd);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed connecting with agent");
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+static int
+agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
+{
+ unsigned char buf[4];
+ int rc;
+
+ /* Send the length of the request */
+ if (transctx->state == agent_NB_state_request_created) {
+ _libssh2_htonu32(buf, transctx->request_len);
+ rc = LIBSSH2_SEND_FD(agent->session, agent->fd, buf, sizeof buf, 0);
+ if (rc == -EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ else if (rc < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent send failed");
+ transctx->state = agent_NB_state_request_length_sent;
+ }
+
+ /* Send the request body */
+ if (transctx->state == agent_NB_state_request_length_sent) {
+ rc = LIBSSH2_SEND_FD(agent->session, agent->fd, transctx->request,
+ transctx->request_len, 0);
+ if (rc == -EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ else if (rc < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent send failed");
+ transctx->state = agent_NB_state_request_sent;
+ }
+
+ /* Receive the length of a response */
+ if (transctx->state == agent_NB_state_request_sent) {
+ rc = LIBSSH2_RECV_FD(agent->session, agent->fd, buf, sizeof buf, 0);
+ if (rc < 0) {
+ if (rc == -EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
+ "agent recv failed");
+ }
+ transctx->response_len = _libssh2_ntohu32(buf);
+ transctx->response = LIBSSH2_ALLOC(agent->session,
+ transctx->response_len);
+ if (!transctx->response)
+ return LIBSSH2_ERROR_ALLOC;
+
+ transctx->state = agent_NB_state_response_length_received;
+ }
+
+ /* Receive the response body */
+ if (transctx->state == agent_NB_state_response_length_received) {
+ rc = LIBSSH2_RECV_FD(agent->session, agent->fd, transctx->response,
+ transctx->response_len, 0);
+ if (rc < 0) {
+ if (rc == -EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent recv failed");
+ }
+ transctx->state = agent_NB_state_response_received;
+ }
+
+ return 0;
+}
+
+static int
+agent_disconnect_unix(LIBSSH2_AGENT *agent)
+{
+ int ret;
+ ret = close(agent->fd);
+ if(ret != -1)
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+ else
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "failed closing the agent socket");
+ return LIBSSH2_ERROR_NONE;
+}
+
+struct agent_ops agent_ops_unix = {
+ agent_connect_unix,
+ agent_transact_unix,
+ agent_disconnect_unix
+};
+#endif /* PF_UNIX */
+
+#ifdef WIN32
+/* Code to talk to Pageant was taken from PuTTY.
+ *
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas
+ * Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,
+ * Markus Kuhn, Colin Watson, and CORE SDI S.A.
+ */
+#define PAGEANT_COPYDATA_ID 0x804e50ba /* random goop */
+#define PAGEANT_MAX_MSGLEN 8192
+
+static int
+agent_connect_pageant(LIBSSH2_AGENT *agent)
+{
+ HWND hwnd;
+ hwnd = FindWindow("Pageant", "Pageant");
+ if (!hwnd)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed connecting agent");
+ agent->fd = 0; /* Mark as the connection has been established */
+ return LIBSSH2_ERROR_NONE;
+}
+
+static int
+agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
+{
+ HWND hwnd;
+ char mapname[23];
+ HANDLE filemap;
+ unsigned char *p;
+ unsigned char *p2;
+ int id;
+ COPYDATASTRUCT cds;
+
+ if (!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL,
+ "illegal input");
+
+ hwnd = FindWindow("Pageant", "Pageant");
+ if (!hwnd)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "found no pageant");
+
+ sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());
+ filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+ 0, PAGEANT_MAX_MSGLEN, mapname);
+
+ if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed setting up pageant filemap");
+
+ p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
+ if (p == NULL || p2 == NULL) {
+ CloseHandle(filemap);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed to open pageant filemap for writing");
+ }
+
+ _libssh2_store_str(&p2, (const char *)transctx->request,
+ transctx->request_len);
+
+ cds.dwData = PAGEANT_COPYDATA_ID;
+ cds.cbData = 1 + strlen(mapname);
+ cds.lpData = mapname;
+
+ id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
+ if (id > 0) {
+ transctx->response_len = _libssh2_ntohu32(p);
+ if (transctx->response_len > PAGEANT_MAX_MSGLEN) {
+ UnmapViewOfFile(p);
+ CloseHandle(filemap);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "agent setup fail");
+ }
+ transctx->response = LIBSSH2_ALLOC(agent->session,
+ transctx->response_len);
+ if (!transctx->response) {
+ UnmapViewOfFile(p);
+ CloseHandle(filemap);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC,
+ "agent malloc");
+ }
+ memcpy(transctx->response, p + 4, transctx->response_len);
+ }
+
+ UnmapViewOfFile(p);
+ CloseHandle(filemap);
+ return 0;
+}
+
+static int
+agent_disconnect_pageant(LIBSSH2_AGENT *agent)
+{
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+ return 0;
+}
+
+struct agent_ops agent_ops_pageant = {
+ agent_connect_pageant,
+ agent_transact_pageant,
+ agent_disconnect_pageant
+};
+#endif /* WIN32 */
+
+static struct {
+ const char *name;
+ struct agent_ops *ops;
+} supported_backends[] = {
+#ifdef WIN32
+ {"Pageant", &agent_ops_pageant},
+#endif /* WIN32 */
+#ifdef PF_UNIX
+ {"Unix", &agent_ops_unix},
+#endif /* PF_UNIX */
+ {NULL, NULL}
+};
+
+static int
+agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract);
+ agent_transaction_ctx_t transctx = &agent->transctx;
+ struct agent_publickey *identity = agent->identity;
+ ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4;
+ ssize_t method_len;
+ unsigned char *s;
+ int rc;
+
+ /* Create a request to sign the data */
+ if (transctx->state == agent_NB_state_init) {
+ s = transctx->request = LIBSSH2_ALLOC(session, len);
+ if (!transctx->request)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "out of memory");
+
+ *s++ = SSH2_AGENTC_SIGN_REQUEST;
+ /* key blob */
+ _libssh2_store_str(&s, (const char *)identity->external.blob,
+ identity->external.blob_len);
+ /* data */
+ _libssh2_store_str(&s, (const char *)data, data_len);
+
+ /* flags */
+ _libssh2_store_u32(&s, 0);
+
+ transctx->request_len = s - transctx->request;
+ transctx->state = agent_NB_state_request_created;
+ }
+
+ /* Make sure to be re-called as a result of EAGAIN. */
+ if (*transctx->request != SSH2_AGENTC_SIGN_REQUEST)
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "illegal request");
+
+ if (!agent->ops)
+ /* if no agent has been connected, bail out */
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "agent not connected");
+
+ rc = agent->ops->transact(agent, transctx);
+ if (rc) {
+ goto error;
+ }
+ LIBSSH2_FREE(session, transctx->request);
+ transctx->request = NULL;
+
+ len = transctx->response_len;
+ s = transctx->response;
+ len--;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ if (*s != SSH2_AGENT_SIGN_RESPONSE) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s++;
+
+ /* Skip the entire length of the signature */
+ len -= 4;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s += 4;
+
+ /* Skip signing method */
+ len -= 4;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ method_len = _libssh2_ntohu32(s);
+ s += 4;
+ len -= method_len;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s += method_len;
+
+ /* Read the signature */
+ len -= 4;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ *sig_len = _libssh2_ntohu32(s);
+ s += 4;
+ len -= *sig_len;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+
+ *sig = LIBSSH2_ALLOC(session, *sig_len);
+ if (!*sig) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ goto error;
+ }
+ memcpy(*sig, s, *sig_len);
+
+ error:
+ LIBSSH2_FREE(session, transctx->request);
+ transctx->request = NULL;
+
+ LIBSSH2_FREE(session, transctx->response);
+ transctx->response = NULL;
+
+ return _libssh2_error(session, rc, "agent sign failure");
+}
+
+static int
+agent_list_identities(LIBSSH2_AGENT *agent)
+{
+ agent_transaction_ctx_t transctx = &agent->transctx;
+ ssize_t len, num_identities;
+ unsigned char *s;
+ int rc;
+ unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES;
+
+ /* Create a request to list identities */
+ if (transctx->state == agent_NB_state_init) {
+ transctx->request = &c;
+ transctx->request_len = 1;
+ transctx->state = agent_NB_state_request_created;
+ }
+
+ /* Make sure to be re-called as a result of EAGAIN. */
+ if (*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
+ "illegal agent request");
+
+ if (!agent->ops)
+ /* if no agent has been connected, bail out */
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
+ "agent not connected");
+
+ rc = agent->ops->transact(agent, transctx);
+ if (rc) {
+ goto error;
+ }
+ transctx->request = NULL;
+
+ len = transctx->response_len;
+ s = transctx->response;
+ len--;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ if (*s != SSH2_AGENT_IDENTITIES_ANSWER) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s++;
+
+ /* Read the length of identities */
+ len -= 4;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ num_identities = _libssh2_ntohu32(s);
+ s += 4;
+
+ while (num_identities--) {
+ struct agent_publickey *identity;
+ ssize_t comment_len;
+
+ /* Read the length of the blob */
+ len -= 4;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ identity = LIBSSH2_ALLOC(agent->session, sizeof *identity);
+ if (!identity) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ goto error;
+ }
+ identity->external.blob_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* Read the blob */
+ len -= identity->external.blob_len;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+
+ identity->external.blob = LIBSSH2_ALLOC(agent->session,
+ identity->external.blob_len);
+ if (!identity->external.blob) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+ memcpy(identity->external.blob, s, identity->external.blob_len);
+ s += identity->external.blob_len;
+
+ /* Read the length of the comment */
+ len -= 4;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ LIBSSH2_FREE(agent->session, identity->external.blob);
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+ comment_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* Read the comment */
+ len -= comment_len;
+ if (len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ LIBSSH2_FREE(agent->session, identity->external.blob);
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+
+ identity->external.comment = LIBSSH2_ALLOC(agent->session,
+ comment_len + 1);
+ if (!identity->external.comment) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ LIBSSH2_FREE(agent->session, identity->external.blob);
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+ identity->external.comment[comment_len] = '\0';
+ memcpy(identity->external.comment, s, comment_len);
+ s += comment_len;
+
+ _libssh2_list_add(&agent->head, &identity->node);
+ }
+ error:
+ LIBSSH2_FREE(agent->session, transctx->response);
+ transctx->response = NULL;
+
+ return _libssh2_error(agent->session, rc,
+ "agent list id failed");
+}
+
+static void
+agent_free_identities(LIBSSH2_AGENT *agent) {
+ struct agent_publickey *node;
+ struct agent_publickey *next;
+
+ for (node = _libssh2_list_first(&agent->head); node; node = next) {
+ next = _libssh2_list_next(&node->node);
+ LIBSSH2_FREE(agent->session, node->external.blob);
+ LIBSSH2_FREE(agent->session, node->external.comment);
+ LIBSSH2_FREE(agent->session, node);
+ }
+ _libssh2_list_init(&agent->head);
+}
+
+#define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
+/*
+ * agent_publickey_to_external()
+ *
+ * Copies data from the internal to the external representation struct.
+ *
+ */
+static struct libssh2_agent_publickey *
+agent_publickey_to_external(struct agent_publickey *node)
+{
+ struct libssh2_agent_publickey *ext = &node->external;
+
+ ext->magic = AGENT_PUBLICKEY_MAGIC;
+ ext->node = node;
+
+ return ext;
+}
+
+/*
+ * libssh2_agent_init
+ *
+ * Init an ssh-agent handle. Returns the pointer to the handle.
+ *
+ */
+LIBSSH2_API LIBSSH2_AGENT *
+libssh2_agent_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_AGENT *agent;
+
+ agent = LIBSSH2_CALLOC(session, sizeof *agent);
+ if (!agent) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate space for agent connection");
+ return NULL;
+ }
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+ agent->session = session;
+ _libssh2_list_init(&agent->head);
+
+ return agent;
+}
+
+/*
+ * libssh2_agent_connect()
+ *
+ * Connect to an ssh-agent.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_connect(LIBSSH2_AGENT *agent)
+{
+ int i, rc = -1;
+ for (i = 0; supported_backends[i].name; i++) {
+ agent->ops = supported_backends[i].ops;
+ rc = (agent->ops->connect)(agent);
+ if (!rc)
+ return 0;
+ }
+ return rc;
+}
+
+/*
+ * libssh2_agent_list_identities()
+ *
+ * Request ssh-agent to list identities.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
+{
+ memset(&agent->transctx, 0, sizeof agent->transctx);
+ /* Abondon the last fetched identities */
+ agent_free_identities(agent);
+ return agent_list_identities(agent);
+}
+
+/*
+ * libssh2_agent_get_identity()
+ *
+ * Traverse the internal list of public keys. Pass NULL to 'prev' to get
+ * the first one. Or pass a pointer to the previously returned one to get the
+ * next.
+ *
+ * Returns:
+ * 0 if a fine public key was stored in 'store'
+ * 1 if end of public keys
+ * [negative] on errors
+ */
+LIBSSH2_API int
+libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
+ struct libssh2_agent_publickey **ext,
+ struct libssh2_agent_publickey *oprev)
+{
+ struct agent_publickey *node;
+ if (oprev && oprev->node) {
+ /* we have a starting point */
+ struct agent_publickey *prev = oprev->node;
+
+ /* get the next node in the list */
+ node = _libssh2_list_next(&prev->node);
+ }
+ else
+ node = _libssh2_list_first(&agent->head);
+
+ if (!node)
+ /* no (more) node */
+ return 1;
+
+ *ext = agent_publickey_to_external(node);
+
+ return 0;
+}
+
+/*
+ * libssh2_agent_userauth()
+ *
+ * Do publickey user authentication with the help of ssh-agent.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_userauth(LIBSSH2_AGENT *agent,
+ const char *username,
+ struct libssh2_agent_publickey *identity)
+{
+ void *abstract = agent;
+ int rc;
+
+ if (agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
+ memset(&agent->transctx, 0, sizeof agent->transctx);
+ agent->identity = identity->node;
+ }
+
+ BLOCK_ADJUST(rc, agent->session,
+ _libssh2_userauth_publickey(agent->session, username,
+ strlen(username),
+ identity->blob,
+ identity->blob_len,
+ agent_sign,
+ &abstract));
+ return rc;
+}
+
+/*
+ * libssh2_agent_disconnect()
+ *
+ * Close a connection to an ssh-agent.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
+{
+ if (agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET)
+ return agent->ops->disconnect(agent);
+ return 0;
+}
+
+/*
+ * libssh2_agent_free()
+ *
+ * Free an ssh-agent handle. This function also frees the internal
+ * collection of public keys.
+ */
+LIBSSH2_API void
+libssh2_agent_free(LIBSSH2_AGENT *agent) {
+ /* Allow connection freeing when the socket has lost its connection */
+ if (agent->fd != LIBSSH2_INVALID_SOCKET) {
+ libssh2_agent_disconnect(agent);
+ }
+ agent_free_identities(agent);
+ LIBSSH2_FREE(agent->session, agent);
+}
diff --git a/libs/libssh2/src/channel.c b/libs/libssh2/src/channel.c
new file mode 100644
index 0000000000..538a0ab0d9
--- /dev/null
+++ b/libs/libssh2/src/channel.c
@@ -0,0 +1,2621 @@
+/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2005 Mikhail Gusarov <dottedmag@dottedmag.net>
+ * Copyright (c) 2008-2014 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#include <assert.h>
+
+#include "channel.h"
+#include "transport.h"
+#include "packet.h"
+#include "session.h"
+
+/*
+ * _libssh2_channel_nextid
+ *
+ * Determine the next channel ID we can use at our end
+ */
+uint32_t
+_libssh2_channel_nextid(LIBSSH2_SESSION * session)
+{
+ uint32_t id = session->next_channel;
+ LIBSSH2_CHANNEL *channel;
+
+ channel = _libssh2_list_first(&session->channels);
+
+ while (channel) {
+ if (channel->local.id > id) {
+ id = channel->local.id;
+ }
+ channel = _libssh2_list_next(&channel->node);
+ }
+
+ /* This is a shortcut to avoid waiting for close packets on channels we've
+ * forgotten about, This *could* be a problem if we request and close 4
+ * billion or so channels in too rapid succession for the remote end to
+ * respond, but the worst case scenario is that some data meant for
+ * another channel Gets picked up by the new one.... Pretty unlikely all
+ * told...
+ */
+ session->next_channel = id + 1;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Allocated new channel ID#%lu",
+ id);
+ return id;
+}
+
+/*
+ * _libssh2_channel_locate
+ *
+ * Locate a channel pointer by number
+ */
+LIBSSH2_CHANNEL *
+_libssh2_channel_locate(LIBSSH2_SESSION *session, uint32_t channel_id)
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_LISTENER *l;
+
+ for(channel = _libssh2_list_first(&session->channels);
+ channel;
+ channel = _libssh2_list_next(&channel->node)) {
+ if (channel->local.id == channel_id)
+ return channel;
+ }
+
+ /* We didn't find the channel in the session, let's then check its
+ listeners since each listener may have its own set of pending channels
+ */
+ for(l = _libssh2_list_first(&session->listeners); l;
+ l = _libssh2_list_next(&l->node)) {
+ for(channel = _libssh2_list_first(&l->queue);
+ channel;
+ channel = _libssh2_list_next(&channel->node)) {
+ if (channel->local.id == channel_id)
+ return channel;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * _libssh2_channel_open
+ *
+ * Establish a generic session channel
+ */
+LIBSSH2_CHANNEL *
+_libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type,
+ uint32_t channel_type_len,
+ uint32_t window_size,
+ uint32_t packet_size,
+ const unsigned char *message,
+ size_t message_len)
+{
+ static const unsigned char reply_codes[3] = {
+ SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
+ SSH_MSG_CHANNEL_OPEN_FAILURE,
+ 0
+ };
+ unsigned char *s;
+ int rc;
+
+ if (session->open_state == libssh2_NB_state_idle) {
+ session->open_channel = NULL;
+ session->open_packet = NULL;
+ session->open_data = NULL;
+ /* 17 = packet_type(1) + channel_type_len(4) + sender_channel(4) +
+ * window_size(4) + packet_size(4) */
+ session->open_packet_len = channel_type_len + 17;
+ session->open_local_channel = _libssh2_channel_nextid(session);
+
+ /* Zero the whole thing out */
+ memset(&session->open_packet_requirev_state, 0,
+ sizeof(session->open_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Opening Channel - win %d pack %d", window_size,
+ packet_size);
+ session->open_channel =
+ LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if (!session->open_channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate space for channel data");
+ return NULL;
+ }
+ session->open_channel->channel_type_len = channel_type_len;
+ session->open_channel->channel_type =
+ LIBSSH2_ALLOC(session, channel_type_len);
+ if (!session->open_channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating memory for channel type name");
+ LIBSSH2_FREE(session, session->open_channel);
+ session->open_channel = NULL;
+ return NULL;
+ }
+ memcpy(session->open_channel->channel_type, channel_type,
+ channel_type_len);
+
+ /* REMEMBER: local as in locally sourced */
+ session->open_channel->local.id = session->open_local_channel;
+ session->open_channel->remote.window_size = window_size;
+ session->open_channel->remote.window_size_initial = window_size;
+ session->open_channel->remote.packet_size = packet_size;
+ session->open_channel->session = session;
+
+ _libssh2_list_add(&session->channels,
+ &session->open_channel->node);
+
+ s = session->open_packet =
+ LIBSSH2_ALLOC(session, session->open_packet_len);
+ if (!session->open_packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate temporary space for packet");
+ goto channel_error;
+ }
+ *(s++) = SSH_MSG_CHANNEL_OPEN;
+ _libssh2_store_str(&s, channel_type, channel_type_len);
+ _libssh2_store_u32(&s, session->open_local_channel);
+ _libssh2_store_u32(&s, window_size);
+ _libssh2_store_u32(&s, packet_size);
+
+ /* Do not copy the message */
+
+ session->open_state = libssh2_NB_state_created;
+ }
+
+ if (session->open_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ session->open_packet,
+ session->open_packet_len,
+ message, message_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending channel-open request");
+ return NULL;
+ }
+ else if (rc) {
+ _libssh2_error(session, rc,
+ "Unable to send channel-open request");
+ goto channel_error;
+ }
+
+ session->open_state = libssh2_NB_state_sent;
+ }
+
+ if (session->open_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->open_data,
+ &session->open_data_len, 1,
+ session->open_packet + 5 +
+ channel_type_len, 4,
+ &session->open_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ return NULL;
+ } else if (rc) {
+ goto channel_error;
+ }
+
+ if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ session->open_channel->remote.id =
+ _libssh2_ntohu32(session->open_data + 5);
+ session->open_channel->local.window_size =
+ _libssh2_ntohu32(session->open_data + 9);
+ session->open_channel->local.window_size_initial =
+ _libssh2_ntohu32(session->open_data + 9);
+ session->open_channel->local.packet_size =
+ _libssh2_ntohu32(session->open_data + 13);
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Connection Established - ID: %lu/%lu win: %lu/%lu"
+ " pack: %lu/%lu",
+ session->open_channel->local.id,
+ session->open_channel->remote.id,
+ session->open_channel->local.window_size,
+ session->open_channel->remote.window_size,
+ session->open_channel->local.packet_size,
+ session->open_channel->remote.packet_size);
+ LIBSSH2_FREE(session, session->open_packet);
+ session->open_packet = NULL;
+ LIBSSH2_FREE(session, session->open_data);
+ session->open_data = NULL;
+
+ session->open_state = libssh2_NB_state_idle;
+ return session->open_channel;
+ }
+
+ if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
+ unsigned int reason_code = _libssh2_ntohu32(session->open_data + 5);
+ switch (reason_code) {
+ case SSH_OPEN_ADMINISTRATIVELY_PROHIBITED:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (administratively prohibited)");
+ break;
+ case SSH_OPEN_CONNECT_FAILED:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (connect failed)");
+ break;
+ case SSH_OPEN_UNKNOWN_CHANNELTYPE:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (unknown channel type)");
+ break;
+ case SSH_OPEN_RESOURCE_SHORTAGE:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (resource shortage)");
+ break;
+ default:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure");
+ }
+ }
+ }
+
+ channel_error:
+
+ if (session->open_data) {
+ LIBSSH2_FREE(session, session->open_data);
+ session->open_data = NULL;
+ }
+ if (session->open_packet) {
+ LIBSSH2_FREE(session, session->open_packet);
+ session->open_packet = NULL;
+ }
+ if (session->open_channel) {
+ unsigned char channel_id[4];
+ LIBSSH2_FREE(session, session->open_channel->channel_type);
+
+ _libssh2_list_remove(&session->open_channel->node);
+
+ /* Clear out packets meant for this channel */
+ _libssh2_htonu32(channel_id, session->open_channel->local.id);
+ while ((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA,
+ &session->open_data,
+ &session->open_data_len, 1,
+ channel_id, 4) >= 0)
+ ||
+ (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA,
+ &session->open_data,
+ &session->open_data_len, 1,
+ channel_id, 4) >= 0)) {
+ LIBSSH2_FREE(session, session->open_data);
+ session->open_data = NULL;
+ }
+
+ LIBSSH2_FREE(session, session->open_channel);
+ session->open_channel = NULL;
+ }
+
+ session->open_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_channel_open_ex
+ *
+ * Establish a generic session channel
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *type,
+ unsigned int type_len,
+ unsigned int window_size, unsigned int packet_size,
+ const char *msg, unsigned int msg_len)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ if(!session)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ _libssh2_channel_open(session, type, type_len,
+ window_size, packet_size,
+ (unsigned char *)msg,
+ msg_len));
+ return ptr;
+}
+
+/*
+ * libssh2_channel_direct_tcpip_ex
+ *
+ * Tunnel TCP/IP connect through the SSH session to direct host/port
+ */
+static LIBSSH2_CHANNEL *
+channel_direct_tcpip(LIBSSH2_SESSION * session, const char *host,
+ int port, const char *shost, int sport)
+{
+ LIBSSH2_CHANNEL *channel;
+ unsigned char *s;
+
+ if (session->direct_state == libssh2_NB_state_idle) {
+ session->direct_host_len = strlen(host);
+ session->direct_shost_len = strlen(shost);
+ /* host_len(4) + port(4) + shost_len(4) + sport(4) */
+ session->direct_message_len =
+ session->direct_host_len + session->direct_shost_len + 16;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting direct-tcpip session to from %s:%d to %s:%d",
+ shost, sport, host, port);
+
+ s = session->direct_message =
+ LIBSSH2_ALLOC(session, session->direct_message_len);
+ if (!session->direct_message) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for direct-tcpip connection");
+ return NULL;
+ }
+ _libssh2_store_str(&s, host, session->direct_host_len);
+ _libssh2_store_u32(&s, port);
+ _libssh2_store_str(&s, shost, session->direct_shost_len);
+ _libssh2_store_u32(&s, sport);
+ }
+
+ channel =
+ _libssh2_channel_open(session, "direct-tcpip",
+ sizeof("direct-tcpip") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT,
+ session->direct_message,
+ session->direct_message_len);
+
+ if (!channel &&
+ libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
+ /* The error code is still set to LIBSSH2_ERROR_EAGAIN, set our state
+ to created to avoid re-creating the package on next invoke */
+ session->direct_state = libssh2_NB_state_created;
+ return NULL;
+ }
+ /* by default we set (keep?) idle state... */
+ session->direct_state = libssh2_NB_state_idle;
+
+ LIBSSH2_FREE(session, session->direct_message);
+ session->direct_message = NULL;
+
+ return channel;
+}
+
+/*
+ * libssh2_channel_direct_tcpip_ex
+ *
+ * Tunnel TCP/IP connect through the SSH session to direct host/port
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host,
+ int port, const char *shost, int sport)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ if(!session)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ channel_direct_tcpip(session, host, port, shost, sport));
+ return ptr;
+}
+
+/*
+ * channel_forward_listen
+ *
+ * Bind a port on the remote host and listen for connections
+ */
+static LIBSSH2_LISTENER *
+channel_forward_listen(LIBSSH2_SESSION * session, const char *host,
+ int port, int *bound_port, int queue_maxsize)
+{
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 };
+ int rc;
+
+ if(!host)
+ host = "0.0.0.0";
+
+ if (session->fwdLstn_state == libssh2_NB_state_idle) {
+ session->fwdLstn_host_len = strlen(host);
+ /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4)
+ + port(4) */
+ session->fwdLstn_packet_len =
+ session->fwdLstn_host_len + (sizeof("tcpip-forward") - 1) + 14;
+
+ /* Zero the whole thing out */
+ memset(&session->fwdLstn_packet_requirev_state, 0,
+ sizeof(session->fwdLstn_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting tcpip-forward session for %s:%d", host,
+ port);
+
+ s = session->fwdLstn_packet =
+ LIBSSH2_ALLOC(session, session->fwdLstn_packet_len);
+ if (!session->fwdLstn_packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for setenv packet");
+ return NULL;
+ }
+
+ *(s++) = SSH_MSG_GLOBAL_REQUEST;
+ _libssh2_store_str(&s, "tcpip-forward", sizeof("tcpip-forward") - 1);
+ *(s++) = 0x01; /* want_reply */
+
+ _libssh2_store_str(&s, host, session->fwdLstn_host_len);
+ _libssh2_store_u32(&s, port);
+
+ session->fwdLstn_state = libssh2_NB_state_created;
+ }
+
+ if (session->fwdLstn_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ session->fwdLstn_packet,
+ session->fwdLstn_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending global-request packet for "
+ "forward listen request");
+ return NULL;
+ }
+ else if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send global-request packet for forward "
+ "listen request");
+ LIBSSH2_FREE(session, session->fwdLstn_packet);
+ session->fwdLstn_packet = NULL;
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+ LIBSSH2_FREE(session, session->fwdLstn_packet);
+ session->fwdLstn_packet = NULL;
+
+ session->fwdLstn_state = libssh2_NB_state_sent;
+ }
+
+ if (session->fwdLstn_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 0, NULL, 0,
+ &session->fwdLstn_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ return NULL;
+ } else if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unknown");
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ if (data[0] == SSH_MSG_REQUEST_SUCCESS) {
+ LIBSSH2_LISTENER *listener;
+
+ listener = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_LISTENER));
+ if (!listener)
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for listener queue");
+ else {
+ listener->host =
+ LIBSSH2_ALLOC(session, session->fwdLstn_host_len + 1);
+ if (!listener->host) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for listener queue");
+ LIBSSH2_FREE(session, listener);
+ listener = NULL;
+ }
+ else {
+ listener->session = session;
+ memcpy(listener->host, host, session->fwdLstn_host_len);
+ listener->host[session->fwdLstn_host_len] = 0;
+ if (data_len >= 5 && !port) {
+ listener->port = _libssh2_ntohu32(data + 1);
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Dynamic tcpip-forward port allocated: %d",
+ listener->port);
+ }
+ else
+ listener->port = port;
+
+ listener->queue_size = 0;
+ listener->queue_maxsize = queue_maxsize;
+
+ /* append this to the parent's list of listeners */
+ _libssh2_list_add(&session->listeners, &listener->node);
+
+ if (bound_port) {
+ *bound_port = listener->port;
+ }
+ }
+ }
+
+ LIBSSH2_FREE(session, data);
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return listener;
+ }
+ else if (data[0] == SSH_MSG_REQUEST_FAILURE) {
+ LIBSSH2_FREE(session, data);
+ _libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED,
+ "Unable to complete request for forward-listen");
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+ }
+
+ session->fwdLstn_state = libssh2_NB_state_idle;
+
+ return NULL;
+}
+
+/*
+ * libssh2_channel_forward_listen_ex
+ *
+ * Bind a port on the remote host and listen for connections
+ */
+LIBSSH2_API LIBSSH2_LISTENER *
+libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host,
+ int port, int *bound_port, int queue_maxsize)
+{
+ LIBSSH2_LISTENER *ptr;
+
+ if(!session)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ channel_forward_listen(session, host, port, bound_port,
+ queue_maxsize));
+ return ptr;
+}
+
+/*
+ * _libssh2_channel_forward_cancel
+ *
+ * Stop listening on a remote port and free the listener
+ * Toss out any pending (un-accept()ed) connections
+ *
+ * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error
+ */
+int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
+{
+ LIBSSH2_SESSION *session = listener->session;
+ LIBSSH2_CHANNEL *queued;
+ unsigned char *packet, *s;
+ size_t host_len = strlen(listener->host);
+ /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) +
+ port(4) */
+ size_t packet_len =
+ host_len + 14 + sizeof("cancel-tcpip-forward") - 1;
+ int rc;
+ int retcode = 0;
+
+ if (listener->chanFwdCncl_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Cancelling tcpip-forward session for %s:%d",
+ listener->host, listener->port);
+
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for setenv packet");
+ return LIBSSH2_ERROR_ALLOC;
+ }
+
+ *(s++) = SSH_MSG_GLOBAL_REQUEST;
+ _libssh2_store_str(&s, "cancel-tcpip-forward",
+ sizeof("cancel-tcpip-forward") - 1);
+ *(s++) = 0x00; /* want_reply */
+
+ _libssh2_store_str(&s, listener->host, host_len);
+ _libssh2_store_u32(&s, listener->port);
+
+ listener->chanFwdCncl_state = libssh2_NB_state_created;
+ } else {
+ packet = listener->chanFwdCncl_data;
+ }
+
+ if (listener->chanFwdCncl_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, packet, packet_len, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending forward request");
+ listener->chanFwdCncl_data = packet;
+ return rc;
+ }
+ else if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send global-request packet for forward "
+ "listen request");
+ /* set the state to something we don't check for, for the
+ unfortunate situation where we get an EAGAIN further down
+ when trying to bail out due to errors! */
+ listener->chanFwdCncl_state = libssh2_NB_state_sent;
+ retcode = LIBSSH2_ERROR_SOCKET_SEND;
+ }
+ LIBSSH2_FREE(session, packet);
+
+ listener->chanFwdCncl_state = libssh2_NB_state_sent;
+ }
+
+ queued = _libssh2_list_first(&listener->queue);
+ while (queued) {
+ LIBSSH2_CHANNEL *next = _libssh2_list_next(&queued->node);
+
+ rc = _libssh2_channel_free(queued);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ queued = next;
+ }
+ LIBSSH2_FREE(session, listener->host);
+
+ /* remove this entry from the parent's list of listeners */
+ _libssh2_list_remove(&listener->node);
+
+ LIBSSH2_FREE(session, listener);
+
+ return retcode;
+}
+
+/*
+ * libssh2_channel_forward_cancel
+ *
+ * Stop listening on a remote port and free the listener
+ * Toss out any pending (un-accept()ed) connections
+ *
+ * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error
+ */
+LIBSSH2_API int
+libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
+{
+ int rc;
+
+ if(!listener)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, listener->session,
+ _libssh2_channel_forward_cancel(listener));
+ return rc;
+}
+
+/*
+ * channel_forward_accept
+ *
+ * Accept a connection
+ */
+static LIBSSH2_CHANNEL *
+channel_forward_accept(LIBSSH2_LISTENER *listener)
+{
+ int rc;
+
+ do {
+ rc = _libssh2_transport_read(listener->session);
+ } while (rc > 0);
+
+ if (_libssh2_list_first(&listener->queue)) {
+ LIBSSH2_CHANNEL *channel = _libssh2_list_first(&listener->queue);
+
+ /* detach channel from listener's queue */
+ _libssh2_list_remove(&channel->node);
+
+ listener->queue_size--;
+
+ /* add channel to session's channel list */
+ _libssh2_list_add(&channel->session->channels, &channel->node);
+
+ return channel;
+ }
+
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(listener->session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for packet");
+ }
+ else
+ _libssh2_error(listener->session, LIBSSH2_ERROR_CHANNEL_UNKNOWN,
+ "Channel not found");
+ return NULL;
+}
+
+/*
+ * libssh2_channel_forward_accept
+ *
+ * Accept a connection
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ if(!listener)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, listener->session,
+ channel_forward_accept(listener));
+ return ptr;
+
+}
+
+/*
+ * channel_setenv
+ *
+ * Set an environment variable prior to requesting a shell/program/subsystem
+ */
+static int channel_setenv(LIBSSH2_CHANNEL *channel,
+ const char *varname, unsigned int varname_len,
+ const char *value, unsigned int value_len)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s, *data;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ size_t data_len;
+ int rc;
+
+ if (channel->setenv_state == libssh2_NB_state_idle) {
+ /* 21 = packet_type(1) + channel_id(4) + request_len(4) +
+ * request(3)"env" + want_reply(1) + varname_len(4) + value_len(4) */
+ channel->setenv_packet_len = varname_len + value_len + 21;
+
+ /* Zero the whole thing out */
+ memset(&channel->setenv_packet_requirev_state, 0,
+ sizeof(channel->setenv_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Setting remote environment variable: %s=%s on "
+ "channel %lu/%lu",
+ varname, value, channel->local.id, channel->remote.id);
+
+ s = channel->setenv_packet =
+ LIBSSH2_ALLOC(session, channel->setenv_packet_len);
+ if (!channel->setenv_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for setenv packet");
+ }
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, "env", sizeof("env") - 1);
+ *(s++) = 0x01;
+ _libssh2_store_str(&s, varname, varname_len);
+ _libssh2_store_str(&s, value, value_len);
+
+ channel->setenv_state = libssh2_NB_state_created;
+ }
+
+ if (channel->setenv_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ channel->setenv_packet,
+ channel->setenv_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending setenv request");
+ return rc;
+ } else if (rc) {
+ LIBSSH2_FREE(session, channel->setenv_packet);
+ channel->setenv_packet = NULL;
+ channel->setenv_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send channel-request packet for "
+ "setenv request");
+ }
+ LIBSSH2_FREE(session, channel->setenv_packet);
+ channel->setenv_packet = NULL;
+
+ _libssh2_htonu32(channel->setenv_local_channel, channel->local.id);
+
+ channel->setenv_state = libssh2_NB_state_sent;
+ }
+
+ if (channel->setenv_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->setenv_local_channel, 4,
+ &channel->
+ setenv_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ if (rc) {
+ channel->setenv_state = libssh2_NB_state_idle;
+ return rc;
+ }
+
+ if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
+ LIBSSH2_FREE(session, data);
+ channel->setenv_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ LIBSSH2_FREE(session, data);
+ }
+
+ channel->setenv_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for channel-setenv");
+}
+
+/*
+ * libssh2_channel_setenv_ex
+ *
+ * Set an environment variable prior to requesting a shell/program/subsystem
+ */
+LIBSSH2_API int
+libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel,
+ const char *varname, unsigned int varname_len,
+ const char *value, unsigned int value_len)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_setenv(channel, varname, varname_len,
+ value, value_len));
+ return rc;
+}
+
+/*
+ * channel_request_pty
+ * Duh... Request a PTY
+ */
+static int channel_request_pty(LIBSSH2_CHANNEL *channel,
+ const char *term, unsigned int term_len,
+ const char *modes, unsigned int modes_len,
+ int width, int height,
+ int width_px, int height_px)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ int rc;
+
+ if (channel->reqPTY_state == libssh2_NB_state_idle) {
+ /* 41 = packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) +
+ * want_reply(1) + term_len(4) + width(4) + height(4) + width_px(4) +
+ * height_px(4) + modes_len(4) */
+ if(term_len + modes_len > 256) {
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "term + mode lengths too large");
+ }
+
+ channel->reqPTY_packet_len = term_len + modes_len + 41;
+
+ /* Zero the whole thing out */
+ memset(&channel->reqPTY_packet_requirev_state, 0,
+ sizeof(channel->reqPTY_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Allocating tty on channel %lu/%lu", channel->local.id,
+ channel->remote.id);
+
+ s = channel->reqPTY_packet;
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, (char *)"pty-req", sizeof("pty-req") - 1);
+
+ *(s++) = 0x01;
+
+ _libssh2_store_str(&s, term, term_len);
+ _libssh2_store_u32(&s, width);
+ _libssh2_store_u32(&s, height);
+ _libssh2_store_u32(&s, width_px);
+ _libssh2_store_u32(&s, height_px);
+ _libssh2_store_str(&s, modes, modes_len);
+
+ channel->reqPTY_state = libssh2_NB_state_created;
+ }
+
+ if (channel->reqPTY_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->reqPTY_packet,
+ channel->reqPTY_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending pty request");
+ return rc;
+ } else if (rc) {
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send pty-request packet");
+ }
+ _libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id);
+
+ channel->reqPTY_state = libssh2_NB_state_sent;
+ }
+
+ if (channel->reqPTY_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ unsigned char code;
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->reqPTY_local_channel, 4,
+ &channel->reqPTY_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Failed to require the PTY package");
+ }
+
+ code = data[0];
+
+ LIBSSH2_FREE(session, data);
+ channel->reqPTY_state = libssh2_NB_state_idle;
+
+ if (code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for channel request-pty");
+}
+
+/*
+ * libssh2_channel_request_pty_ex
+ * Duh... Request a PTY
+ */
+LIBSSH2_API int
+libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term,
+ unsigned int term_len, const char *modes,
+ unsigned int modes_len, int width, int height,
+ int width_px, int height_px)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_request_pty(channel, term, term_len, modes,
+ modes_len, width, height,
+ width_px, height_px));
+ return rc;
+}
+
+static int
+channel_request_pty_size(LIBSSH2_CHANNEL * channel, int width,
+ int height, int width_px, int height_px)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ int rc;
+ int retcode = LIBSSH2_ERROR_PROTO;
+
+ if (channel->reqPTY_state == libssh2_NB_state_idle) {
+ channel->reqPTY_packet_len = 39;
+
+ /* Zero the whole thing out */
+ memset(&channel->reqPTY_packet_requirev_state, 0,
+ sizeof(channel->reqPTY_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "changing tty size on channel %lu/%lu",
+ channel->local.id,
+ channel->remote.id);
+
+ s = channel->reqPTY_packet;
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, (char *)"window-change",
+ sizeof("window-change") - 1);
+ *(s++) = 0x00; /* Don't reply */
+ _libssh2_store_u32(&s, width);
+ _libssh2_store_u32(&s, height);
+ _libssh2_store_u32(&s, width_px);
+ _libssh2_store_u32(&s, height_px);
+
+ channel->reqPTY_state = libssh2_NB_state_created;
+ }
+
+ if (channel->reqPTY_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->reqPTY_packet,
+ channel->reqPTY_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending window-change request");
+ return rc;
+ } else if (rc) {
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send window-change packet");
+ }
+ _libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id);
+ retcode = LIBSSH2_ERROR_NONE;
+ }
+
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return retcode;
+}
+
+LIBSSH2_API int
+libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL *channel, int width,
+ int height, int width_px, int height_px)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_request_pty_size(channel, width, height, width_px,
+ height_px));
+ return rc;
+}
+
+/* Keep this an even number */
+#define LIBSSH2_X11_RANDOM_COOKIE_LEN 32
+
+/*
+ * channel_x11_req
+ * Request X11 forwarding
+ */
+static int
+channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection,
+ const char *auth_proto, const char *auth_cookie,
+ int screen_number)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ size_t proto_len =
+ auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1);
+ size_t cookie_len =
+ auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN;
+ int rc;
+
+ if (channel->reqX11_state == libssh2_NB_state_idle) {
+ /* 30 = packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) +
+ * want_reply(1) + single_cnx(1) + proto_len(4) + cookie_len(4) +
+ * screen_num(4) */
+ channel->reqX11_packet_len = proto_len + cookie_len + 30;
+
+ /* Zero the whole thing out */
+ memset(&channel->reqX11_packet_requirev_state, 0,
+ sizeof(channel->reqX11_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting x11-req for channel %lu/%lu: single=%d "
+ "proto=%s cookie=%s screen=%d",
+ channel->local.id, channel->remote.id,
+ single_connection,
+ auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1",
+ auth_cookie ? auth_cookie : "<random>", screen_number);
+
+ s = channel->reqX11_packet =
+ LIBSSH2_ALLOC(session, channel->reqX11_packet_len);
+ if (!channel->reqX11_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for pty-request");
+ }
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, "x11-req", sizeof("x11-req") - 1);
+
+ *(s++) = 0x01; /* want_reply */
+ *(s++) = single_connection ? 0x01 : 0x00;
+
+ _libssh2_store_str(&s, auth_proto?auth_proto:"MIT-MAGIC-COOKIE-1",
+ proto_len);
+
+ _libssh2_store_u32(&s, cookie_len);
+ if (auth_cookie) {
+ memcpy(s, auth_cookie, cookie_len);
+ } else {
+ int i;
+ /* note: the extra +1 below is necessary since the sprintf()
+ loop will always write 3 bytes so the last one will write
+ the trailing zero at the LIBSSH2_X11_RANDOM_COOKIE_LEN/2
+ border */
+ unsigned char buffer[(LIBSSH2_X11_RANDOM_COOKIE_LEN / 2) +1];
+
+ _libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2);
+ for(i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) {
+ sprintf((char *)&s[i*2], "%02X", buffer[i]);
+ }
+ }
+ s += cookie_len;
+
+ _libssh2_store_u32(&s, screen_number);
+ channel->reqX11_state = libssh2_NB_state_created;
+ }
+
+ if (channel->reqX11_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->reqX11_packet,
+ channel->reqX11_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending X11-req packet");
+ return rc;
+ }
+ if (rc) {
+ LIBSSH2_FREE(session, channel->reqX11_packet);
+ channel->reqX11_packet = NULL;
+ channel->reqX11_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send x11-req packet");
+ }
+ LIBSSH2_FREE(session, channel->reqX11_packet);
+ channel->reqX11_packet = NULL;
+
+ _libssh2_htonu32(channel->reqX11_local_channel, channel->local.id);
+
+ channel->reqX11_state = libssh2_NB_state_sent;
+ }
+
+ if (channel->reqX11_state == libssh2_NB_state_sent) {
+ size_t data_len;
+ unsigned char *data;
+ unsigned char code;
+
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->reqX11_local_channel, 4,
+ &channel->reqX11_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ channel->reqX11_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "waiting for x11-req response packet");
+ }
+
+ code = data[0];
+ LIBSSH2_FREE(session, data);
+ channel->reqX11_state = libssh2_NB_state_idle;
+
+ if (code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for channel x11-req");
+}
+
+/*
+ * libssh2_channel_x11_req_ex
+ * Request X11 forwarding
+ */
+LIBSSH2_API int
+libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection,
+ const char *auth_proto, const char *auth_cookie,
+ int screen_number)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_x11_req(channel, single_connection, auth_proto,
+ auth_cookie, screen_number));
+ return rc;
+}
+
+
+/*
+ * _libssh2_channel_process_startup
+ *
+ * Primitive for libssh2_channel_(shell|exec|subsystem)
+ */
+int
+_libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
+ const char *request, size_t request_len,
+ const char *message, size_t message_len)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ int rc;
+
+ if (channel->process_state == libssh2_NB_state_end) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "Channel can not be reused");
+ }
+
+ if (channel->process_state == libssh2_NB_state_idle) {
+ /* 10 = packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
+ channel->process_packet_len = request_len + 10;
+
+ /* Zero the whole thing out */
+ memset(&channel->process_packet_requirev_state, 0,
+ sizeof(channel->process_packet_requirev_state));
+
+ if (message)
+ channel->process_packet_len += + 4;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "starting request(%s) on channel %lu/%lu, message=%s",
+ request, channel->local.id, channel->remote.id,
+ message?message:"<null>");
+ s = channel->process_packet =
+ LIBSSH2_ALLOC(session, channel->process_packet_len);
+ if (!channel->process_packet)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for channel-process request");
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, request, request_len);
+ *(s++) = 0x01;
+
+ if (message)
+ _libssh2_store_u32(&s, message_len);
+
+ channel->process_state = libssh2_NB_state_created;
+ }
+
+ if (channel->process_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ channel->process_packet,
+ channel->process_packet_len,
+ (unsigned char *)message, message_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending channel request");
+ return rc;
+ }
+ else if (rc) {
+ LIBSSH2_FREE(session, channel->process_packet);
+ channel->process_packet = NULL;
+ channel->process_state = libssh2_NB_state_end;
+ return _libssh2_error(session, rc,
+ "Unable to send channel request");
+ }
+ LIBSSH2_FREE(session, channel->process_packet);
+ channel->process_packet = NULL;
+
+ _libssh2_htonu32(channel->process_local_channel, channel->local.id);
+
+ channel->process_state = libssh2_NB_state_sent;
+ }
+
+ if (channel->process_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ unsigned char code;
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->process_local_channel, 4,
+ &channel->process_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ channel->process_state = libssh2_NB_state_end;
+ return _libssh2_error(session, rc,
+ "Failed waiting for channel success");
+ }
+
+ code = data[0];
+ LIBSSH2_FREE(session, data);
+ channel->process_state = libssh2_NB_state_end;
+
+ if (code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for "
+ "channel-process-startup");
+}
+
+/*
+ * libssh2_channel_process_startup
+ *
+ * Primitive for libssh2_channel_(shell|exec|subsystem)
+ */
+LIBSSH2_API int
+libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
+ const char *req, unsigned int req_len,
+ const char *msg, unsigned int msg_len)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_process_startup(channel, req, req_len,
+ msg, msg_len));
+ return rc;
+}
+
+
+/*
+ * libssh2_channel_set_blocking
+ *
+ * Set a channel's BEHAVIOR blocking on or off. The socket will remain non-
+ * blocking.
+ */
+LIBSSH2_API void
+libssh2_channel_set_blocking(LIBSSH2_CHANNEL * channel, int blocking)
+{
+ if(channel)
+ (void) _libssh2_session_set_blocking(channel->session, blocking);
+}
+
+/*
+ * _libssh2_channel_flush
+ *
+ * Flush data from one (or all) stream
+ * Returns number of bytes flushed, or negative on failure
+ */
+int
+_libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid)
+{
+ if (channel->flush_state == libssh2_NB_state_idle) {
+ LIBSSH2_PACKET *packet =
+ _libssh2_list_first(&channel->session->packets);
+ channel->flush_refund_bytes = 0;
+ channel->flush_flush_bytes = 0;
+
+ while (packet) {
+ LIBSSH2_PACKET *next = _libssh2_list_next(&packet->node);
+ unsigned char packet_type = packet->data[0];
+
+ if (((packet_type == SSH_MSG_CHANNEL_DATA)
+ || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA))
+ && (_libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
+ /* It's our channel at least */
+ long packet_stream_id =
+ (packet_type == SSH_MSG_CHANNEL_DATA) ? 0 :
+ _libssh2_ntohu32(packet->data + 5);
+ if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL)
+ || ((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA)
+ || (streamid == packet_stream_id)))
+ || ((packet_type == SSH_MSG_CHANNEL_DATA)
+ && (streamid == 0))) {
+ int bytes_to_flush = packet->data_len - packet->data_head;
+
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Flushing %d bytes of data from stream "
+ "%lu on channel %lu/%lu",
+ bytes_to_flush, packet_stream_id,
+ channel->local.id, channel->remote.id);
+
+ /* It's one of the streams we wanted to flush */
+ channel->flush_refund_bytes += packet->data_len - 13;
+ channel->flush_flush_bytes += bytes_to_flush;
+
+ LIBSSH2_FREE(channel->session, packet->data);
+
+ /* remove this packet from the parent's list */
+ _libssh2_list_remove(&packet->node);
+ LIBSSH2_FREE(channel->session, packet);
+ }
+ }
+ packet = next;
+ }
+
+ channel->flush_state = libssh2_NB_state_created;
+ }
+
+ channel->read_avail -= channel->flush_flush_bytes;
+ channel->remote.window_size -= channel->flush_flush_bytes;
+
+ if (channel->flush_refund_bytes) {
+ int rc;
+
+ rc = _libssh2_channel_receive_window_adjust(channel,
+ channel->flush_refund_bytes,
+ 1, NULL);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ channel->flush_state = libssh2_NB_state_idle;
+
+ return channel->flush_flush_bytes;
+}
+
+/*
+ * libssh2_channel_flush_ex
+ *
+ * Flush data from one (or all) stream
+ * Returns number of bytes flushed, or negative on failure
+ */
+LIBSSH2_API int
+libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int stream)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_flush(channel, stream));
+ return rc;
+}
+
+/*
+ * libssh2_channel_get_exit_status
+ *
+ * Return the channel's program exit status. Note that the actual protocol
+ * provides the full 32bit this function returns. We cannot abuse it to
+ * return error values in case of errors so we return a zero if channel is
+ * NULL.
+ */
+LIBSSH2_API int
+libssh2_channel_get_exit_status(LIBSSH2_CHANNEL *channel)
+{
+ if(!channel)
+ return 0;
+
+ return channel->exit_status;
+}
+
+/*
+ * libssh2_channel_get_exit_signal
+ *
+ * Get exit signal (without leading "SIG"), error message, and language
+ * tag into newly allocated buffers of indicated length. Caller can
+ * use NULL pointers to indicate that the value should not be set. The
+ * *_len variables are set if they are non-NULL even if the
+ * corresponding string parameter is NULL. Returns LIBSSH2_ERROR_NONE
+ * on success, or an API error code.
+ */
+LIBSSH2_API int
+libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL *channel,
+ char **exitsignal,
+ size_t *exitsignal_len,
+ char **errmsg,
+ size_t *errmsg_len,
+ char **langtag,
+ size_t *langtag_len)
+{
+ size_t namelen = 0;
+
+ if (channel) {
+ LIBSSH2_SESSION *session = channel->session;
+
+ if (channel->exit_signal) {
+ namelen = strlen(channel->exit_signal);
+ if (exitsignal) {
+ *exitsignal = LIBSSH2_ALLOC(session, namelen + 1);
+ if (!*exitsignal) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for signal name");
+ }
+ memcpy(*exitsignal, channel->exit_signal, namelen);
+ (*exitsignal)[namelen] = '\0';
+ }
+ if (exitsignal_len)
+ *exitsignal_len = namelen;
+ } else {
+ if (exitsignal)
+ *exitsignal = NULL;
+ if (exitsignal_len)
+ *exitsignal_len = 0;
+ }
+
+ /* TODO: set error message and language tag */
+
+ if (errmsg)
+ *errmsg = NULL;
+
+ if (errmsg_len)
+ *errmsg_len = 0;
+
+ if (langtag)
+ *langtag = NULL;
+
+ if (langtag_len)
+ *langtag_len = 0;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * _libssh2_channel_receive_window_adjust
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Calls _libssh2_error() !
+ */
+int
+_libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel,
+ uint32_t adjustment,
+ unsigned char force,
+ unsigned int *store)
+{
+ int rc;
+
+ if(store)
+ *store = channel->remote.window_size;
+
+ if (channel->adjust_state == libssh2_NB_state_idle) {
+ if (!force
+ && (adjustment + channel->adjust_queue <
+ LIBSSH2_CHANNEL_MINADJUST)) {
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Queueing %lu bytes for receive window adjustment "
+ "for channel %lu/%lu",
+ adjustment, channel->local.id, channel->remote.id);
+ channel->adjust_queue += adjustment;
+ return 0;
+ }
+
+ if (!adjustment && !channel->adjust_queue) {
+ return 0;
+ }
+
+ adjustment += channel->adjust_queue;
+ channel->adjust_queue = 0;
+
+ /* Adjust the window based on the block we just freed */
+ channel->adjust_adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
+ _libssh2_htonu32(&channel->adjust_adjust[1], channel->remote.id);
+ _libssh2_htonu32(&channel->adjust_adjust[5], adjustment);
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Adjusting window %lu bytes for data on "
+ "channel %lu/%lu",
+ adjustment, channel->local.id, channel->remote.id);
+
+ channel->adjust_state = libssh2_NB_state_created;
+ }
+
+ rc = _libssh2_transport_send(channel->session, channel->adjust_adjust, 9,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(channel->session, rc,
+ "Would block sending window adjust");
+ return rc;
+ }
+ else if (rc) {
+ channel->adjust_queue = adjustment;
+ return _libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send transfer-window adjustment "
+ "packet, deferring");
+ }
+ else {
+ channel->remote.window_size += adjustment;
+ }
+
+ channel->adjust_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_receive_window_adjust
+ *
+ * DEPRECATED
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Returns the new size of the receive window (as understood by remote end).
+ * Note that it might return EAGAIN too which is highly stupid.
+ *
+ */
+LIBSSH2_API unsigned long
+libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel,
+ unsigned long adj,
+ unsigned char force)
+{
+ unsigned int window;
+ int rc;
+
+ if(!channel)
+ return (unsigned long)LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_receive_window_adjust(channel, adj,
+ force, &window));
+
+ /* stupid - but this is how it was made to work before and this is just
+ kept for backwards compatibility */
+ return rc?(unsigned long)rc:window;
+}
+
+/*
+ * libssh2_channel_receive_window_adjust2
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Stores the new size of the receive window in the data 'window' points to.
+ *
+ * Returns the "normal" error code: 0 for success, negative for failure.
+ */
+LIBSSH2_API int
+libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel,
+ unsigned long adj,
+ unsigned char force,
+ unsigned int *window)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_receive_window_adjust(channel, adj, force,
+ window));
+ return rc;
+}
+
+int
+_libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode)
+{
+ if (channel->extData2_state == libssh2_NB_state_idle) {
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Setting channel %lu/%lu handle_extended_data"
+ " mode to %d",
+ channel->local.id, channel->remote.id, ignore_mode);
+ channel->remote.extended_data_ignore_mode = (char)ignore_mode;
+
+ channel->extData2_state = libssh2_NB_state_created;
+ }
+
+ if (channel->extData2_state == libssh2_NB_state_idle) {
+ if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) {
+ int rc =
+ _libssh2_channel_flush(channel,
+ LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA);
+ if(LIBSSH2_ERROR_EAGAIN == rc)
+ return rc;
+ }
+ }
+
+ channel->extData2_state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * libssh2_channel_handle_extended_data2()
+ *
+ */
+LIBSSH2_API int
+libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel,
+ int mode)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, _libssh2_channel_extended_data(channel,
+ mode));
+ return rc;
+}
+
+/*
+ * libssh2_channel_handle_extended_data
+ *
+ * DEPRECATED DO NOTE USE!
+ *
+ * How should extended data look to the calling app? Keep it in separate
+ * channels[_read() _read_stdder()]? (NORMAL) Merge the extended data to the
+ * standard data? [everything via _read()]? (MERGE) Ignore it entirely [toss
+ * out packets as they come in]? (IGNORE)
+ */
+LIBSSH2_API void
+libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
+ int ignore_mode)
+{
+ (void)libssh2_channel_handle_extended_data2(channel, ignore_mode);
+}
+
+
+
+/*
+ * _libssh2_channel_read
+ *
+ * Read data from a channel
+ *
+ * It is important to not return 0 until the currently read channel is
+ * complete. If we read stuff from the wire but it was no payload data to fill
+ * in the buffer with, we MUST make sure to return LIBSSH2_ERROR_EAGAIN.
+ *
+ * The receive window must be maintained (enlarged) by the user of this
+ * function.
+ */
+ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
+ char *buf, size_t buflen)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc;
+ int bytes_read = 0;
+ int bytes_want;
+ int unlink_packet;
+ LIBSSH2_PACKET *read_packet;
+ LIBSSH2_PACKET *read_next;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "channel_read() wants %d bytes from channel %lu/%lu "
+ "stream #%d",
+ (int) buflen, channel->local.id, channel->remote.id,
+ stream_id);
+
+ /* expand the receiving window first if it has become too narrow */
+ if( (channel->read_state == libssh2_NB_state_jump1) ||
+ (channel->remote.window_size < channel->remote.window_size_initial / 4 * 3 + buflen) ) {
+
+ uint32_t adjustment = channel->remote.window_size_initial + buflen - channel->remote.window_size;
+ if (adjustment < LIBSSH2_CHANNEL_MINADJUST)
+ adjustment = LIBSSH2_CHANNEL_MINADJUST;
+
+ /* the actual window adjusting may not finish so we need to deal with
+ this special state here */
+ channel->read_state = libssh2_NB_state_jump1;
+ rc = _libssh2_channel_receive_window_adjust(channel, adjustment,
+ 0, NULL);
+ if (rc)
+ return rc;
+
+ channel->read_state = libssh2_NB_state_idle;
+ }
+
+ /* Process all pending incoming packets. Tests prove that this way
+ produces faster transfers. */
+ do {
+ rc = _libssh2_transport_read(session);
+ } while (rc > 0);
+
+ if ((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN))
+ return _libssh2_error(session, rc, "transport read");
+
+ read_packet = _libssh2_list_first(&session->packets);
+ while (read_packet && (bytes_read < (int) buflen)) {
+ /* previously this loop condition also checked for
+ !channel->remote.close but we cannot let it do this:
+
+ We may have a series of packets to read that are still pending even
+ if a close has been received. Acknowledging the close too early
+ makes us flush buffers prematurely and loose data.
+ */
+
+ LIBSSH2_PACKET *readpkt = read_packet;
+
+ /* In case packet gets destroyed during this iteration */
+ read_next = _libssh2_list_next(&readpkt->node);
+
+ channel->read_local_id =
+ _libssh2_ntohu32(readpkt->data + 1);
+
+ /*
+ * Either we asked for a specific extended data stream
+ * (and data was available),
+ * or the standard stream (and data was available),
+ * or the standard stream with extended_data_merge
+ * enabled and data was available
+ */
+ if ((stream_id
+ && (readpkt->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == channel->read_local_id)
+ && (stream_id == (int) _libssh2_ntohu32(readpkt->data + 5)))
+ || (!stream_id && (readpkt->data[0] == SSH_MSG_CHANNEL_DATA)
+ && (channel->local.id == channel->read_local_id))
+ || (!stream_id
+ && (readpkt->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == channel->read_local_id)
+ && (channel->remote.extended_data_ignore_mode ==
+ LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) {
+
+ /* figure out much more data we want to read */
+ bytes_want = buflen - bytes_read;
+ unlink_packet = FALSE;
+
+ if (bytes_want >= (int) (readpkt->data_len - readpkt->data_head)) {
+ /* we want more than this node keeps, so adjust the number and
+ delete this node after the copy */
+ bytes_want = readpkt->data_len - readpkt->data_head;
+ unlink_packet = TRUE;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "channel_read() got %d of data from %lu/%lu/%d%s",
+ bytes_want, channel->local.id,
+ channel->remote.id, stream_id,
+ unlink_packet?" [ul]":"");
+
+ /* copy data from this struct to the target buffer */
+ memcpy(&buf[bytes_read],
+ &readpkt->data[readpkt->data_head], bytes_want);
+
+ /* advance pointer and counter */
+ readpkt->data_head += bytes_want;
+ bytes_read += bytes_want;
+
+ /* if drained, remove from list */
+ if (unlink_packet) {
+ /* detach readpkt from session->packets list */
+ _libssh2_list_remove(&readpkt->node);
+
+ LIBSSH2_FREE(session, readpkt->data);
+ LIBSSH2_FREE(session, readpkt);
+ }
+ }
+
+ /* check the next struct in the chain */
+ read_packet = read_next;
+ }
+
+ if (!bytes_read) {
+ /* If the channel is already at EOF or even closed, we need to signal
+ that back. We may have gotten that info while draining the incoming
+ transport layer until EAGAIN so we must not be fooled by that
+ return code. */
+ if(channel->remote.eof || channel->remote.close)
+ return 0;
+ else if(rc != LIBSSH2_ERROR_EAGAIN)
+ return 0;
+
+ /* if the transport layer said EAGAIN then we say so as well */
+ return _libssh2_error(session, rc, "would block");
+ }
+
+ channel->read_avail -= bytes_read;
+ channel->remote.window_size -= bytes_read;
+
+ return bytes_read;
+}
+
+/*
+ * libssh2_channel_read_ex
+ *
+ * Read data from a channel (blocking or non-blocking depending on set state)
+ *
+ * When this is done non-blocking, it is important to not return 0 until the
+ * currently read channel is complete. If we read stuff from the wire but it
+ * was no payload data to fill in the buffer with, we MUST make sure to return
+ * LIBSSH2_ERROR_EAGAIN.
+ *
+ * This function will first make sure there's a receive window enough to
+ * receive a full buffer's wort of contents. An application may choose to
+ * adjust the receive window more to increase transfer performance.
+ */
+LIBSSH2_API ssize_t
+libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf,
+ size_t buflen)
+{
+ int rc;
+ unsigned long recv_window;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
+
+ if(buflen > recv_window) {
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_receive_window_adjust(channel, buflen,
+ 1, NULL));
+ }
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_read(channel, stream_id, buf, buflen));
+ return rc;
+}
+
+/*
+ * _libssh2_channel_packet_data_len
+ *
+ * Return the size of the data block of the current packet, or 0 if there
+ * isn't a packet.
+ */
+size_t
+_libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_PACKET *read_packet;
+ uint32_t read_local_id;
+
+ read_packet = _libssh2_list_first(&session->packets);
+ if (read_packet == NULL)
+ return 0;
+
+ while (read_packet) {
+ read_local_id = _libssh2_ntohu32(read_packet->data + 1);
+
+ /*
+ * Either we asked for a specific extended data stream
+ * (and data was available),
+ * or the standard stream (and data was available),
+ * or the standard stream with extended_data_merge
+ * enabled and data was available
+ */
+ if ((stream_id
+ && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == read_local_id)
+ && (stream_id == (int) _libssh2_ntohu32(read_packet->data + 5)))
+ ||
+ (!stream_id
+ && (read_packet->data[0] == SSH_MSG_CHANNEL_DATA)
+ && (channel->local.id == read_local_id))
+ ||
+ (!stream_id
+ && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == read_local_id)
+ && (channel->remote.extended_data_ignore_mode
+ == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE)))
+ {
+ return (read_packet->data_len - read_packet->data_head);
+ }
+ read_packet = _libssh2_list_next(&read_packet->node);
+ }
+
+ return 0;
+}
+
+/*
+ * _libssh2_channel_write
+ *
+ * Send data to a channel. Note that if this returns EAGAIN, the caller must
+ * call this function again with the SAME input arguments.
+ *
+ * Returns: number of bytes sent, or if it returns a negative number, that is
+ * the error code!
+ */
+ssize_t
+_libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
+ const unsigned char *buf, size_t buflen)
+{
+ int rc = 0;
+ LIBSSH2_SESSION *session = channel->session;
+ ssize_t wrote = 0; /* counter for this specific this call */
+
+ /* In theory we could split larger buffers into several smaller packets
+ * but it turns out to be really hard and nasty to do while still offering
+ * the API/prototype.
+ *
+ * Instead we only deal with the first 32K in this call and for the parent
+ * function to call it again with the remainder! 32K is a conservative
+ * limit based on the text in RFC4253 section 6.1.
+ */
+ if(buflen > 32700)
+ buflen = 32700;
+
+ if (channel->write_state == libssh2_NB_state_idle) {
+ unsigned char *s = channel->write_packet;
+
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Writing %d bytes on channel %lu/%lu, stream #%d",
+ (int) buflen, channel->local.id, channel->remote.id,
+ stream_id);
+
+ if (channel->local.close)
+ return _libssh2_error(channel->session,
+ LIBSSH2_ERROR_CHANNEL_CLOSED,
+ "We've already closed this channel");
+ else if (channel->local.eof)
+ return _libssh2_error(channel->session,
+ LIBSSH2_ERROR_CHANNEL_EOF_SENT,
+ "EOF has already been received, "
+ "data might be ignored");
+
+ /* drain the incoming flow first, mostly to make sure we get all
+ * pending window adjust packets */
+ do
+ rc = _libssh2_transport_read(session);
+ while (rc > 0);
+
+ if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) {
+ return _libssh2_error(channel->session, rc,
+ "Failure while draining incoming flow");
+ }
+
+ if(channel->local.window_size <= 0) {
+ /* there's no room for data so we stop */
+
+ /* Waiting on the socket to be writable would be wrong because we
+ * would be back here immediately, but a readable socket might
+ * herald an incoming window adjustment.
+ */
+ session->socket_block_directions = LIBSSH2_SESSION_BLOCK_INBOUND;
+
+ return (rc==LIBSSH2_ERROR_EAGAIN?rc:0);
+ }
+
+ channel->write_bufwrite = buflen;
+
+ *(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA :
+ SSH_MSG_CHANNEL_DATA;
+ _libssh2_store_u32(&s, channel->remote.id);
+ if (stream_id)
+ _libssh2_store_u32(&s, stream_id);
+
+ /* Don't exceed the remote end's limits */
+ /* REMEMBER local means local as the SOURCE of the data */
+ if (channel->write_bufwrite > channel->local.window_size) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Splitting write block due to %lu byte "
+ "window_size on %lu/%lu/%d",
+ channel->local.window_size, channel->local.id,
+ channel->remote.id, stream_id);
+ channel->write_bufwrite = channel->local.window_size;
+ }
+ if (channel->write_bufwrite > channel->local.packet_size) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Splitting write block due to %lu byte "
+ "packet_size on %lu/%lu/%d",
+ channel->local.packet_size, channel->local.id,
+ channel->remote.id, stream_id);
+ channel->write_bufwrite = channel->local.packet_size;
+ }
+ /* store the size here only, the buffer is passed in as-is to
+ _libssh2_transport_send() */
+ _libssh2_store_u32(&s, channel->write_bufwrite);
+ channel->write_packet_len = s - channel->write_packet;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Sending %d bytes on channel %lu/%lu, stream_id=%d",
+ (int) channel->write_bufwrite, channel->local.id,
+ channel->remote.id, stream_id);
+
+ channel->write_state = libssh2_NB_state_created;
+ }
+
+ if (channel->write_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->write_packet,
+ channel->write_packet_len,
+ buf, channel->write_bufwrite);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, rc,
+ "Unable to send channel data");
+ }
+ else if (rc) {
+ channel->write_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send channel data");
+ }
+ /* Shrink local window size */
+ channel->local.window_size -= channel->write_bufwrite;
+
+ wrote += channel->write_bufwrite;
+
+ /* Since _libssh2_transport_write() succeeded, we must return
+ now to allow the caller to provide the next chunk of data.
+
+ We cannot move on to send the next piece of data that may
+ already have been provided in this same function call, as we
+ risk getting EAGAIN for that and we can't return information
+ both about sent data as well as EAGAIN. So, by returning short
+ now, the caller will call this function again with new data to
+ send */
+
+ channel->write_state = libssh2_NB_state_idle;
+
+ return wrote;
+ }
+
+ return LIBSSH2_ERROR_INVAL; /* reaching this point is really bad */
+}
+
+/*
+ * libssh2_channel_write_ex
+ *
+ * Send data to a channel
+ */
+LIBSSH2_API ssize_t
+libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id,
+ const char *buf, size_t buflen)
+{
+ ssize_t rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_write(channel, stream_id,
+ (unsigned char *)buf, buflen));
+ return rc;
+}
+
+/*
+ * channel_send_eof
+ *
+ * Send EOF on channel
+ */
+static int channel_send_eof(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char packet[5]; /* packet_type(1) + channelno(4) */
+ int rc;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Sending EOF on channel %lu/%lu",
+ channel->local.id, channel->remote.id);
+ packet[0] = SSH_MSG_CHANNEL_EOF;
+ _libssh2_htonu32(packet + 1, channel->remote.id);
+ rc = _libssh2_transport_send(session, packet, 5, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending EOF");
+ return rc;
+ }
+ else if (rc) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send EOF on channel");
+ }
+ channel->local.eof = 1;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_send_eof
+ *
+ * Send EOF on channel
+ */
+LIBSSH2_API int
+libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, channel_send_eof(channel));
+ return rc;
+}
+
+/*
+ * libssh2_channel_eof
+ *
+ * Read channel's eof status
+ */
+LIBSSH2_API int
+libssh2_channel_eof(LIBSSH2_CHANNEL * channel)
+{
+ LIBSSH2_SESSION *session;
+ LIBSSH2_PACKET *packet;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = channel->session;
+ packet = _libssh2_list_first(&session->packets);
+
+ while (packet) {
+ if (((packet->data[0] == SSH_MSG_CHANNEL_DATA)
+ || (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA))
+ && (channel->local.id == _libssh2_ntohu32(packet->data + 1))) {
+ /* There's data waiting to be read yet, mask the EOF status */
+ return 0;
+ }
+ packet = _libssh2_list_next(&packet->node);
+ }
+
+ return channel->remote.eof;
+}
+
+/*
+ * channel_wait_eof
+ *
+ * Awaiting channel EOF
+ */
+static int channel_wait_eof(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc;
+
+ if (channel->wait_eof_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Awaiting close of channel %lu/%lu", channel->local.id,
+ channel->remote.id);
+
+ channel->wait_eof_state = libssh2_NB_state_created;
+ }
+
+ /*
+ * While channel is not eof, read more packets from the network.
+ * Either the EOF will be set or network timeout will occur.
+ */
+ do {
+ if (channel->remote.eof) {
+ break;
+ }
+ rc = _libssh2_transport_read(session);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if (rc < 0) {
+ channel->wait_eof_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "_libssh2_transport_read() bailed out!");
+ }
+ } while (1);
+
+ channel->wait_eof_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_wait_eof
+ *
+ * Awaiting channel EOF
+ */
+LIBSSH2_API int
+libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, channel_wait_eof(channel));
+ return rc;
+}
+
+int _libssh2_channel_close(LIBSSH2_CHANNEL * channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc = 0;
+
+ if (channel->local.close) {
+ /* Already closed, act like we sent another close,
+ * even though we didn't... shhhhhh */
+ channel->close_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if (!channel->local.eof) {
+ if ((rc = channel_send_eof(channel))) {
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ _libssh2_error(session, rc,
+ "Unable to send EOF, but closing channel anyway");
+ }
+ }
+
+ /* ignore if we have received a remote eof or not, as it is now too
+ late for us to wait for it. Continue closing! */
+
+ if (channel->close_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Closing channel %lu/%lu",
+ channel->local.id, channel->remote.id);
+
+ channel->close_packet[0] = SSH_MSG_CHANNEL_CLOSE;
+ _libssh2_htonu32(channel->close_packet + 1, channel->remote.id);
+
+ channel->close_state = libssh2_NB_state_created;
+ }
+
+ if (channel->close_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->close_packet, 5,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending close-channel");
+ return rc;
+
+ } else if (rc) {
+ _libssh2_error(session, rc,
+ "Unable to send close-channel request, "
+ "but closing anyway");
+ /* skip waiting for the response and fall through to
+ LIBSSH2_CHANNEL_CLOSE below */
+
+ } else
+ channel->close_state = libssh2_NB_state_sent;
+ }
+
+ if (channel->close_state == libssh2_NB_state_sent) {
+ /* We must wait for the remote SSH_MSG_CHANNEL_CLOSE message */
+
+ while (!channel->remote.close && !rc &&
+ (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED))
+ rc = _libssh2_transport_read(session);
+ }
+
+ if(rc != LIBSSH2_ERROR_EAGAIN) {
+ /* set the local close state first when we're perfectly confirmed to not
+ do any more EAGAINs */
+ channel->local.close = 1;
+
+ /* We call the callback last in this function to make it keep the local
+ data as long as EAGAIN is returned. */
+ if (channel->close_cb) {
+ LIBSSH2_CHANNEL_CLOSE(session, channel);
+ }
+
+ channel->close_state = libssh2_NB_state_idle;
+ }
+
+ /* return 0 or an error */
+ return rc>=0?0:rc;
+}
+
+/*
+ * libssh2_channel_close
+ *
+ * Close a channel
+ */
+LIBSSH2_API int
+libssh2_channel_close(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, _libssh2_channel_close(channel) );
+ return rc;
+}
+
+/*
+ * channel_wait_closed
+ *
+ * Awaiting channel close after EOF
+ */
+static int channel_wait_closed(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc;
+
+ if (!libssh2_channel_eof(channel)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "libssh2_channel_wait_closed() invoked when "
+ "channel is not in EOF state");
+ }
+
+ if (channel->wait_closed_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Awaiting close of channel %lu/%lu", channel->local.id,
+ channel->remote.id);
+
+ channel->wait_closed_state = libssh2_NB_state_created;
+ }
+
+ /*
+ * While channel is not closed, read more packets from the network.
+ * Either the channel will be closed or network timeout will occur.
+ */
+ if (!channel->remote.close) {
+ do {
+ rc = _libssh2_transport_read(session);
+ if (channel->remote.close)
+ /* it is now closed, move on! */
+ break;
+ } while (rc > 0);
+ if(rc < 0)
+ return rc;
+ }
+
+ channel->wait_closed_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_wait_closed
+ *
+ * Awaiting channel close after EOF
+ */
+LIBSSH2_API int
+libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, channel_wait_closed(channel));
+ return rc;
+}
+
+/*
+ * _libssh2_channel_free
+ *
+ * Make sure a channel is closed, then remove the channel from the session
+ * and free its resource(s)
+ *
+ * Returns 0 on success, negative on failure
+ */
+int _libssh2_channel_free(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char channel_id[4];
+ unsigned char *data;
+ size_t data_len;
+ int rc;
+
+ assert(session);
+
+ if (channel->free_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Freeing channel %lu/%lu resources", channel->local.id,
+ channel->remote.id);
+
+ channel->free_state = libssh2_NB_state_created;
+ }
+
+ /* Allow channel freeing even when the socket has lost its connection */
+ if (!channel->local.close
+ && (session->socket_state == LIBSSH2_SOCKET_CONNECTED)) {
+ rc = _libssh2_channel_close(channel);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ /* ignore all other errors as they otherwise risk blocking the channel
+ free from happening */
+ }
+
+ channel->free_state = libssh2_NB_state_idle;
+
+ if (channel->exit_signal) {
+ LIBSSH2_FREE(session, channel->exit_signal);
+ }
+
+ /*
+ * channel->remote.close *might* not be set yet, Well...
+ * We've sent the close packet, what more do you want?
+ * Just let packet_add ignore it when it finally arrives
+ */
+
+ /* Clear out packets meant for this channel */
+ _libssh2_htonu32(channel_id, channel->local.id);
+ while ((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA, &data,
+ &data_len, 1, channel_id, 4) >= 0)
+ ||
+ (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data,
+ &data_len, 1, channel_id, 4) >= 0)) {
+ LIBSSH2_FREE(session, data);
+ }
+
+ /* free "channel_type" */
+ if (channel->channel_type) {
+ LIBSSH2_FREE(session, channel->channel_type);
+ }
+
+ /* Unlink from channel list */
+ _libssh2_list_remove(&channel->node);
+
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if (channel->setenv_packet) {
+ LIBSSH2_FREE(session, channel->setenv_packet);
+ }
+ if (channel->reqX11_packet) {
+ LIBSSH2_FREE(session, channel->reqX11_packet);
+ }
+ if (channel->process_packet) {
+ LIBSSH2_FREE(session, channel->process_packet);
+ }
+
+ LIBSSH2_FREE(session, channel);
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_free
+ *
+ * Make sure a channel is closed, then remove the channel from the session
+ * and free its resource(s)
+ *
+ * Returns 0 on success, negative on failure
+ */
+LIBSSH2_API int
+libssh2_channel_free(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, _libssh2_channel_free(channel));
+ return rc;
+}
+/*
+ * libssh2_channel_window_read_ex
+ *
+ * Check the status of the read window. Returns the number of bytes which the
+ * remote end may send without overflowing the window limit read_avail (if
+ * passed) will be populated with the number of bytes actually available to be
+ * read window_size_initial (if passed) will be populated with the
+ * window_size_initial as defined by the channel_open request
+ */
+LIBSSH2_API unsigned long
+libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel,
+ unsigned long *read_avail,
+ unsigned long *window_size_initial)
+{
+ if(!channel)
+ return 0; /* no channel, no window! */
+
+ if (window_size_initial) {
+ *window_size_initial = channel->remote.window_size_initial;
+ }
+
+ if (read_avail) {
+ size_t bytes_queued = 0;
+ LIBSSH2_PACKET *packet =
+ _libssh2_list_first(&channel->session->packets);
+
+ while (packet) {
+ unsigned char packet_type = packet->data[0];
+
+ if (((packet_type == SSH_MSG_CHANNEL_DATA)
+ || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA))
+ && (_libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
+ bytes_queued += packet->data_len - packet->data_head;
+ }
+
+ packet = _libssh2_list_next(&packet->node);
+ }
+
+ *read_avail = bytes_queued;
+ }
+
+ return channel->remote.window_size;
+}
+
+/*
+ * libssh2_channel_window_write_ex
+ *
+ * Check the status of the write window Returns the number of bytes which may
+ * be safely written on the channel without blocking window_size_initial (if
+ * passed) will be populated with the size of the initial window as defined by
+ * the channel_open request
+ */
+LIBSSH2_API unsigned long
+libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel,
+ unsigned long *window_size_initial)
+{
+ if(!channel)
+ return 0; /* no channel, no window! */
+
+ if (window_size_initial) {
+ /* For locally initiated channels this is very often 0, so it's not
+ * *that* useful as information goes */
+ *window_size_initial = channel->local.window_size_initial;
+ }
+
+ return channel->local.window_size;
+}
diff --git a/libs/libssh2/src/channel.h b/libs/libssh2/src/channel.h
new file mode 100644
index 0000000000..dc0ee3764f
--- /dev/null
+++ b/libs/libssh2/src/channel.h
@@ -0,0 +1,141 @@
+#ifndef __LIBSSH2_CHANNEL_H
+#define __LIBSSH2_CHANNEL_H
+/* Copyright (c) 2008-2010 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/*
+ * _libssh2_channel_receive_window_adjust
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Always non-blocking.
+ */
+int _libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel,
+ uint32_t adjustment,
+ unsigned char force,
+ unsigned int *store);
+
+/*
+ * _libssh2_channel_flush
+ *
+ * Flush data from one (or all) stream
+ * Returns number of bytes flushed, or negative on failure
+ */
+int _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid);
+
+/*
+ * _libssh2_channel_free
+ *
+ * Make sure a channel is closed, then remove the channel from the session
+ * and free its resource(s)
+ *
+ * Returns 0 on success, negative on failure
+ */
+int _libssh2_channel_free(LIBSSH2_CHANNEL *channel);
+
+int
+_libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
+
+/*
+ * _libssh2_channel_write
+ *
+ * Send data to a channel
+ */
+ssize_t
+_libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
+ const unsigned char *buf, size_t buflen);
+
+/*
+ * _libssh2_channel_open
+ *
+ * Establish a generic session channel
+ */
+LIBSSH2_CHANNEL *
+_libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type,
+ uint32_t channel_type_len,
+ uint32_t window_size,
+ uint32_t packet_size,
+ const unsigned char *message, size_t message_len);
+
+
+/*
+ * _libssh2_channel_process_startup
+ *
+ * Primitive for libssh2_channel_(shell|exec|subsystem)
+ */
+int
+_libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
+ const char *request, size_t request_len,
+ const char *message, size_t message_len);
+
+/*
+ * _libssh2_channel_read
+ *
+ * Read data from a channel
+ *
+ * It is important to not return 0 until the currently read channel is
+ * complete. If we read stuff from the wire but it was no payload data to fill
+ * in the buffer with, we MUST make sure to return PACKET_EAGAIN.
+ */
+ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
+ char *buf, size_t buflen);
+
+uint32_t _libssh2_channel_nextid(LIBSSH2_SESSION * session);
+
+LIBSSH2_CHANNEL *_libssh2_channel_locate(LIBSSH2_SESSION * session,
+ uint32_t channel_id);
+
+size_t _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel,
+ int stream_id);
+
+int _libssh2_channel_close(LIBSSH2_CHANNEL * channel);
+
+/*
+ * _libssh2_channel_forward_cancel
+ *
+ * Stop listening on a remote port and free the listener
+ * Toss out any pending (un-accept()ed) connections
+ *
+ * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error
+ */
+int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener);
+
+#endif /* __LIBSSH2_CHANNEL_H */
+
diff --git a/libs/libssh2/src/comp.c b/libs/libssh2/src/comp.c
new file mode 100644
index 0000000000..4560188bb7
--- /dev/null
+++ b/libs/libssh2/src/comp.c
@@ -0,0 +1,366 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2010-2014, Daniel Stenberg <daniel@haxx.se>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#ifdef LIBSSH2_HAVE_ZLIB
+# include <zlib.h>
+#endif
+
+#include "comp.h"
+
+/* ********
+ * none *
+ ******** */
+
+/*
+ * comp_method_none_comp
+ *
+ * Minimalist compression: Absolutely none
+ */
+static int
+comp_method_none_comp(LIBSSH2_SESSION *session,
+ unsigned char *dest,
+ size_t *dest_len,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract)
+{
+ (void) session;
+ (void) abstract;
+ (void) dest;
+ (void) dest_len;
+ (void) src;
+ (void) src_len;
+
+ return 0;
+}
+
+/*
+ * comp_method_none_decomp
+ *
+ * Minimalist decompression: Absolutely none
+ */
+static int
+comp_method_none_decomp(LIBSSH2_SESSION * session,
+ unsigned char **dest,
+ size_t *dest_len,
+ size_t payload_limit,
+ const unsigned char *src,
+ size_t src_len, void **abstract)
+{
+ (void) session;
+ (void) payload_limit;
+ (void) abstract;
+ *dest = (unsigned char *) src;
+ *dest_len = src_len;
+ return 0;
+}
+
+
+
+static const LIBSSH2_COMP_METHOD comp_method_none = {
+ "none",
+ 0, /* not really compressing */
+ 0, /* isn't used in userauth, go figure */
+ NULL,
+ comp_method_none_comp,
+ comp_method_none_decomp,
+ NULL
+};
+
+#ifdef LIBSSH2_HAVE_ZLIB
+/* ********
+ * zlib *
+ ******** */
+
+/* Memory management wrappers
+ * Yes, I realize we're doing a callback to a callback,
+ * Deal...
+ */
+
+static voidpf
+comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size)
+{
+ LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque;
+
+ return (voidpf) LIBSSH2_ALLOC(session, items * size);
+}
+
+static void
+comp_method_zlib_free(voidpf opaque, voidpf address)
+{
+ LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque;
+
+ LIBSSH2_FREE(session, address);
+}
+
+
+
+/* libssh2_comp_method_zlib_init
+ * All your bandwidth are belong to us (so save some)
+ */
+static int
+comp_method_zlib_init(LIBSSH2_SESSION * session, int compr,
+ void **abstract)
+{
+ z_stream *strm;
+ int status;
+
+ strm = LIBSSH2_CALLOC(session, sizeof(z_stream));
+ if (!strm) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "zlib compression/decompression");
+ }
+
+ strm->opaque = (voidpf) session;
+ strm->zalloc = (alloc_func) comp_method_zlib_alloc;
+ strm->zfree = (free_func) comp_method_zlib_free;
+ if (compr) {
+ /* deflate */
+ status = deflateInit(strm, Z_DEFAULT_COMPRESSION);
+ } else {
+ /* inflate */
+ status = inflateInit(strm);
+ }
+
+ if (status != Z_OK) {
+ LIBSSH2_FREE(session, strm);
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unhandled zlib error %d", status);
+ return LIBSSH2_ERROR_COMPRESS;
+ }
+ *abstract = strm;
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * libssh2_comp_method_zlib_comp
+ *
+ * Compresses source to destination. Without allocation.
+ */
+static int
+comp_method_zlib_comp(LIBSSH2_SESSION *session,
+ unsigned char *dest,
+
+ /* dest_len is a pointer to allow this function to
+ update it with the final actual size used */
+ size_t *dest_len,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract)
+{
+ z_stream *strm = *abstract;
+ int out_maxlen = *dest_len;
+ int status;
+
+ strm->next_in = (unsigned char *) src;
+ strm->avail_in = src_len;
+ strm->next_out = dest;
+ strm->avail_out = out_maxlen;
+
+ status = deflate(strm, Z_PARTIAL_FLUSH);
+
+ if ((status == Z_OK) && (strm->avail_out > 0)) {
+ *dest_len = out_maxlen - strm->avail_out;
+ return 0;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unhandled zlib compression error %d, avail_out", status, strm->avail_out);
+ return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compression failure");
+}
+
+/*
+ * libssh2_comp_method_zlib_decomp
+ *
+ * Decompresses source to destination. Allocates the output memory.
+ */
+static int
+comp_method_zlib_decomp(LIBSSH2_SESSION * session,
+ unsigned char **dest,
+ size_t *dest_len,
+ size_t payload_limit,
+ const unsigned char *src,
+ size_t src_len, void **abstract)
+{
+ z_stream *strm = *abstract;
+ /* A short-term alloc of a full data chunk is better than a series of
+ reallocs */
+ char *out;
+ int out_maxlen = 4 * src_len;
+
+ /* If strm is null, then we have not yet been initialized. */
+ if (strm == NULL)
+ return _libssh2_error(session, LIBSSH2_ERROR_COMPRESS,
+ "decompression uninitialized");;
+
+ /* In practice they never come smaller than this */
+ if (out_maxlen < 25)
+ out_maxlen = 25;
+
+ if (out_maxlen > (int) payload_limit)
+ out_maxlen = payload_limit;
+
+ strm->next_in = (unsigned char *) src;
+ strm->avail_in = src_len;
+ strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen);
+ out = (char *) strm->next_out;
+ strm->avail_out = out_maxlen;
+ if (!strm->next_out)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate decompression buffer");
+
+ /* Loop until it's all inflated or hit error */
+ for (;;) {
+ int status;
+ size_t out_ofs;
+ char *newout;
+
+ status = inflate(strm, Z_PARTIAL_FLUSH);
+
+ if (status == Z_OK) {
+ if (strm->avail_out > 0)
+ /* status is OK and the output buffer has not been exhausted so we're done */
+ break;
+ } else if (status == Z_BUF_ERROR) {
+ /* the input data has been exhausted so we are done */
+ break;
+ } else {
+ /* error state */
+ LIBSSH2_FREE(session, out);
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unhandled zlib error %d", status);
+ return _libssh2_error(session, LIBSSH2_ERROR_ZLIB,
+ "decompression failure");
+ }
+
+ if (out_maxlen >= (int) payload_limit) {
+ LIBSSH2_FREE(session, out);
+ return _libssh2_error(session, LIBSSH2_ERROR_ZLIB,
+ "Excessive growth in decompression phase");
+ }
+
+ /* If we get here we need to grow the output buffer and try again */
+ out_ofs = out_maxlen - strm->avail_out;
+ out_maxlen *= 2;
+ newout = LIBSSH2_REALLOC(session, out, out_maxlen);
+ if (!newout) {
+ LIBSSH2_FREE(session, out);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to expand decompression buffer");
+ }
+ out = newout;
+ strm->next_out = (unsigned char *) out + out_ofs;
+ strm->avail_out = out_maxlen - out_ofs;
+ }
+
+ *dest = (unsigned char *) out;
+ *dest_len = out_maxlen - strm->avail_out;
+
+ return 0;
+}
+
+
+/* libssh2_comp_method_zlib_dtor
+ * All done, no more compression for you
+ */
+static int
+comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compr, void **abstract)
+{
+ z_stream *strm = *abstract;
+
+ if (strm) {
+ if (compr)
+ deflateEnd(strm);
+ else
+ inflateEnd(strm);
+ LIBSSH2_FREE(session, strm);
+ }
+
+ *abstract = NULL;
+ return 0;
+}
+
+static const LIBSSH2_COMP_METHOD comp_method_zlib = {
+ "zlib",
+ 1, /* yes, this compresses */
+ 1, /* do compression during userauth */
+ comp_method_zlib_init,
+ comp_method_zlib_comp,
+ comp_method_zlib_decomp,
+ comp_method_zlib_dtor,
+};
+
+static const LIBSSH2_COMP_METHOD comp_method_zlib_openssh = {
+ "zlib@openssh.com",
+ 1, /* yes, this compresses */
+ 0, /* don't use compression during userauth */
+ comp_method_zlib_init,
+ comp_method_zlib_comp,
+ comp_method_zlib_decomp,
+ comp_method_zlib_dtor,
+};
+#endif /* LIBSSH2_HAVE_ZLIB */
+
+/* If compression is enabled by the API, then this array is used which then
+ may allow compression if zlib is available at build time */
+static const LIBSSH2_COMP_METHOD *comp_methods[] = {
+#ifdef LIBSSH2_HAVE_ZLIB
+ &comp_method_zlib,
+ &comp_method_zlib_openssh,
+#endif /* LIBSSH2_HAVE_ZLIB */
+ &comp_method_none,
+ NULL
+};
+
+/* If compression is disabled by the API, then this array is used */
+static const LIBSSH2_COMP_METHOD *no_comp_methods[] = {
+ &comp_method_none,
+ NULL
+};
+
+const LIBSSH2_COMP_METHOD **
+_libssh2_comp_methods(LIBSSH2_SESSION *session)
+{
+ if(session->flag.compress)
+ return comp_methods;
+ else
+ return no_comp_methods;
+}
diff --git a/libs/libssh2/src/comp.h b/libs/libssh2/src/comp.h
new file mode 100644
index 0000000000..8edc150299
--- /dev/null
+++ b/libs/libssh2/src/comp.h
@@ -0,0 +1,45 @@
+#ifndef __LIBSSH2_COMP_H
+#define __LIBSSH2_COMP_H
+
+/* Copyright (C) 2009-2010 by Daniel Stenberg
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+#include "libssh2_priv.h"
+
+const LIBSSH2_COMP_METHOD **_libssh2_comp_methods(LIBSSH2_SESSION *session);
+
+#endif /* __LIBSSH2_COMP_H */
diff --git a/libs/libssh2/src/crypt.c b/libs/libssh2/src/crypt.c
new file mode 100644
index 0000000000..931ae8b805
--- /dev/null
+++ b/libs/libssh2/src/crypt.c
@@ -0,0 +1,336 @@
+/* Copyright (c) 2009, 2010 Simon Josefsson <simon@josefsson.org>
+ * Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#ifdef LIBSSH2_CRYPT_NONE
+
+/* crypt_none_crypt
+ * Minimalist cipher: VERY secure *wink*
+ */
+static int
+crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf,
+ void **abstract)
+{
+ /* Do nothing to the data! */
+ return 0;
+}
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_none = {
+ "none",
+ 8, /* blocksize (SSH2 defines minimum blocksize as 8) */
+ 0, /* iv_len */
+ 0, /* secret_len */
+ 0, /* flags */
+ NULL,
+ crypt_none_crypt,
+ NULL
+};
+#endif /* LIBSSH2_CRYPT_NONE */
+
+struct crypt_ctx
+{
+ int encrypt;
+ _libssh2_cipher_type(algo);
+ _libssh2_cipher_ctx h;
+};
+
+static int
+crypt_init(LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method,
+ unsigned char *iv, int *free_iv,
+ unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract)
+{
+ struct crypt_ctx *ctx = LIBSSH2_ALLOC(session,
+ sizeof(struct crypt_ctx));
+ if (!ctx)
+ return LIBSSH2_ERROR_ALLOC;
+
+ ctx->encrypt = encrypt;
+ ctx->algo = method->algo;
+ if (_libssh2_cipher_init(&ctx->h, ctx->algo, iv, secret, encrypt)) {
+ LIBSSH2_FREE(session, ctx);
+ return -1;
+ }
+ *abstract = ctx;
+ *free_iv = 1;
+ *free_secret = 1;
+ return 0;
+}
+
+static int
+crypt_encrypt(LIBSSH2_SESSION * session, unsigned char *block,
+ size_t blocksize, void **abstract)
+{
+ struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
+ (void) session;
+ return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
+ blocksize);
+}
+
+static int
+crypt_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ struct crypt_ctx **cctx = (struct crypt_ctx **) abstract;
+ if (cctx && *cctx) {
+ _libssh2_cipher_dtor(&(*cctx)->h);
+ LIBSSH2_FREE(session, *cctx);
+ *abstract = NULL;
+ }
+ return 0;
+}
+
+#if LIBSSH2_AES_CTR
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
+ "aes128-ctr",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 16, /* secret length -- 16*8 == 128bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes128ctr
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = {
+ "aes192-ctr",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 24, /* secret length -- 24*8 == 192bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes192ctr
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = {
+ "aes256-ctr",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 32, /* secret length -- 32*8 == 256bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes256ctr
+};
+#endif
+
+#if LIBSSH2_AES
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = {
+ "aes128-cbc",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 16, /* secret length -- 16*8 == 128bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes128
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = {
+ "aes192-cbc",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 24, /* secret length -- 24*8 == 192bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes192
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = {
+ "aes256-cbc",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 32, /* secret length -- 32*8 == 256bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes256
+};
+
+/* rijndael-cbc@lysator.liu.se == aes256-cbc */
+static const LIBSSH2_CRYPT_METHOD
+ libssh2_crypt_method_rijndael_cbc_lysator_liu_se = {
+ "rijndael-cbc@lysator.liu.se",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 32, /* secret length -- 32*8 == 256bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes256
+};
+#endif /* LIBSSH2_AES */
+
+#if LIBSSH2_BLOWFISH
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = {
+ "blowfish-cbc",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_blowfish
+};
+#endif /* LIBSSH2_BLOWFISH */
+
+#if LIBSSH2_RC4
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = {
+ "arcfour",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_arcfour
+};
+
+static int
+crypt_init_arcfour128(LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method,
+ unsigned char *iv, int *free_iv,
+ unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract)
+{
+ int rc;
+
+ rc = crypt_init (session, method, iv, free_iv, secret, free_secret,
+ encrypt, abstract);
+ if (rc == 0) {
+ struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
+ unsigned char block[8];
+ size_t discard = 1536;
+ for (; discard; discard -= 8)
+ _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
+ method->blocksize);
+ }
+
+ return rc;
+}
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour128 = {
+ "arcfour128",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init_arcfour128,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_arcfour
+};
+#endif /* LIBSSH2_RC4 */
+
+#if LIBSSH2_CAST
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = {
+ "cast128-cbc",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_cast5
+};
+#endif /* LIBSSH2_CAST */
+
+#if LIBSSH2_3DES
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = {
+ "3des-cbc",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 24, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_3des
+};
+#endif
+
+static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
+#if LIBSSH2_AES_CTR
+ &libssh2_crypt_method_aes128_ctr,
+ &libssh2_crypt_method_aes192_ctr,
+ &libssh2_crypt_method_aes256_ctr,
+#endif /* LIBSSH2_AES */
+#if LIBSSH2_AES
+ &libssh2_crypt_method_aes256_cbc,
+ &libssh2_crypt_method_rijndael_cbc_lysator_liu_se, /* == aes256-cbc */
+ &libssh2_crypt_method_aes192_cbc,
+ &libssh2_crypt_method_aes128_cbc,
+#endif /* LIBSSH2_AES */
+#if LIBSSH2_BLOWFISH
+ &libssh2_crypt_method_blowfish_cbc,
+#endif /* LIBSSH2_BLOWFISH */
+#if LIBSSH2_RC4
+ &libssh2_crypt_method_arcfour128,
+ &libssh2_crypt_method_arcfour,
+#endif /* LIBSSH2_RC4 */
+#if LIBSSH2_CAST
+ &libssh2_crypt_method_cast128_cbc,
+#endif /* LIBSSH2_CAST */
+#if LIBSSH2_3DES
+ &libssh2_crypt_method_3des_cbc,
+#endif /* LIBSSH2_DES */
+#ifdef LIBSSH2_CRYPT_NONE
+ &libssh2_crypt_method_none,
+#endif
+ NULL
+};
+
+/* Expose to kex.c */
+const LIBSSH2_CRYPT_METHOD **
+libssh2_crypt_methods(void)
+{
+ return _libssh2_crypt_methods;
+}
diff --git a/libs/libssh2/src/crypto.h b/libs/libssh2/src/crypto.h
new file mode 100644
index 0000000000..aa997a3071
--- /dev/null
+++ b/libs/libssh2/src/crypto.h
@@ -0,0 +1,150 @@
+/* Copyright (C) 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (C) 2010 Daniel Stenberg
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+#ifndef LIBSSH2_CRYPTO_H
+#define LIBSSH2_CRYPTO_H
+
+#ifdef LIBSSH2_OPENSSL
+#include "openssl.h"
+#endif
+
+#ifdef LIBSSH2_LIBGCRYPT
+#include "libgcrypt.h"
+#endif
+
+#ifdef LIBSSH2_WINCNG
+#include "wincng.h"
+#endif
+
+#ifdef LIBSSH2_OS400QC3
+#include "os400qc3.h"
+#endif
+
+#ifdef LIBSSH2_MBEDTLS
+#include "mbedtls.h"
+#endif
+
+int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata, unsigned long coefflen);
+int _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase);
+int _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len);
+int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len);
+int _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase);
+
+#if LIBSSH2_DSA
+int _libssh2_dsa_new(libssh2_dsa_ctx ** dsa,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *gdata,
+ unsigned long glen,
+ const unsigned char *ydata,
+ unsigned long ylen,
+ const unsigned char *x, unsigned long x_len);
+int _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase);
+int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx,
+ const unsigned char *sig,
+ const unsigned char *m, unsigned long m_len);
+int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
+ const unsigned char *hash,
+ unsigned long hash_len, unsigned char *sig);
+int _libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase);
+#endif
+
+int _libssh2_cipher_init(_libssh2_cipher_ctx * h,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv,
+ unsigned char *secret, int encrypt);
+
+int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt, unsigned char *block, size_t blocksize);
+
+int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase);
+int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase);
+
+void _libssh2_init_aes_ctr(void);
+
+#endif
diff --git a/libs/libssh2/src/global.c b/libs/libssh2/src/global.c
new file mode 100644
index 0000000000..dc45e70036
--- /dev/null
+++ b/libs/libssh2/src/global.c
@@ -0,0 +1,78 @@
+/* Copyright (c) 2010 Lars Nordin <Lars.Nordin@SDlabs.se>
+ * Copyright (C) 2010 Simon Josefsson <simon@josefsson.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+static int _libssh2_initialized = 0;
+static int _libssh2_init_flags = 0;
+
+LIBSSH2_API int
+libssh2_init(int flags)
+{
+ if (_libssh2_initialized == 0 && !(flags & LIBSSH2_INIT_NO_CRYPTO)) {
+ libssh2_crypto_init();
+ _libssh2_init_aes_ctr();
+ }
+
+ _libssh2_initialized++;
+ _libssh2_init_flags |= flags;
+
+ return 0;
+}
+
+LIBSSH2_API void
+libssh2_exit(void)
+{
+ if (_libssh2_initialized == 0)
+ return;
+
+ _libssh2_initialized--;
+
+ if (!(_libssh2_init_flags & LIBSSH2_INIT_NO_CRYPTO)) {
+ libssh2_crypto_exit();
+ }
+
+ return;
+}
+
+void
+_libssh2_init_if_needed(void)
+{
+ if (_libssh2_initialized == 0)
+ (void)libssh2_init (0);
+}
diff --git a/libs/libssh2/src/hostkey.c b/libs/libssh2/src/hostkey.c
new file mode 100644
index 0000000000..2a0a8f943b
--- /dev/null
+++ b/libs/libssh2/src/hostkey.c
@@ -0,0 +1,573 @@
+/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include "misc.h"
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#if LIBSSH2_RSA
+/* ***********
+ * ssh-rsa *
+ *********** */
+
+static int hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session,
+ void **abstract);
+
+/*
+ * hostkey_method_ssh_rsa_init
+ *
+ * Initialize the server hostkey working area with e/n pair
+ */
+static int
+hostkey_method_ssh_rsa_init(LIBSSH2_SESSION * session,
+ const unsigned char *hostkey_data,
+ size_t hostkey_data_len,
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx;
+ const unsigned char *s, *e, *n;
+ unsigned long len, e_len, n_len;
+ int ret;
+
+ (void) hostkey_data_len;
+
+ if (*abstract) {
+ hostkey_method_ssh_rsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ s = hostkey_data;
+ len = _libssh2_ntohu32(s);
+ s += 4;
+
+ if (len != 7 || strncmp((char *) s, "ssh-rsa", 7) != 0) {
+ return -1;
+ }
+ s += 7;
+
+ e_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ e = s;
+ s += e_len;
+ n_len = _libssh2_ntohu32(s);
+ s += 4;
+ n = s;
+
+ ret = _libssh2_rsa_new(&rsactx, e, e_len, n, n_len, NULL, 0,
+ NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
+ if (ret) {
+ return -1;
+ }
+
+ *abstract = rsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_rsa_initPEM
+ *
+ * Load a Private Key from a PEM file
+ */
+static int
+hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION * session,
+ const char *privkeyfile,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx;
+ int ret;
+
+ if (*abstract) {
+ hostkey_method_ssh_rsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_rsa_new_private(&rsactx, session, privkeyfile, passphrase);
+ if (ret) {
+ return -1;
+ }
+
+ *abstract = rsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_rsa_initPEMFromMemory
+ *
+ * Load a Private Key from a memory
+ */
+static int
+hostkey_method_ssh_rsa_initPEMFromMemory(LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx;
+ int ret;
+
+ if (*abstract) {
+ hostkey_method_ssh_rsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_rsa_new_private_frommemory(&rsactx, session,
+ privkeyfiledata,
+ privkeyfiledata_len, passphrase);
+ if (ret) {
+ return -1;
+ }
+
+ *abstract = rsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_rsa_sign
+ *
+ * Verify signature created by remote
+ */
+static int
+hostkey_method_ssh_rsa_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+ (void) session;
+
+ /* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */
+ sig += 15;
+ sig_len -= 15;
+ return _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len);
+}
+
+/*
+ * hostkey_method_ssh_rsa_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+
+#ifdef _libssh2_rsa_sha1_signv
+ return _libssh2_rsa_sha1_signv(session, signature, signature_len,
+ veccount, datavec, rsactx);
+#else
+ int ret;
+ int i;
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ libssh2_sha1_ctx ctx;
+
+ libssh2_sha1_init(&ctx);
+ for(i = 0; i < veccount; i++) {
+ libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
+ }
+ libssh2_sha1_final(ctx, hash);
+
+ ret = _libssh2_rsa_sha1_sign(session, rsactx, hash, SHA_DIGEST_LENGTH,
+ signature, signature_len);
+ if (ret) {
+ return -1;
+ }
+
+ return 0;
+#endif
+}
+
+/*
+ * hostkey_method_ssh_rsa_dtor
+ *
+ * Shutdown the hostkey
+ */
+static int
+hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+ (void) session;
+
+ _libssh2_rsa_free(rsactx);
+
+ *abstract = NULL;
+
+ return 0;
+}
+
+#ifdef OPENSSL_NO_MD5
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa = {
+ "ssh-rsa",
+ MD5_DIGEST_LENGTH,
+ hostkey_method_ssh_rsa_init,
+ hostkey_method_ssh_rsa_initPEM,
+ hostkey_method_ssh_rsa_initPEMFromMemory,
+ hostkey_method_ssh_rsa_sig_verify,
+ hostkey_method_ssh_rsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_rsa_dtor,
+};
+#endif /* LIBSSH2_RSA */
+
+#if LIBSSH2_DSA
+/* ***********
+ * ssh-dss *
+ *********** */
+
+static int hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session,
+ void **abstract);
+
+/*
+ * hostkey_method_ssh_dss_init
+ *
+ * Initialize the server hostkey working area with p/q/g/y set
+ */
+static int
+hostkey_method_ssh_dss_init(LIBSSH2_SESSION * session,
+ const unsigned char *hostkey_data,
+ size_t hostkey_data_len,
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx;
+ const unsigned char *p, *q, *g, *y, *s;
+ unsigned long p_len, q_len, g_len, y_len, len;
+ int ret;
+
+ (void) hostkey_data_len;
+
+ if (*abstract) {
+ hostkey_method_ssh_dss_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ s = hostkey_data;
+ len = _libssh2_ntohu32(s);
+ s += 4;
+ if (len != 7 || strncmp((char *) s, "ssh-dss", 7) != 0) {
+ return -1;
+ }
+ s += 7;
+
+ p_len = _libssh2_ntohu32(s);
+ s += 4;
+ p = s;
+ s += p_len;
+ q_len = _libssh2_ntohu32(s);
+ s += 4;
+ q = s;
+ s += q_len;
+ g_len = _libssh2_ntohu32(s);
+ s += 4;
+ g = s;
+ s += g_len;
+ y_len = _libssh2_ntohu32(s);
+ s += 4;
+ y = s;
+ /* s += y_len; */
+
+ ret = _libssh2_dsa_new(&dsactx, p, p_len, q, q_len,
+ g, g_len, y, y_len, NULL, 0);
+ if (ret) {
+ return -1;
+ }
+
+ *abstract = dsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_dss_initPEM
+ *
+ * Load a Private Key from a PEM file
+ */
+static int
+hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION * session,
+ const char *privkeyfile,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx;
+ int ret;
+
+ if (*abstract) {
+ hostkey_method_ssh_dss_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_dsa_new_private(&dsactx, session, privkeyfile, passphrase);
+ if (ret) {
+ return -1;
+ }
+
+ *abstract = dsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_dss_initPEMFromMemory
+ *
+ * Load a Private Key from memory
+ */
+static int
+hostkey_method_ssh_dss_initPEMFromMemory(LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx;
+ int ret;
+
+ if (*abstract) {
+ hostkey_method_ssh_dss_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_dsa_new_private_frommemory(&dsactx, session,
+ privkeyfiledata,
+ privkeyfiledata_len, passphrase);
+ if (ret) {
+ return -1;
+ }
+
+ *abstract = dsactx;
+
+ return 0;
+}
+
+/*
+ * libssh2_hostkey_method_ssh_dss_sign
+ *
+ * Verify signature created by remote
+ */
+static int
+hostkey_method_ssh_dss_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
+
+ /* Skip past keyname_len(4) + keyname(7){"ssh-dss"} + signature_len(4) */
+ sig += 15;
+ sig_len -= 15;
+ if (sig_len != 40) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid DSS signature length");
+ }
+ return _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len);
+}
+
+/*
+ * hostkey_method_ssh_dss_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_dss_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ libssh2_sha1_ctx ctx;
+ int i;
+
+ *signature = LIBSSH2_CALLOC(session, 2 * SHA_DIGEST_LENGTH);
+ if (!*signature) {
+ return -1;
+ }
+
+ *signature_len = 2 * SHA_DIGEST_LENGTH;
+
+ libssh2_sha1_init(&ctx);
+ for(i = 0; i < veccount; i++) {
+ libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
+ }
+ libssh2_sha1_final(ctx, hash);
+
+ if (_libssh2_dsa_sha1_sign(dsactx, hash, SHA_DIGEST_LENGTH, *signature)) {
+ LIBSSH2_FREE(session, *signature);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * libssh2_hostkey_method_ssh_dss_dtor
+ *
+ * Shutdown the hostkey method
+ */
+static int
+hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
+ (void) session;
+
+ _libssh2_dsa_free(dsactx);
+
+ *abstract = NULL;
+
+ return 0;
+}
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_dss = {
+ "ssh-dss",
+ MD5_DIGEST_LENGTH,
+ hostkey_method_ssh_dss_init,
+ hostkey_method_ssh_dss_initPEM,
+ hostkey_method_ssh_dss_initPEMFromMemory,
+ hostkey_method_ssh_dss_sig_verify,
+ hostkey_method_ssh_dss_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_dss_dtor,
+};
+#endif /* LIBSSH2_DSA */
+
+static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = {
+#if LIBSSH2_RSA
+ &hostkey_method_ssh_rsa,
+#endif /* LIBSSH2_RSA */
+#if LIBSSH2_DSA
+ &hostkey_method_ssh_dss,
+#endif /* LIBSSH2_DSA */
+ NULL
+};
+
+const LIBSSH2_HOSTKEY_METHOD **
+libssh2_hostkey_methods(void)
+{
+ return hostkey_methods;
+}
+
+/*
+ * libssh2_hostkey_hash
+ *
+ * Returns hash signature
+ * Returned buffer should NOT be freed
+ * Length of buffer is determined by hash type
+ * i.e. MD5 == 16, SHA1 == 20
+ */
+LIBSSH2_API const char *
+libssh2_hostkey_hash(LIBSSH2_SESSION * session, int hash_type)
+{
+ switch (hash_type) {
+#if LIBSSH2_MD5
+ case LIBSSH2_HOSTKEY_HASH_MD5:
+ return (session->server_hostkey_md5_valid)
+ ? (char *) session->server_hostkey_md5
+ : NULL;
+ break;
+#endif /* LIBSSH2_MD5 */
+ case LIBSSH2_HOSTKEY_HASH_SHA1:
+ return (session->server_hostkey_sha1_valid)
+ ? (char *) session->server_hostkey_sha1
+ : NULL;
+ break;
+ default:
+ return NULL;
+ }
+}
+
+static int hostkey_type(const unsigned char *hostkey, size_t len)
+{
+ const unsigned char rsa[] = {
+ 0, 0, 0, 0x07, 's', 's', 'h', '-', 'r', 's', 'a'
+ };
+ const unsigned char dss[] = {
+ 0, 0, 0, 0x07, 's', 's', 'h', '-', 'd', 's', 's'
+ };
+
+ if (len < 11)
+ return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
+
+ if (!memcmp(rsa, hostkey, 11))
+ return LIBSSH2_HOSTKEY_TYPE_RSA;
+
+ if (!memcmp(dss, hostkey, 11))
+ return LIBSSH2_HOSTKEY_TYPE_DSS;
+
+ return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
+}
+
+/*
+ * libssh2_session_hostkey()
+ *
+ * Returns the server key and length.
+ *
+ */
+LIBSSH2_API const char *
+libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
+{
+ if(session->server_hostkey_len) {
+ if(len)
+ *len = session->server_hostkey_len;
+ if (type)
+ *type = hostkey_type(session->server_hostkey,
+ session->server_hostkey_len);
+ return (char *) session->server_hostkey;
+ }
+ if(len)
+ *len = 0;
+ return NULL;
+}
+
diff --git a/libs/libssh2/src/keepalive.c b/libs/libssh2/src/keepalive.c
new file mode 100644
index 0000000000..fd749dd296
--- /dev/null
+++ b/libs/libssh2/src/keepalive.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 2010 Simon Josefsson
+ * Author: Simon Josefsson
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+#include "libssh2_priv.h"
+#include "transport.h" /* _libssh2_transport_write */
+
+/* Keep-alive stuff. */
+
+LIBSSH2_API void
+libssh2_keepalive_config (LIBSSH2_SESSION *session,
+ int want_reply,
+ unsigned interval)
+{
+ if (interval == 1)
+ session->keepalive_interval = 2;
+ else
+ session->keepalive_interval = interval;
+ session->keepalive_want_reply = want_reply ? 1 : 0;
+}
+
+LIBSSH2_API int
+libssh2_keepalive_send (LIBSSH2_SESSION *session,
+ int *seconds_to_next)
+{
+ time_t now;
+
+ if (!session->keepalive_interval) {
+ if (seconds_to_next)
+ *seconds_to_next = 0;
+ return 0;
+ }
+
+ now = time (NULL);
+
+ if (session->keepalive_last_sent + session->keepalive_interval <= now) {
+ /* Format is
+ "SSH_MSG_GLOBAL_REQUEST || 4-byte len || str || want-reply". */
+ unsigned char keepalive_data[]
+ = "\x50\x00\x00\x00\x15keepalive@libssh2.orgW";
+ size_t len = sizeof (keepalive_data) - 1;
+ int rc;
+
+ keepalive_data[len - 1] =
+ (unsigned char)session->keepalive_want_reply;
+
+ rc = _libssh2_transport_send(session, keepalive_data, len, NULL, 0);
+ /* Silently ignore PACKET_EAGAIN here: if the write buffer is
+ already full, sending another keepalive is not useful. */
+ if (rc && rc != LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send keepalive message");
+ return rc;
+ }
+
+ session->keepalive_last_sent = now;
+ if (seconds_to_next)
+ *seconds_to_next = session->keepalive_interval;
+ } else if (seconds_to_next) {
+ *seconds_to_next = (int) (session->keepalive_last_sent - now)
+ + session->keepalive_interval;
+ }
+
+ return 0;
+}
diff --git a/libs/libssh2/src/kex.c b/libs/libssh2/src/kex.c
new file mode 100644
index 0000000000..65b722f421
--- /dev/null
+++ b/libs/libssh2/src/kex.c
@@ -0,0 +1,2794 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2010, Daniel Stenberg <daniel@haxx.se>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#include "transport.h"
+#include "comp.h"
+#include "mac.h"
+
+/* TODO: Switch this to an inline and handle alloc() failures */
+/* Helper macro called from kex_method_diffie_hellman_group1_sha1_key_exchange */
+#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \
+ { \
+ libssh2_sha1_ctx hash; \
+ unsigned long len = 0; \
+ if (!(value)) { \
+ value = LIBSSH2_ALLOC(session, reqlen + SHA_DIGEST_LENGTH); \
+ } \
+ if (value) \
+ while (len < (unsigned long)reqlen) { \
+ libssh2_sha1_init(&hash); \
+ libssh2_sha1_update(hash, exchange_state->k_value, \
+ exchange_state->k_value_len); \
+ libssh2_sha1_update(hash, exchange_state->h_sig_comp, \
+ SHA_DIGEST_LENGTH); \
+ if (len > 0) { \
+ libssh2_sha1_update(hash, value, len); \
+ } else { \
+ libssh2_sha1_update(hash, (version), 1); \
+ libssh2_sha1_update(hash, session->session_id, \
+ session->session_id_len); \
+ } \
+ libssh2_sha1_final(hash, (value) + len); \
+ len += SHA_DIGEST_LENGTH; \
+ } \
+ }
+
+
+/* Helper macro called from kex_method_diffie_hellman_group1_sha256_key_exchange */
+#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(value, reqlen, version) \
+ { \
+ libssh2_sha256_ctx hash; \
+ unsigned long len = 0; \
+ if (!(value)) { \
+ value = LIBSSH2_ALLOC(session, reqlen + SHA256_DIGEST_LENGTH); \
+ } \
+ if (value) \
+ while (len < (unsigned long)reqlen) { \
+ libssh2_sha256_init(&hash); \
+ libssh2_sha256_update(hash, exchange_state->k_value, \
+ exchange_state->k_value_len); \
+ libssh2_sha256_update(hash, exchange_state->h_sig_comp, \
+ SHA256_DIGEST_LENGTH); \
+ if (len > 0) { \
+ libssh2_sha256_update(hash, value, len); \
+ } else { \
+ libssh2_sha256_update(hash, (version), 1); \
+ libssh2_sha256_update(hash, session->session_id, \
+ session->session_id_len); \
+ } \
+ libssh2_sha256_final(hash, (value) + len); \
+ len += SHA256_DIGEST_LENGTH; \
+ } \
+ }
+
+
+/*
+ * diffie_hellman_sha1
+ *
+ * Diffie Hellman Key Exchange, Group Agnostic
+ */
+static int diffie_hellman_sha1(LIBSSH2_SESSION *session,
+ _libssh2_bn *g,
+ _libssh2_bn *p,
+ int group_order,
+ unsigned char packet_type_init,
+ unsigned char packet_type_reply,
+ unsigned char *midhash,
+ unsigned long midhash_len,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ int ret = 0;
+ int rc;
+ libssh2_sha1_ctx exchange_hash_ctx;
+
+ if (exchange_state->state == libssh2_NB_state_idle) {
+ /* Setup initial values */
+ exchange_state->e_packet = NULL;
+ exchange_state->s_packet = NULL;
+ exchange_state->k_value = NULL;
+ exchange_state->ctx = _libssh2_bn_ctx_new();
+ exchange_state->x = _libssh2_bn_init(); /* Random from client */
+ exchange_state->e = _libssh2_bn_init(); /* g^x mod p */
+ exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from server) mod p */
+ exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */
+
+ /* Zero the whole thing out */
+ memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t));
+
+ /* Generate x and e */
+ _libssh2_bn_rand(exchange_state->x, group_order * 8 - 1, 0, -1);
+ _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p,
+ exchange_state->ctx);
+
+ /* Send KEX init */
+ /* packet_type(1) + String Length(4) + leading 0(1) */
+ exchange_state->e_packet_len =
+ _libssh2_bn_bytes(exchange_state->e) + 6;
+ if (_libssh2_bn_bits(exchange_state->e) % 8) {
+ /* Leading 00 not needed */
+ exchange_state->e_packet_len--;
+ }
+
+ exchange_state->e_packet =
+ LIBSSH2_ALLOC(session, exchange_state->e_packet_len);
+ if (!exchange_state->e_packet) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory error");
+ goto clean_exit;
+ }
+ exchange_state->e_packet[0] = packet_type_init;
+ _libssh2_htonu32(exchange_state->e_packet + 1,
+ exchange_state->e_packet_len - 5);
+ if (_libssh2_bn_bits(exchange_state->e) % 8) {
+ _libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 5);
+ } else {
+ exchange_state->e_packet[5] = 0;
+ _libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 6);
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending KEX packet %d",
+ (int) packet_type_init);
+ exchange_state->state = libssh2_NB_state_created;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, exchange_state->e_packet,
+ exchange_state->e_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send KEX init message");
+ goto clean_exit;
+ }
+ exchange_state->state = libssh2_NB_state_sent;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent) {
+ if (session->burn_optimistic_kexinit) {
+ /* The first KEX packet to come along will be the guess initially
+ * sent by the server. That guess turned out to be wrong so we
+ * need to silently ignore it */
+ int burn_type;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Waiting for badly guessed KEX packet (to be ignored)");
+ burn_type =
+ _libssh2_packet_burn(session, &exchange_state->burn_state);
+ if (burn_type == LIBSSH2_ERROR_EAGAIN) {
+ return burn_type;
+ } else if (burn_type <= 0) {
+ /* Failed to receive a packet */
+ ret = burn_type;
+ goto clean_exit;
+ }
+ session->burn_optimistic_kexinit = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Burnt packet of type: %02x",
+ (unsigned int) burn_type);
+ }
+
+ exchange_state->state = libssh2_NB_state_sent1;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent1) {
+ /* Wait for KEX reply */
+ rc = _libssh2_packet_require(session, packet_type_reply,
+ &exchange_state->s_packet,
+ &exchange_state->s_packet_len, 0, NULL,
+ 0, &exchange_state->req_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ if (rc) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Timed out waiting for KEX reply");
+ goto clean_exit;
+ }
+
+ /* Parse KEXDH_REPLY */
+ exchange_state->s = exchange_state->s_packet + 1;
+
+ session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s);
+ exchange_state->s += 4;
+
+ if (session->server_hostkey)
+ LIBSSH2_FREE(session, session->server_hostkey);
+
+ session->server_hostkey =
+ LIBSSH2_ALLOC(session, session->server_hostkey_len);
+ if (!session->server_hostkey) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for a copy "
+ "of the host key");
+ goto clean_exit;
+ }
+ memcpy(session->server_hostkey, exchange_state->s,
+ session->server_hostkey_len);
+ exchange_state->s += session->server_hostkey_len;
+
+#if LIBSSH2_MD5
+ {
+ libssh2_md5_ctx fingerprint_ctx;
+
+ if (libssh2_md5_init(&fingerprint_ctx)) {
+ libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_md5_final(fingerprint_ctx,
+ session->server_hostkey_md5);
+ session->server_hostkey_md5_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_md5_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[50], *fprint = fingerprint;
+ int i;
+ for(i = 0; i < 16; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's MD5 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+#endif /* ! LIBSSH2_MD5 */
+
+ {
+ libssh2_sha1_ctx fingerprint_ctx;
+
+ if (libssh2_sha1_init(&fingerprint_ctx)) {
+ libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha1_final(fingerprint_ctx,
+ session->server_hostkey_sha1);
+ session->server_hostkey_sha1_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha1_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[64], *fprint = fingerprint;
+ int i;
+
+ for(i = 0; i < 20; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA1 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+
+ if (session->hostkey->init(session, session->server_hostkey,
+ session->server_hostkey_len,
+ &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to initialize hostkey importer");
+ goto clean_exit;
+ }
+
+ exchange_state->f_value_len = _libssh2_ntohu32(exchange_state->s);
+ exchange_state->s += 4;
+ exchange_state->f_value = exchange_state->s;
+ exchange_state->s += exchange_state->f_value_len;
+ _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len,
+ exchange_state->f_value);
+
+ exchange_state->h_sig_len = _libssh2_ntohu32(exchange_state->s);
+ exchange_state->s += 4;
+ exchange_state->h_sig = exchange_state->s;
+
+ /* Compute the shared secret */
+ _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f,
+ exchange_state->x, p, exchange_state->ctx);
+ exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
+ if (_libssh2_bn_bits(exchange_state->k) % 8) {
+ /* don't need leading 00 */
+ exchange_state->k_value_len--;
+ }
+ exchange_state->k_value =
+ LIBSSH2_ALLOC(session, exchange_state->k_value_len);
+ if (!exchange_state->k_value) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for K");
+ goto clean_exit;
+ }
+ _libssh2_htonu32(exchange_state->k_value,
+ exchange_state->k_value_len - 4);
+ if (_libssh2_bn_bits(exchange_state->k) % 8) {
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ } else {
+ exchange_state->k_value[4] = 0;
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ }
+
+ exchange_state->exchange_hash = (void*)&exchange_hash_ctx;
+ libssh2_sha1_init(&exchange_hash_ctx);
+
+ if (session->local.banner) {
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ strlen((char *) session->local.banner) - 2);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ (char *) session->local.banner,
+ strlen((char *) session->local.banner) - 2);
+ } else {
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ LIBSSH2_SSH_DEFAULT_BANNER,
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+ }
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ strlen((char *) session->remote.banner));
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ session->remote.banner,
+ strlen((char *) session->remote.banner));
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->local.kexinit_len);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ session->local.kexinit,
+ session->local.kexinit_len);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->remote.kexinit_len);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ session->remote.kexinit,
+ session->remote.kexinit_len);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->server_hostkey_len);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ session->server_hostkey,
+ session->server_hostkey_len);
+
+ if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) {
+ /* diffie-hellman-group-exchange hashes additional fields */
+#ifdef LIBSSH2_DH_GEX_NEW
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(exchange_state->h_sig_comp + 4,
+ LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(exchange_state->h_sig_comp + 8,
+ LIBSSH2_DH_GEX_MAXGROUP);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 12);
+#else
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ LIBSSH2_DH_GEX_OPTGROUP);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+#endif
+ }
+
+ if (midhash) {
+ libssh2_sha1_update(exchange_hash_ctx, midhash,
+ midhash_len);
+ }
+
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->e_packet + 1,
+ exchange_state->e_packet_len - 1);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ exchange_state->f_value_len);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->f_value,
+ exchange_state->f_value_len);
+
+ libssh2_sha1_update(exchange_hash_ctx,
+ exchange_state->k_value,
+ exchange_state->k_value_len);
+
+ libssh2_sha1_final(exchange_hash_ctx,
+ exchange_state->h_sig_comp);
+
+ if (session->hostkey->
+ sig_verify(session, exchange_state->h_sig,
+ exchange_state->h_sig_len, exchange_state->h_sig_comp,
+ 20, &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
+ "Unable to verify hostkey signature");
+ goto clean_exit;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending NEWKEYS message");
+ exchange_state->c = SSH_MSG_NEWKEYS;
+
+ exchange_state->state = libssh2_NB_state_sent2;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message");
+ goto clean_exit;
+ }
+
+ exchange_state->state = libssh2_NB_state_sent3;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent3) {
+ rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
+ &exchange_state->tmp,
+ &exchange_state->tmp_len, 0, NULL, 0,
+ &exchange_state->req_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
+ goto clean_exit;
+ }
+ /* The first key exchange has been performed,
+ switch to active crypt/comp/mac mode */
+ session->state |= LIBSSH2_STATE_NEWKEYS;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
+
+ /* This will actually end up being just packet_type(1)
+ for this packet type anyway */
+ LIBSSH2_FREE(session, exchange_state->tmp);
+
+ if (!session->session_id) {
+ session->session_id = LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH);
+ if (!session->session_id) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for SHA digest");
+ goto clean_exit;
+ }
+ memcpy(session->session_id, exchange_state->h_sig_comp,
+ SHA_DIGEST_LENGTH);
+ session->session_id_len = SHA_DIGEST_LENGTH;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "session_id calculated");
+ }
+
+ /* Cleanup any existing cipher */
+ if (session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+
+ /* Calculate IV/Secret/Key for each direction */
+ if (session->local.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv,
+ session->local.crypt->
+ iv_len, "A");
+ if (!iv) {
+ ret = -1;
+ goto clean_exit;
+ }
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret,
+ session->local.crypt->
+ secret_len, "C");
+ if (!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if (session->local.crypt->
+ init(session, session->local.crypt, iv, &free_iv, secret,
+ &free_secret, 1, &session->local.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if (free_iv) {
+ memset(iv, 0, session->local.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if (free_secret) {
+ memset(secret, 0, session->local.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server IV and Key calculated");
+
+ if (session->remote.crypt->dtor) {
+ /* Cleanup any existing cipher */
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+
+ if (session->remote.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv,
+ session->remote.crypt->
+ iv_len, "B");
+ if (!iv) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret,
+ session->remote.crypt->
+ secret_len, "D");
+ if (!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if (session->remote.crypt->
+ init(session, session->remote.crypt, iv, &free_iv, secret,
+ &free_secret, 0, &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if (free_iv) {
+ memset(iv, 0, session->remote.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if (free_secret) {
+ memset(secret, 0, session->remote.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client IV and Key calculated");
+
+ if (session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ if (session->local.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key,
+ session->local.mac->
+ key_len, "E");
+ if (!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->local.mac->init(session, key, &free_key,
+ &session->local.mac_abstract);
+
+ if (free_key) {
+ memset(key, 0, session->local.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server HMAC Key calculated");
+
+ if (session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ if (session->remote.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key,
+ session->remote.mac->
+ key_len, "F");
+ if (!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->remote.mac->init(session, key, &free_key,
+ &session->remote.mac_abstract);
+
+ if (free_key) {
+ memset(key, 0, session->remote.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client HMAC Key calculated");
+
+ /* Initialize compression for each direction */
+
+ /* Cleanup any existing compression */
+ if (session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+
+ if (session->local.comp && session->local.comp->init) {
+ if (session->local.comp->init(session, 1,
+ &session->local.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server compression initialized");
+
+ if (session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+
+ if (session->remote.comp && session->remote.comp->init) {
+ if (session->remote.comp->init(session, 0,
+ &session->remote.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client compression initialized");
+
+ }
+
+ clean_exit:
+ _libssh2_bn_free(exchange_state->x);
+ exchange_state->x = NULL;
+ _libssh2_bn_free(exchange_state->e);
+ exchange_state->e = NULL;
+ _libssh2_bn_free(exchange_state->f);
+ exchange_state->f = NULL;
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+ _libssh2_bn_ctx_free(exchange_state->ctx);
+ exchange_state->ctx = NULL;
+
+ if (exchange_state->e_packet) {
+ LIBSSH2_FREE(session, exchange_state->e_packet);
+ exchange_state->e_packet = NULL;
+ }
+
+ if (exchange_state->s_packet) {
+ LIBSSH2_FREE(session, exchange_state->s_packet);
+ exchange_state->s_packet = NULL;
+ }
+
+ if (exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+
+/*
+ * diffie_hellman_sha256
+ *
+ * Diffie Hellman Key Exchange, Group Agnostic
+ */
+static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
+ _libssh2_bn *g,
+ _libssh2_bn *p,
+ int group_order,
+ unsigned char packet_type_init,
+ unsigned char packet_type_reply,
+ unsigned char *midhash,
+ unsigned long midhash_len,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ int ret = 0;
+ int rc;
+ libssh2_sha256_ctx exchange_hash_ctx;
+
+ if (exchange_state->state == libssh2_NB_state_idle) {
+ /* Setup initial values */
+ exchange_state->e_packet = NULL;
+ exchange_state->s_packet = NULL;
+ exchange_state->k_value = NULL;
+ exchange_state->ctx = _libssh2_bn_ctx_new();
+ exchange_state->x = _libssh2_bn_init(); /* Random from client */
+ exchange_state->e = _libssh2_bn_init(); /* g^x mod p */
+ exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from server) mod p */
+ exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */
+
+ /* Zero the whole thing out */
+ memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t));
+
+ /* Generate x and e */
+ _libssh2_bn_rand(exchange_state->x, group_order * 8 - 1, 0, -1);
+ _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p,
+ exchange_state->ctx);
+
+ /* Send KEX init */
+ /* packet_type(1) + String Length(4) + leading 0(1) */
+ exchange_state->e_packet_len =
+ _libssh2_bn_bytes(exchange_state->e) + 6;
+ if (_libssh2_bn_bits(exchange_state->e) % 8) {
+ /* Leading 00 not needed */
+ exchange_state->e_packet_len--;
+ }
+
+ exchange_state->e_packet =
+ LIBSSH2_ALLOC(session, exchange_state->e_packet_len);
+ if (!exchange_state->e_packet) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory error");
+ goto clean_exit;
+ }
+ exchange_state->e_packet[0] = packet_type_init;
+ _libssh2_htonu32(exchange_state->e_packet + 1,
+ exchange_state->e_packet_len - 5);
+ if (_libssh2_bn_bits(exchange_state->e) % 8) {
+ _libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 5);
+ } else {
+ exchange_state->e_packet[5] = 0;
+ _libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 6);
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending KEX packet %d",
+ (int) packet_type_init);
+ exchange_state->state = libssh2_NB_state_created;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, exchange_state->e_packet,
+ exchange_state->e_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send KEX init message");
+ goto clean_exit;
+ }
+ exchange_state->state = libssh2_NB_state_sent;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent) {
+ if (session->burn_optimistic_kexinit) {
+ /* The first KEX packet to come along will be the guess initially
+ * sent by the server. That guess turned out to be wrong so we
+ * need to silently ignore it */
+ int burn_type;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Waiting for badly guessed KEX packet (to be ignored)");
+ burn_type =
+ _libssh2_packet_burn(session, &exchange_state->burn_state);
+ if (burn_type == LIBSSH2_ERROR_EAGAIN) {
+ return burn_type;
+ } else if (burn_type <= 0) {
+ /* Failed to receive a packet */
+ ret = burn_type;
+ goto clean_exit;
+ }
+ session->burn_optimistic_kexinit = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Burnt packet of type: %02x",
+ (unsigned int) burn_type);
+ }
+
+ exchange_state->state = libssh2_NB_state_sent1;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent1) {
+ /* Wait for KEX reply */
+ rc = _libssh2_packet_require(session, packet_type_reply,
+ &exchange_state->s_packet,
+ &exchange_state->s_packet_len, 0, NULL,
+ 0, &exchange_state->req_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ if (rc) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Timed out waiting for KEX reply");
+ goto clean_exit;
+ }
+
+ /* Parse KEXDH_REPLY */
+ exchange_state->s = exchange_state->s_packet + 1;
+
+ session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s);
+ exchange_state->s += 4;
+
+ if (session->server_hostkey)
+ LIBSSH2_FREE(session, session->server_hostkey);
+
+ session->server_hostkey =
+ LIBSSH2_ALLOC(session, session->server_hostkey_len);
+ if (!session->server_hostkey) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for a copy "
+ "of the host key");
+ goto clean_exit;
+ }
+ memcpy(session->server_hostkey, exchange_state->s,
+ session->server_hostkey_len);
+ exchange_state->s += session->server_hostkey_len;
+
+#if LIBSSH2_MD5
+ {
+ libssh2_md5_ctx fingerprint_ctx;
+
+ if (libssh2_md5_init(&fingerprint_ctx)) {
+ libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_md5_final(fingerprint_ctx,
+ session->server_hostkey_md5);
+ session->server_hostkey_md5_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_md5_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[50], *fprint = fingerprint;
+ int i;
+ for(i = 0; i < 16; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's MD5 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+#endif /* ! LIBSSH2_MD5 */
+
+ {
+ libssh2_sha1_ctx fingerprint_ctx;
+
+ if (libssh2_sha1_init(&fingerprint_ctx)) {
+ libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha1_final(fingerprint_ctx,
+ session->server_hostkey_sha1);
+ session->server_hostkey_sha1_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha1_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[64], *fprint = fingerprint;
+ int i;
+
+ for(i = 0; i < 20; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA1 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+
+ if (session->hostkey->init(session, session->server_hostkey,
+ session->server_hostkey_len,
+ &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to initialize hostkey importer");
+ goto clean_exit;
+ }
+
+ exchange_state->f_value_len = _libssh2_ntohu32(exchange_state->s);
+ exchange_state->s += 4;
+ exchange_state->f_value = exchange_state->s;
+ exchange_state->s += exchange_state->f_value_len;
+ _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len,
+ exchange_state->f_value);
+
+ exchange_state->h_sig_len = _libssh2_ntohu32(exchange_state->s);
+ exchange_state->s += 4;
+ exchange_state->h_sig = exchange_state->s;
+
+ /* Compute the shared secret */
+ _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f,
+ exchange_state->x, p, exchange_state->ctx);
+ exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
+ if (_libssh2_bn_bits(exchange_state->k) % 8) {
+ /* don't need leading 00 */
+ exchange_state->k_value_len--;
+ }
+ exchange_state->k_value =
+ LIBSSH2_ALLOC(session, exchange_state->k_value_len);
+ if (!exchange_state->k_value) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for K");
+ goto clean_exit;
+ }
+ _libssh2_htonu32(exchange_state->k_value,
+ exchange_state->k_value_len - 4);
+ if (_libssh2_bn_bits(exchange_state->k) % 8) {
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ } else {
+ exchange_state->k_value[4] = 0;
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ }
+
+ exchange_state->exchange_hash = (void*)&exchange_hash_ctx;
+ libssh2_sha256_init(&exchange_hash_ctx);
+
+ if (session->local.banner) {
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ strlen((char *) session->local.banner) - 2);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ (char *) session->local.banner,
+ strlen((char *) session->local.banner) - 2);
+ } else {
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ LIBSSH2_SSH_DEFAULT_BANNER,
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+ }
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ strlen((char *) session->remote.banner));
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ session->remote.banner,
+ strlen((char *) session->remote.banner));
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->local.kexinit_len);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ session->local.kexinit,
+ session->local.kexinit_len);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->remote.kexinit_len);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ session->remote.kexinit,
+ session->remote.kexinit_len);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->server_hostkey_len);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ session->server_hostkey,
+ session->server_hostkey_len);
+
+ if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) {
+ /* diffie-hellman-group-exchange hashes additional fields */
+#ifdef LIBSSH2_DH_GEX_NEW
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(exchange_state->h_sig_comp + 4,
+ LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(exchange_state->h_sig_comp + 8,
+ LIBSSH2_DH_GEX_MAXGROUP);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 12);
+#else
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ LIBSSH2_DH_GEX_OPTGROUP);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+#endif
+ }
+
+ if (midhash) {
+ libssh2_sha256_update(exchange_hash_ctx, midhash,
+ midhash_len);
+ }
+
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->e_packet + 1,
+ exchange_state->e_packet_len - 1);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ exchange_state->f_value_len);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->f_value,
+ exchange_state->f_value_len);
+
+ libssh2_sha256_update(exchange_hash_ctx,
+ exchange_state->k_value,
+ exchange_state->k_value_len);
+
+ libssh2_sha256_final(exchange_hash_ctx,
+ exchange_state->h_sig_comp);
+
+ if (session->hostkey->
+ sig_verify(session, exchange_state->h_sig,
+ exchange_state->h_sig_len, exchange_state->h_sig_comp,
+ SHA256_DIGEST_LENGTH, &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
+ "Unable to verify hostkey signature");
+ goto clean_exit;
+ }
+
+
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending NEWKEYS message");
+ exchange_state->c = SSH_MSG_NEWKEYS;
+
+ exchange_state->state = libssh2_NB_state_sent2;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message");
+ goto clean_exit;
+ }
+
+ exchange_state->state = libssh2_NB_state_sent3;
+ }
+
+ if (exchange_state->state == libssh2_NB_state_sent3) {
+ rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
+ &exchange_state->tmp,
+ &exchange_state->tmp_len, 0, NULL, 0,
+ &exchange_state->req_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
+ goto clean_exit;
+ }
+ /* The first key exchange has been performed,
+ switch to active crypt/comp/mac mode */
+ session->state |= LIBSSH2_STATE_NEWKEYS;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
+
+ /* This will actually end up being just packet_type(1)
+ for this packet type anyway */
+ LIBSSH2_FREE(session, exchange_state->tmp);
+
+ if (!session->session_id) {
+ session->session_id = LIBSSH2_ALLOC(session, SHA256_DIGEST_LENGTH);
+ if (!session->session_id) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for SHA digest");
+ goto clean_exit;
+ }
+ memcpy(session->session_id, exchange_state->h_sig_comp,
+ SHA256_DIGEST_LENGTH);
+ session->session_id_len = SHA256_DIGEST_LENGTH;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "session_id calculated");
+ }
+
+ /* Cleanup any existing cipher */
+ if (session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+
+ /* Calculate IV/Secret/Key for each direction */
+ if (session->local.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(iv,
+ session->local.crypt->
+ iv_len, "A");
+ if (!iv) {
+ ret = -1;
+ goto clean_exit;
+ }
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(secret,
+ session->local.crypt->
+ secret_len, "C");
+ if (!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if (session->local.crypt->
+ init(session, session->local.crypt, iv, &free_iv, secret,
+ &free_secret, 1, &session->local.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if (free_iv) {
+ memset(iv, 0, session->local.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if (free_secret) {
+ memset(secret, 0, session->local.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server IV and Key calculated");
+
+ if (session->remote.crypt->dtor) {
+ /* Cleanup any existing cipher */
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+
+ if (session->remote.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(iv,
+ session->remote.crypt->
+ iv_len, "B");
+ if (!iv) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(secret,
+ session->remote.crypt->
+ secret_len, "D");
+ if (!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if (session->remote.crypt->
+ init(session, session->remote.crypt, iv, &free_iv, secret,
+ &free_secret, 0, &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if (free_iv) {
+ memset(iv, 0, session->remote.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if (free_secret) {
+ memset(secret, 0, session->remote.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client IV and Key calculated");
+
+ if (session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ if (session->local.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(key,
+ session->local.mac->
+ key_len, "E");
+ if (!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->local.mac->init(session, key, &free_key,
+ &session->local.mac_abstract);
+
+ if (free_key) {
+ memset(key, 0, session->local.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server HMAC Key calculated");
+
+ if (session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ if (session->remote.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(key,
+ session->remote.mac->
+ key_len, "F");
+ if (!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->remote.mac->init(session, key, &free_key,
+ &session->remote.mac_abstract);
+
+ if (free_key) {
+ memset(key, 0, session->remote.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client HMAC Key calculated");
+
+ /* Initialize compression for each direction */
+
+ /* Cleanup any existing compression */
+ if (session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+
+ if (session->local.comp && session->local.comp->init) {
+ if (session->local.comp->init(session, 1,
+ &session->local.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server compression initialized");
+
+ if (session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+
+ if (session->remote.comp && session->remote.comp->init) {
+ if (session->remote.comp->init(session, 0,
+ &session->remote.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client compression initialized");
+
+ }
+
+ clean_exit:
+ _libssh2_bn_free(exchange_state->x);
+ exchange_state->x = NULL;
+ _libssh2_bn_free(exchange_state->e);
+ exchange_state->e = NULL;
+ _libssh2_bn_free(exchange_state->f);
+ exchange_state->f = NULL;
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+ _libssh2_bn_ctx_free(exchange_state->ctx);
+ exchange_state->ctx = NULL;
+
+ if (exchange_state->e_packet) {
+ LIBSSH2_FREE(session, exchange_state->e_packet);
+ exchange_state->e_packet = NULL;
+ }
+
+ if (exchange_state->s_packet) {
+ LIBSSH2_FREE(session, exchange_state->s_packet);
+ exchange_state->s_packet = NULL;
+ }
+
+ if (exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group1_sha1_key_exchange
+ * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1
+ */
+static int
+kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+{
+ static const unsigned char p_value[128] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ int ret;
+
+ if (key_state->state == libssh2_NB_state_idle) {
+ /* g == 2 */
+ key_state->p = _libssh2_bn_init_from_bin(); /* SSH2 defined value (p_value) */
+ key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
+
+ /* Initialize P and G */
+ _libssh2_bn_set_word(key_state->g, 2);
+ _libssh2_bn_from_bin(key_state->p, 128, p_value);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group1 Key Exchange");
+
+ key_state->state = libssh2_NB_state_created;
+ }
+ ret = diffie_hellman_sha1(session, key_state->g, key_state->p, 128,
+ SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY,
+ NULL, 0, &key_state->exchange_state);
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+ key_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group14_sha1_key_exchange
+ * Diffie-Hellman Group14 Key Exchange using SHA1
+ */
+static int
+kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+{
+ static const unsigned char p_value[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ int ret;
+
+ if (key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init_from_bin(); /* SSH2 defined value (p_value) */
+ key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
+
+ /* g == 2 */
+ /* Initialize P and G */
+ _libssh2_bn_set_word(key_state->g, 2);
+ _libssh2_bn_from_bin(key_state->p, 256, p_value);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group14 Key Exchange");
+
+ key_state->state = libssh2_NB_state_created;
+ }
+ ret = diffie_hellman_sha1(session, key_state->g, key_state->p,
+ 256, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY,
+ NULL, 0, &key_state->exchange_state);
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group_exchange_sha1_key_exchange
+ * Diffie-Hellman Group Exchange Key Exchange using SHA1
+ * Negotiates random(ish) group for secret derivation
+ */
+static int
+kex_method_diffie_hellman_group_exchange_sha1_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ unsigned long p_len, g_len;
+ int ret = 0;
+ int rc;
+
+ if (key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init_from_bin();
+ key_state->g = _libssh2_bn_init_from_bin();
+ /* Ask for a P and G pair */
+#ifdef LIBSSH2_DH_GEX_NEW
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP);
+ key_state->request_len = 13;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange (New Method)");
+#else
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP);
+ key_state->request_len = 5;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange (Old Method)");
+#endif
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if (key_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, key_state->request,
+ key_state->request_len, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send Group Exchange Request");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if (key_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP,
+ &key_state->data, &key_state->data_len,
+ 0, NULL, 0, &key_state->req_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc,
+ "Timeout waiting for GEX_GROUP reply");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if (key_state->state == libssh2_NB_state_sent1) {
+ unsigned char *s = key_state->data + 1;
+ p_len = _libssh2_ntohu32(s);
+ s += 4;
+ _libssh2_bn_from_bin(key_state->p, p_len, s);
+ s += p_len;
+
+ g_len = _libssh2_ntohu32(s);
+ s += 4;
+ _libssh2_bn_from_bin(key_state->g, g_len, s);
+
+ ret = diffie_hellman_sha1(session, key_state->g, key_state->p, p_len,
+ SSH_MSG_KEX_DH_GEX_INIT,
+ SSH_MSG_KEX_DH_GEX_REPLY,
+ key_state->data + 1,
+ key_state->data_len - 1,
+ &key_state->exchange_state);
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ LIBSSH2_FREE(session, key_state->data);
+ }
+
+ dh_gex_clean_exit:
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group_exchange_sha256_key_exchange
+ * Diffie-Hellman Group Exchange Key Exchange using SHA256
+ * Negotiates random(ish) group for secret derivation
+ */
+static int
+kex_method_diffie_hellman_group_exchange_sha256_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ unsigned long p_len, g_len;
+ int ret = 0;
+ int rc;
+
+ if (key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init();
+ key_state->g = _libssh2_bn_init();
+ /* Ask for a P and G pair */
+#ifdef LIBSSH2_DH_GEX_NEW
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP);
+ key_state->request_len = 13;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange (New Method SHA256)");
+#else
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP);
+ key_state->request_len = 5;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange (Old Method SHA256)");
+#endif
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if (key_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, key_state->request,
+ key_state->request_len, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send Group Exchange Request SHA256");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if (key_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP,
+ &key_state->data, &key_state->data_len,
+ 0, NULL, 0, &key_state->req_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ ret = _libssh2_error(session, rc,
+ "Timeout waiting for GEX_GROUP reply SHA256");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if (key_state->state == libssh2_NB_state_sent1) {
+ unsigned char *s = key_state->data + 1;
+ p_len = _libssh2_ntohu32(s);
+ s += 4;
+ _libssh2_bn_from_bin(key_state->p, p_len, s);
+ s += p_len;
+
+ g_len = _libssh2_ntohu32(s);
+ s += 4;
+ _libssh2_bn_from_bin(key_state->g, g_len, s);
+
+ ret = diffie_hellman_sha256(session, key_state->g, key_state->p, p_len,
+ SSH_MSG_KEX_DH_GEX_INIT,
+ SSH_MSG_KEX_DH_GEX_REPLY,
+ key_state->data + 1,
+ key_state->data_len - 1,
+ &key_state->exchange_state);
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ LIBSSH2_FREE(session, key_state->data);
+ }
+
+ dh_gex_clean_exit:
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+
+ return ret;
+}
+
+
+#define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY 0x0001
+#define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY 0x0002
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group1_sha1 = {
+ "diffie-hellman-group1-sha1",
+ kex_method_diffie_hellman_group1_sha1_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group14_sha1 = {
+ "diffie-hellman-group14-sha1",
+ kex_method_diffie_hellman_group14_sha1_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD
+kex_method_diffie_helman_group_exchange_sha1 = {
+ "diffie-hellman-group-exchange-sha1",
+ kex_method_diffie_hellman_group_exchange_sha1_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD
+kex_method_diffie_helman_group_exchange_sha256 = {
+ "diffie-hellman-group-exchange-sha256",
+ kex_method_diffie_hellman_group_exchange_sha256_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
+ &kex_method_diffie_helman_group_exchange_sha256,
+ &kex_method_diffie_helman_group_exchange_sha1,
+ &kex_method_diffie_helman_group14_sha1,
+ &kex_method_diffie_helman_group1_sha1,
+ NULL
+};
+
+typedef struct _LIBSSH2_COMMON_METHOD
+{
+ const char *name;
+} LIBSSH2_COMMON_METHOD;
+
+/* kex_method_strlen
+ * Calculate the length of a particular method list's resulting string
+ * Includes SUM(strlen() of each individual method plus 1 (for coma)) - 1 (because the last coma isn't used)
+ * Another sign of bad coding practices gone mad. Pretend you don't see this.
+ */
+static size_t
+kex_method_strlen(LIBSSH2_COMMON_METHOD ** method)
+{
+ size_t len = 0;
+
+ if (!method || !*method) {
+ return 0;
+ }
+
+ while (*method && (*method)->name) {
+ len += strlen((*method)->name) + 1;
+ method++;
+ }
+
+ return len - 1;
+}
+
+
+
+/* kex_method_list
+ * Generate formatted preference list in buf
+ */
+static size_t
+kex_method_list(unsigned char *buf, size_t list_strlen,
+ LIBSSH2_COMMON_METHOD ** method)
+{
+ _libssh2_htonu32(buf, list_strlen);
+ buf += 4;
+
+ if (!method || !*method) {
+ return 4;
+ }
+
+ while (*method && (*method)->name) {
+ int mlen = strlen((*method)->name);
+ memcpy(buf, (*method)->name, mlen);
+ buf += mlen;
+ *(buf++) = ',';
+ method++;
+ }
+
+ return list_strlen + 4;
+}
+
+
+
+#define LIBSSH2_METHOD_PREFS_LEN(prefvar, defaultvar) \
+ ((prefvar) ? strlen(prefvar) : \
+ kex_method_strlen((LIBSSH2_COMMON_METHOD**)(defaultvar)))
+
+#define LIBSSH2_METHOD_PREFS_STR(buf, prefvarlen, prefvar, defaultvar) \
+ if (prefvar) { \
+ _libssh2_htonu32((buf), (prefvarlen)); \
+ buf += 4; \
+ memcpy((buf), (prefvar), (prefvarlen)); \
+ buf += (prefvarlen); \
+ } else { \
+ buf += kex_method_list((buf), (prefvarlen), \
+ (LIBSSH2_COMMON_METHOD**)(defaultvar)); \
+ }
+
+/* kexinit
+ * Send SSH_MSG_KEXINIT packet
+ */
+static int kexinit(LIBSSH2_SESSION * session)
+{
+ /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) +
+ reserved(4) + length longs(40) */
+ size_t data_len = 62;
+ size_t kex_len, hostkey_len = 0;
+ size_t crypt_cs_len, crypt_sc_len;
+ size_t comp_cs_len, comp_sc_len;
+ size_t mac_cs_len, mac_sc_len;
+ size_t lang_cs_len, lang_sc_len;
+ unsigned char *data, *s;
+ int rc;
+
+ if (session->kexinit_state == libssh2_NB_state_idle) {
+ kex_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods);
+ hostkey_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs,
+ libssh2_hostkey_methods());
+ crypt_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs,
+ libssh2_crypt_methods());
+ crypt_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs,
+ libssh2_crypt_methods());
+ mac_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs,
+ _libssh2_mac_methods());
+ mac_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs,
+ _libssh2_mac_methods());
+ comp_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs,
+ _libssh2_comp_methods(session));
+ comp_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs,
+ _libssh2_comp_methods(session));
+ lang_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL);
+ lang_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL);
+
+ data_len += kex_len + hostkey_len + crypt_cs_len + crypt_sc_len +
+ comp_cs_len + comp_sc_len + mac_cs_len + mac_sc_len +
+ lang_cs_len + lang_sc_len;
+
+ s = data = LIBSSH2_ALLOC(session, data_len);
+ if (!data) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory");
+ }
+
+ *(s++) = SSH_MSG_KEXINIT;
+
+ _libssh2_random(s, 16);
+ s += 16;
+
+ /* Ennumerating through these lists twice is probably (certainly?)
+ inefficient from a CPU standpoint, but it saves multiple
+ malloc/realloc calls */
+ LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs,
+ libssh2_kex_methods);
+ LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs,
+ libssh2_hostkey_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs,
+ libssh2_crypt_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs,
+ libssh2_crypt_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs,
+ _libssh2_mac_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs,
+ _libssh2_mac_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs,
+ _libssh2_comp_methods(session));
+ LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs,
+ _libssh2_comp_methods(session));
+ LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs,
+ NULL);
+ LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs,
+ NULL);
+
+ /* No optimistic KEX packet follows */
+ /* Deal with optimistic packets
+ * session->flags |= KEXINIT_OPTIMISTIC
+ * session->flags |= KEXINIT_METHODSMATCH
+ */
+ *(s++) = 0;
+
+ /* Reserved == 0 */
+ _libssh2_htonu32(s, 0);
+
+#ifdef LIBSSH2DEBUG
+ {
+ /* Funnily enough, they'll all "appear" to be '\0' terminated */
+ unsigned char *p = data + 21; /* type(1) + cookie(16) + len(4) */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent KEX: %s", p);
+ p += kex_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent HOSTKEY: %s", p);
+ p += hostkey_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent CRYPT_CS: %s", p);
+ p += crypt_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent CRYPT_SC: %s", p);
+ p += crypt_sc_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent MAC_CS: %s", p);
+ p += mac_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent MAC_SC: %s", p);
+ p += mac_sc_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent COMP_CS: %s", p);
+ p += comp_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent COMP_SC: %s", p);
+ p += comp_sc_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent LANG_CS: %s", p);
+ p += lang_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent LANG_SC: %s", p);
+ p += lang_sc_len + 4;
+ }
+#endif /* LIBSSH2DEBUG */
+
+ session->kexinit_state = libssh2_NB_state_created;
+ } else {
+ data = session->kexinit_data;
+ data_len = session->kexinit_data_len;
+ /* zap the variables to ensure there is NOT a double free later */
+ session->kexinit_data = NULL;
+ session->kexinit_data_len = 0;
+ }
+
+ rc = _libssh2_transport_send(session, data, data_len, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ session->kexinit_data = data;
+ session->kexinit_data_len = data_len;
+ return rc;
+ }
+ else if (rc) {
+ LIBSSH2_FREE(session, data);
+ session->kexinit_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send KEXINIT packet to remote host");
+
+ }
+
+ if (session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+
+ session->local.kexinit = data;
+ session->local.kexinit_len = data_len;
+
+ session->kexinit_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/* kex_agree_instr
+ * Kex specific variant of strstr()
+ * Needle must be preceed by BOL or ',', and followed by ',' or EOL
+ */
+static unsigned char *
+kex_agree_instr(unsigned char *haystack, unsigned long haystack_len,
+ const unsigned char *needle, unsigned long needle_len)
+{
+ unsigned char *s;
+
+ /* Haystack too short to bother trying */
+ if (haystack_len < needle_len) {
+ return NULL;
+ }
+
+ /* Needle at start of haystack */
+ if ((strncmp((char *) haystack, (char *) needle, needle_len) == 0) &&
+ (needle_len == haystack_len || haystack[needle_len] == ',')) {
+ return haystack;
+ }
+
+ s = haystack;
+ /* Search until we run out of comas or we run out of haystack,
+ whichever comes first */
+ while ((s = (unsigned char *) strchr((char *) s, ','))
+ && ((haystack_len - (s - haystack)) > needle_len)) {
+ s++;
+ /* Needle at X position */
+ if ((strncmp((char *) s, (char *) needle, needle_len) == 0) &&
+ (((s - haystack) + needle_len) == haystack_len
+ || s[needle_len] == ',')) {
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/* kex_get_method_by_name
+ */
+static const LIBSSH2_COMMON_METHOD *
+kex_get_method_by_name(const char *name, size_t name_len,
+ const LIBSSH2_COMMON_METHOD ** methodlist)
+{
+ while (*methodlist) {
+ if ((strlen((*methodlist)->name) == name_len) &&
+ (strncmp((*methodlist)->name, name, name_len) == 0)) {
+ return *methodlist;
+ }
+ methodlist++;
+ }
+ return NULL;
+}
+
+
+
+/* kex_agree_hostkey
+ * Agree on a Hostkey which works with this kex
+ */
+static int kex_agree_hostkey(LIBSSH2_SESSION * session,
+ unsigned long kex_flags,
+ unsigned char *hostkey, unsigned long hostkey_len)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkeyp = libssh2_hostkey_methods();
+ unsigned char *s;
+
+ if (session->hostkey_prefs) {
+ s = (unsigned char *) session->hostkey_prefs;
+
+ while (s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+ if (kex_agree_instr(hostkey, hostkey_len, s, method_len)) {
+ const LIBSSH2_HOSTKEY_METHOD *method =
+ (const LIBSSH2_HOSTKEY_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ hostkeyp);
+
+ if (!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ /* So far so good, but does it suit our purposes? (Encrypting
+ vs Signing) */
+ if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) ==
+ 0) || (method->encrypt)) {
+ /* Either this hostkey can do encryption or this kex just
+ doesn't require it */
+ if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY)
+ == 0) || (method->sig_verify)) {
+ /* Either this hostkey can do signing or this kex just
+ doesn't require it */
+ session->hostkey = method;
+ return 0;
+ }
+ }
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while (hostkeyp && (*hostkeyp) && (*hostkeyp)->name) {
+ s = kex_agree_instr(hostkey, hostkey_len,
+ (unsigned char *) (*hostkeyp)->name,
+ strlen((*hostkeyp)->name));
+ if (s) {
+ /* So far so good, but does it suit our purposes? (Encrypting vs
+ Signing) */
+ if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) ||
+ ((*hostkeyp)->encrypt)) {
+ /* Either this hostkey can do encryption or this kex just
+ doesn't require it */
+ if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) ==
+ 0) || ((*hostkeyp)->sig_verify)) {
+ /* Either this hostkey can do signing or this kex just
+ doesn't require it */
+ session->hostkey = *hostkeyp;
+ return 0;
+ }
+ }
+ }
+ hostkeyp++;
+ }
+
+ return -1;
+}
+
+
+
+/* kex_agree_kex_hostkey
+ * Agree on a Key Exchange method and a hostkey encoding type
+ */
+static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
+ unsigned long kex_len, unsigned char *hostkey,
+ unsigned long hostkey_len)
+{
+ const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods;
+ unsigned char *s;
+
+ if (session->kex_prefs) {
+ s = (unsigned char *) session->kex_prefs;
+
+ while (s && *s) {
+ unsigned char *q, *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+ if ((q = kex_agree_instr(kex, kex_len, s, method_len))) {
+ const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ kexp);
+
+ if (!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ /* We've agreed on a key exchange method,
+ * Can we agree on a hostkey that works with this kex?
+ */
+ if (kex_agree_hostkey(session, method->flags, hostkey,
+ hostkey_len) == 0) {
+ session->kex = method;
+ if (session->burn_optimistic_kexinit && (kex == q)) {
+ /* Server sent an optimistic packet,
+ * and client agrees with preference
+ * cancel burning the first KEX_INIT packet that comes in */
+ session->burn_optimistic_kexinit = 0;
+ }
+ return 0;
+ }
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while (*kexp && (*kexp)->name) {
+ s = kex_agree_instr(kex, kex_len,
+ (unsigned char *) (*kexp)->name,
+ strlen((*kexp)->name));
+ if (s) {
+ /* We've agreed on a key exchange method,
+ * Can we agree on a hostkey that works with this kex?
+ */
+ if (kex_agree_hostkey(session, (*kexp)->flags, hostkey,
+ hostkey_len) == 0) {
+ session->kex = *kexp;
+ if (session->burn_optimistic_kexinit && (kex == s)) {
+ /* Server sent an optimistic packet,
+ * and client agrees with preference
+ * cancel burning the first KEX_INIT packet that comes in */
+ session->burn_optimistic_kexinit = 0;
+ }
+ return 0;
+ }
+ }
+ kexp++;
+ }
+ return -1;
+}
+
+
+
+/* kex_agree_crypt
+ * Agree on a cipher algo
+ */
+static int kex_agree_crypt(LIBSSH2_SESSION * session,
+ libssh2_endpoint_data *endpoint,
+ unsigned char *crypt,
+ unsigned long crypt_len)
+{
+ const LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods();
+ unsigned char *s;
+
+ (void) session;
+
+ if (endpoint->crypt_prefs) {
+ s = (unsigned char *) endpoint->crypt_prefs;
+
+ while (s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+
+ if (kex_agree_instr(crypt, crypt_len, s, method_len)) {
+ const LIBSSH2_CRYPT_METHOD *method =
+ (const LIBSSH2_CRYPT_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ cryptp);
+
+ if (!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ endpoint->crypt = method;
+ return 0;
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while (*cryptp && (*cryptp)->name) {
+ s = kex_agree_instr(crypt, crypt_len,
+ (unsigned char *) (*cryptp)->name,
+ strlen((*cryptp)->name));
+ if (s) {
+ endpoint->crypt = *cryptp;
+ return 0;
+ }
+ cryptp++;
+ }
+
+ return -1;
+}
+
+
+
+/* kex_agree_mac
+ * Agree on a message authentication hash
+ */
+static int kex_agree_mac(LIBSSH2_SESSION * session,
+ libssh2_endpoint_data * endpoint, unsigned char *mac,
+ unsigned long mac_len)
+{
+ const LIBSSH2_MAC_METHOD **macp = _libssh2_mac_methods();
+ unsigned char *s;
+ (void) session;
+
+ if (endpoint->mac_prefs) {
+ s = (unsigned char *) endpoint->mac_prefs;
+
+ while (s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+
+ if (kex_agree_instr(mac, mac_len, s, method_len)) {
+ const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ macp);
+
+ if (!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ endpoint->mac = method;
+ return 0;
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while (*macp && (*macp)->name) {
+ s = kex_agree_instr(mac, mac_len, (unsigned char *) (*macp)->name,
+ strlen((*macp)->name));
+ if (s) {
+ endpoint->mac = *macp;
+ return 0;
+ }
+ macp++;
+ }
+
+ return -1;
+}
+
+
+
+/* kex_agree_comp
+ * Agree on a compression scheme
+ */
+static int kex_agree_comp(LIBSSH2_SESSION *session,
+ libssh2_endpoint_data *endpoint, unsigned char *comp,
+ unsigned long comp_len)
+{
+ const LIBSSH2_COMP_METHOD **compp = _libssh2_comp_methods(session);
+ unsigned char *s;
+ (void) session;
+
+ if (endpoint->comp_prefs) {
+ s = (unsigned char *) endpoint->comp_prefs;
+
+ while (s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+
+ if (kex_agree_instr(comp, comp_len, s, method_len)) {
+ const LIBSSH2_COMP_METHOD *method =
+ (const LIBSSH2_COMP_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ compp);
+
+ if (!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ endpoint->comp = method;
+ return 0;
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while (*compp && (*compp)->name) {
+ s = kex_agree_instr(comp, comp_len, (unsigned char *) (*compp)->name,
+ strlen((*compp)->name));
+ if (s) {
+ endpoint->comp = *compp;
+ return 0;
+ }
+ compp++;
+ }
+
+ return -1;
+}
+
+
+
+/* TODO: When in server mode we need to turn this logic on its head
+ * The Client gets to make the final call on "agreed methods"
+ */
+
+/*
+ * kex_string_pair() extracts a string from the packet and makes sure it fits
+ * within the given packet.
+ */
+static int kex_string_pair(unsigned char **sp, /* parsing position */
+ unsigned char *data, /* start pointer to packet */
+ size_t data_len, /* size of total packet */
+ size_t *lenp, /* length of the string */
+ unsigned char **strp) /* pointer to string start */
+{
+ unsigned char *s = *sp;
+ *lenp = _libssh2_ntohu32(s);
+
+ /* the length of the string must fit within the current pointer and the
+ end of the packet */
+ if (*lenp > (data_len - (s - data) -4))
+ return 1;
+ *strp = s + 4;
+ s += 4 + *lenp;
+
+ *sp = s;
+ return 0;
+}
+
+/* kex_agree_methods
+ * Decide which specific method to use of the methods offered by each party
+ */
+static int kex_agree_methods(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned data_len)
+{
+ unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc,
+ *mac_cs, *mac_sc;
+ size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len;
+ size_t comp_sc_len, mac_cs_len, mac_sc_len;
+ unsigned char *s = data;
+
+ /* Skip packet_type, we know it already */
+ s++;
+
+ /* Skip cookie, don't worry, it's preserved in the kexinit field */
+ s += 16;
+
+ /* Locate each string */
+ if(kex_string_pair(&s, data, data_len, &kex_len, &kex))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &hostkey_len, &hostkey))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &crypt_cs_len, &crypt_cs))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &crypt_sc_len, &crypt_sc))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &mac_cs_len, &mac_cs))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &mac_sc_len, &mac_sc))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &comp_cs_len, &comp_cs))
+ return -1;
+ if(kex_string_pair(&s, data, data_len, &comp_sc_len, &comp_sc))
+ return -1;
+
+ /* If the server sent an optimistic packet, assume that it guessed wrong.
+ * If the guess is determined to be right (by kex_agree_kex_hostkey)
+ * This flag will be reset to zero so that it's not ignored */
+ session->burn_optimistic_kexinit = *(s++);
+ /* Next uint32 in packet is all zeros (reserved) */
+
+ if (data_len < (unsigned) (s - data))
+ return -1; /* short packet */
+
+ if (kex_agree_kex_hostkey(session, kex, kex_len, hostkey, hostkey_len)) {
+ return -1;
+ }
+
+ if (kex_agree_crypt(session, &session->local, crypt_cs, crypt_cs_len)
+ || kex_agree_crypt(session, &session->remote, crypt_sc, crypt_sc_len)) {
+ return -1;
+ }
+
+ if (kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) ||
+ kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) {
+ return -1;
+ }
+
+ if (kex_agree_comp(session, &session->local, comp_cs, comp_cs_len) ||
+ kex_agree_comp(session, &session->remote, comp_sc, comp_sc_len)) {
+ return -1;
+ }
+
+#if 0
+ if (libssh2_kex_agree_lang(session, &session->local, lang_cs, lang_cs_len)
+ || libssh2_kex_agree_lang(session, &session->remote, lang_sc,
+ lang_sc_len)) {
+ return -1;
+ }
+#endif
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on KEX method: %s",
+ session->kex->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on HOSTKEY method: %s",
+ session->hostkey->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on CRYPT_CS method: %s",
+ session->local.crypt->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on CRYPT_SC method: %s",
+ session->remote.crypt->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on MAC_CS method: %s",
+ session->local.mac->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on MAC_SC method: %s",
+ session->remote.mac->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on COMP_CS method: %s",
+ session->local.comp->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on COMP_SC method: %s",
+ session->remote.comp->name);
+
+ return 0;
+}
+
+
+
+/* _libssh2_kex_exchange
+ * Exchange keys
+ * Returns 0 on success, non-zero on failure
+ *
+ * Returns some errors without _libssh2_error()
+ */
+int
+_libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
+ key_exchange_state_t * key_state)
+{
+ int rc = 0;
+ int retcode;
+
+ session->state |= LIBSSH2_STATE_KEX_ACTIVE;
+
+ if (key_state->state == libssh2_NB_state_idle) {
+ /* Prevent loop in packet_add() */
+ session->state |= LIBSSH2_STATE_EXCHANGING_KEYS;
+
+ if (reexchange) {
+ session->kex = NULL;
+
+ if (session->hostkey && session->hostkey->dtor) {
+ session->hostkey->dtor(session,
+ &session->server_hostkey_abstract);
+ }
+ session->hostkey = NULL;
+ }
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if (!session->kex || !session->hostkey) {
+ if (key_state->state == libssh2_NB_state_created) {
+ /* Preserve in case of failure */
+ key_state->oldlocal = session->local.kexinit;
+ key_state->oldlocal_len = session->local.kexinit_len;
+
+ session->local.kexinit = NULL;
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if (key_state->state == libssh2_NB_state_sent) {
+ retcode = kexinit(session);
+ if (retcode == LIBSSH2_ERROR_EAGAIN) {
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ return retcode;
+ } else if (retcode) {
+ session->local.kexinit = key_state->oldlocal;
+ session->local.kexinit_len = key_state->oldlocal_len;
+ key_state->state = libssh2_NB_state_idle;
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+ return -1;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if (key_state->state == libssh2_NB_state_sent1) {
+ retcode =
+ _libssh2_packet_require(session, SSH_MSG_KEXINIT,
+ &key_state->data,
+ &key_state->data_len, 0, NULL, 0,
+ &key_state->req_state);
+ if (retcode == LIBSSH2_ERROR_EAGAIN) {
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ return retcode;
+ }
+ else if (retcode) {
+ if (session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+ session->local.kexinit = key_state->oldlocal;
+ session->local.kexinit_len = key_state->oldlocal_len;
+ key_state->state = libssh2_NB_state_idle;
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+ return -1;
+ }
+
+ if (session->remote.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ }
+ session->remote.kexinit = key_state->data;
+ session->remote.kexinit_len = key_state->data_len;
+
+ if (kex_agree_methods(session, key_state->data,
+ key_state->data_len))
+ rc = LIBSSH2_ERROR_KEX_FAILURE;
+
+ key_state->state = libssh2_NB_state_sent2;
+ }
+ } else {
+ key_state->state = libssh2_NB_state_sent2;
+ }
+
+ if (rc == 0 && session->kex) {
+ if (key_state->state == libssh2_NB_state_sent2) {
+ retcode = session->kex->exchange_keys(session,
+ &key_state->key_state_low);
+ if (retcode == LIBSSH2_ERROR_EAGAIN) {
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ return retcode;
+ } else if (retcode) {
+ rc = _libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE,
+ "Unrecoverable error exchanging keys");
+ }
+ }
+ }
+
+ /* Done with kexinit buffers */
+ if (session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ session->local.kexinit = NULL;
+ }
+ if (session->remote.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ session->remote.kexinit = NULL;
+ }
+
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+
+ key_state->state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+
+
+/* libssh2_session_method_pref
+ * Set preferred method
+ */
+LIBSSH2_API int
+libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
+ const char *prefs)
+{
+ char **prefvar, *s, *newprefs;
+ int prefs_len = strlen(prefs);
+ const LIBSSH2_COMMON_METHOD **mlist;
+
+ switch (method_type) {
+ case LIBSSH2_METHOD_KEX:
+ prefvar = &session->kex_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods;
+ break;
+
+ case LIBSSH2_METHOD_HOSTKEY:
+ prefvar = &session->hostkey_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods();
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_CS:
+ prefvar = &session->local.crypt_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_SC:
+ prefvar = &session->remote.crypt_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+ break;
+
+ case LIBSSH2_METHOD_MAC_CS:
+ prefvar = &session->local.mac_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+ break;
+
+ case LIBSSH2_METHOD_MAC_SC:
+ prefvar = &session->remote.mac_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+ break;
+
+ case LIBSSH2_METHOD_COMP_CS:
+ prefvar = &session->local.comp_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **)
+ _libssh2_comp_methods(session);
+ break;
+
+ case LIBSSH2_METHOD_COMP_SC:
+ prefvar = &session->remote.comp_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **)
+ _libssh2_comp_methods(session);
+ break;
+
+ case LIBSSH2_METHOD_LANG_CS:
+ prefvar = &session->local.lang_prefs;
+ mlist = NULL;
+ break;
+
+ case LIBSSH2_METHOD_LANG_SC:
+ prefvar = &session->remote.lang_prefs;
+ mlist = NULL;
+ break;
+
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "Invalid parameter specified for method_type");
+ }
+
+ s = newprefs = LIBSSH2_ALLOC(session, prefs_len + 1);
+ if (!newprefs) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Error allocated space for method preferences");
+ }
+ memcpy(s, prefs, prefs_len + 1);
+
+ while (s && *s && mlist) {
+ char *p = strchr(s, ',');
+ int method_len = p ? (p - s) : (int) strlen(s);
+
+ if (!kex_get_method_by_name(s, method_len, mlist)) {
+ /* Strip out unsupported method */
+ if (p) {
+ memcpy(s, p + 1, strlen(s) - method_len);
+ } else {
+ if (s > newprefs) {
+ *(--s) = '\0';
+ } else {
+ *s = '\0';
+ }
+ }
+ }
+
+ s = p ? (p + 1) : NULL;
+ }
+
+ if (strlen(newprefs) == 0) {
+ LIBSSH2_FREE(session, newprefs);
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "The requested method(s) are not currently "
+ "supported");
+ }
+
+ if (*prefvar) {
+ LIBSSH2_FREE(session, *prefvar);
+ }
+ *prefvar = newprefs;
+
+ return 0;
+}
+
+/*
+ * libssh2_session_supported_algs()
+ * returns a number of returned algorithms (a positive number) on success,
+ * a negative number on failure
+ */
+
+LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session,
+ int method_type,
+ const char*** algs)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int ialg;
+ const LIBSSH2_COMMON_METHOD **mlist;
+
+ /* to prevent coredumps due to dereferencing of NULL */
+ if (NULL == algs)
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "algs must not be NULL");
+
+ switch (method_type) {
+ case LIBSSH2_METHOD_KEX:
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods;
+ break;
+
+ case LIBSSH2_METHOD_HOSTKEY:
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods();
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_CS:
+ case LIBSSH2_METHOD_CRYPT_SC:
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+ break;
+
+ case LIBSSH2_METHOD_MAC_CS:
+ case LIBSSH2_METHOD_MAC_SC:
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+ break;
+
+ case LIBSSH2_METHOD_COMP_CS:
+ case LIBSSH2_METHOD_COMP_SC:
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_comp_methods(session);
+ break;
+
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unknown method type");
+ } /* switch */
+
+ /* weird situation */
+ if (NULL==mlist)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "No algorithm found");
+
+ /*
+ mlist is looped through twice. The first time to find the number od
+ supported algorithms (needed to allocate the proper size of array) and
+ the second time to actually copy the pointers. Typically this function
+ will not be called often (typically at the beginning of a session) and
+ the number of algorithms (i.e. niumber of iterations in one loop) will
+ not be high (typically it will not exceed 20) for quite a long time.
+
+ So double looping really shouldn't be an issue and it is definitely a
+ better solution than reallocation several times.
+ */
+
+ /* count the number of supported algorithms */
+ for ( i=0, ialg=0; NULL!=mlist[i]; i++) {
+ /* do not count fields with NULL name */
+ if (mlist[i]->name)
+ ialg++;
+ }
+
+ /* weird situation, no algorithm found */
+ if (0==ialg)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "No algorithm found");
+
+ /* allocate buffer */
+ *algs = (const char**) LIBSSH2_ALLOC(session, ialg*sizeof(const char*));
+ if ( NULL==*algs ) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Memory allocation failed");
+ }
+ /* Past this point *algs must be deallocated in case of an error!! */
+
+ /* copy non-NULL pointers only */
+ for ( i=0, j=0; NULL!=mlist[i] && j<ialg; i++ ) {
+ if ( NULL==mlist[i]->name ){
+ /* maybe a weird situation but if it occurs, do not include NULL
+ pointers */
+ continue;
+ }
+
+ /* note that [] has higher priority than * (dereferencing) */
+ (*algs)[j++] = mlist[i]->name;
+ }
+
+ /* correct number of pointers copied? (test the code above) */
+ if ( j!=ialg ) {
+ /* deallocate buffer */
+ LIBSSH2_FREE(session, (void *)*algs);
+ *algs = NULL;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "Internal error");
+ }
+
+ return ialg;
+}
diff --git a/libs/libssh2/src/knownhost.c b/libs/libssh2/src/knownhost.c
new file mode 100644
index 0000000000..a32dcf8764
--- /dev/null
+++ b/libs/libssh2/src/knownhost.c
@@ -0,0 +1,1245 @@
+/*
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include "misc.h"
+
+struct known_host {
+ struct list_node node;
+ char *name; /* points to the name or the hash (allocated) */
+ size_t name_len; /* needed for hashed data */
+ int port; /* if non-zero, a specific port this key is for on this
+ host */
+ int typemask; /* plain, sha1, custom, ... */
+ char *salt; /* points to binary salt (allocated) */
+ size_t salt_len; /* size of salt */
+ char *key; /* the (allocated) associated key. This is kept base64
+ encoded in memory. */
+ char *key_type_name; /* the (allocated) key type name */
+ size_t key_type_len; /* size of key_type_name */
+ char *comment; /* the (allocated) optional comment text, may be
+ NULL */
+ size_t comment_len; /* the size of comment */
+
+ /* this is the struct we expose externally */
+ struct libssh2_knownhost external;
+};
+
+struct _LIBSSH2_KNOWNHOSTS
+{
+ LIBSSH2_SESSION *session; /* the session this "belongs to" */
+ struct list_head head;
+};
+
+static void free_host(LIBSSH2_SESSION *session, struct known_host *entry)
+{
+ if(entry) {
+ if(entry->comment)
+ LIBSSH2_FREE(session, entry->comment);
+ if (entry->key_type_name)
+ LIBSSH2_FREE(session, entry->key_type_name);
+ if(entry->key)
+ LIBSSH2_FREE(session, entry->key);
+ if(entry->salt)
+ LIBSSH2_FREE(session, entry->salt);
+ if(entry->name)
+ LIBSSH2_FREE(session, entry->name);
+ LIBSSH2_FREE(session, entry);
+ }
+}
+
+/*
+ * libssh2_knownhost_init
+ *
+ * Init a collection of known hosts. Returns the pointer to a collection.
+ *
+ */
+LIBSSH2_API LIBSSH2_KNOWNHOSTS *
+libssh2_knownhost_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_KNOWNHOSTS *knh =
+ LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS));
+
+ if(!knh) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for known-hosts "
+ "collection");
+ return NULL;
+ }
+
+ knh->session = session;
+
+ _libssh2_list_init(&knh->head);
+
+ return knh;
+}
+
+#define KNOWNHOST_MAGIC 0xdeadcafe
+/*
+ * knownhost_to_external()
+ *
+ * Copies data from the internal to the external representation struct.
+ *
+ */
+static struct libssh2_knownhost *knownhost_to_external(struct known_host *node)
+{
+ struct libssh2_knownhost *ext = &node->external;
+
+ ext->magic = KNOWNHOST_MAGIC;
+ ext->node = node;
+ ext->name = ((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN)? node->name:NULL;
+ ext->key = node->key;
+ ext->typemask = node->typemask;
+
+ return ext;
+}
+
+static int
+knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, const char *salt,
+ const char *key_type_name, size_t key_type_len,
+ const char *key, size_t keylen,
+ const char *comment, size_t commentlen,
+ int typemask, struct libssh2_knownhost **store)
+{
+ struct known_host *entry;
+ size_t hostlen = strlen(host);
+ int rc;
+ char *ptr;
+ unsigned int ptrlen;
+
+ /* make sure we have a key type set */
+ if(!(typemask & LIBSSH2_KNOWNHOST_KEY_MASK))
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+ "No key type set");
+
+ if(!(entry = LIBSSH2_CALLOC(hosts->session, sizeof(struct known_host))))
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for known host "
+ "entry");
+
+ entry->typemask = typemask;
+
+ switch(entry->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) {
+ case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+ case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+ entry->name = LIBSSH2_ALLOC(hosts->session, hostlen+1);
+ if(!entry->name) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for host name");
+ goto error;
+ }
+ memcpy(entry->name, host, hostlen+1);
+ entry->name_len = hostlen;
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+ rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+ host, hostlen);
+ if(rc)
+ goto error;
+ entry->name = ptr;
+ entry->name_len = ptrlen;
+
+ rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+ salt, strlen(salt));
+ if(rc)
+ goto error;
+ entry->salt = ptr;
+ entry->salt_len = ptrlen;
+ break;
+ default:
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unknown host name type");
+ goto error;
+ }
+
+ if(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64) {
+ /* the provided key is base64 encoded already */
+ if(!keylen)
+ keylen = strlen(key);
+ entry->key = LIBSSH2_ALLOC(hosts->session, keylen+1);
+ if(!entry->key) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for key");
+ goto error;
+ }
+ memcpy(entry->key, key, keylen+1);
+ entry->key[keylen]=0; /* force a terminating zero trailer */
+ }
+ else {
+ /* key is raw, we base64 encode it and store it as such */
+ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+ &ptr);
+ if(!nlen) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "base64-encoded key");
+ goto error;
+ }
+
+ entry->key = ptr;
+ }
+
+ if (key_type_name && ((typemask & LIBSSH2_KNOWNHOST_KEY_MASK) ==
+ LIBSSH2_KNOWNHOST_KEY_UNKNOWN)) {
+ entry->key_type_name = LIBSSH2_ALLOC(hosts->session, key_type_len+1);
+ if (!entry->key_type_name) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for key type");
+ goto error;
+ }
+ memcpy(entry->key_type_name, key_type_name, key_type_len);
+ entry->key_type_name[key_type_len]=0;
+ entry->key_type_len = key_type_len;
+ }
+
+ if (comment) {
+ entry->comment = LIBSSH2_ALLOC(hosts->session, commentlen+1);
+ if(!entry->comment) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for comment");
+ goto error;
+ }
+ memcpy(entry->comment, comment, commentlen+1);
+ entry->comment[commentlen]=0; /* force a terminating zero trailer */
+ entry->comment_len = commentlen;
+ }
+ else {
+ entry->comment = NULL;
+ }
+
+ /* add this new host to the big list of known hosts */
+ _libssh2_list_add(&hosts->head, &entry->node);
+
+ if(store)
+ *store = knownhost_to_external(entry);
+
+ return LIBSSH2_ERROR_NONE;
+ error:
+ free_host(hosts->session, entry);
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_add
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * The 'type' argument specifies on what format the given host and keys are:
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ * The keylen parameter may be omitted (zero) if the key is provided as a
+ * NULL-terminated base64-encoded string.
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, const char *salt,
+ const char *key, size_t keylen,
+ int typemask, struct libssh2_knownhost **store)
+{
+ return knownhost_add(hosts, host, salt, NULL, 0, key, keylen, NULL,
+ 0, typemask, store);
+}
+
+
+/*
+ * libssh2_knownhost_addc
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * Takes a comment argument that may be NULL. A NULL comment indicates
+ * there is no comment and the entry will end directly after the key
+ * when written out to a file. An empty string "" comment will indicate an
+ * empty comment which will cause a single space to be written after the key.
+ *
+ * The 'type' argument specifies on what format the given host and keys are:
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ * The keylen parameter may be omitted (zero) if the key is provided as a
+ * NULL-terminated base64-encoded string.
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, const char *salt,
+ const char *key, size_t keylen,
+ const char *comment, size_t commentlen,
+ int typemask, struct libssh2_knownhost **store)
+{
+ return knownhost_add(hosts, host, salt, NULL, 0, key, keylen,
+ comment, commentlen, typemask, store);
+}
+
+/*
+ * knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+static int
+knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *hostp, int port,
+ const char *key, size_t keylen,
+ int typemask,
+ struct libssh2_knownhost **ext)
+{
+ struct known_host *node;
+ struct known_host *badkey = NULL;
+ int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
+ char *keyalloc = NULL;
+ int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND;
+ char hostbuff[270]; /* most host names can't be longer than like 256 */
+ const char *host;
+ int numcheck; /* number of host combos to check */
+ int match = 0;
+
+ if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1)
+ /* we can't work with a sha1 as given input */
+ return LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+
+ /* if a port number is given, check for a '[host]:port' first before the
+ plain 'host' */
+ if(port >= 0) {
+ int len = snprintf(hostbuff, sizeof(hostbuff), "[%s]:%d", hostp, port);
+ if (len < 0 || len >= (int)sizeof(hostbuff)) {
+ _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Known-host write buffer too small");
+ return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+ }
+ host = hostbuff;
+ numcheck = 2; /* check both combos, start with this */
+ }
+ else {
+ host = hostp;
+ numcheck = 1; /* only check this host version */
+ }
+
+ if(!(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64)) {
+ /* we got a raw key input, convert it to base64 for the checks below */
+ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+ &keyalloc);
+ if(!nlen) {
+ _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for base64-encoded "
+ "key");
+ return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+ }
+
+ /* make the key point to this */
+ key = keyalloc;
+ }
+
+ do {
+ node = _libssh2_list_first(&hosts->head);
+ while (node) {
+ switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) {
+ case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN)
+ match = !strcmp(host, node->name);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM)
+ match = !strcmp(host, node->name);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) {
+ /* when we have the sha1 version stored, we can use a
+ plain input to produce a hash to compare with the
+ stored hash.
+ */
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ libssh2_hmac_ctx ctx;
+ libssh2_hmac_ctx_init(ctx);
+
+ if(SHA_DIGEST_LENGTH != node->name_len) {
+ /* the name hash length must be the sha1 size or
+ we can't match it */
+ break;
+ }
+ libssh2_hmac_sha1_init(&ctx, (unsigned char *)node->salt,
+ node->salt_len);
+ libssh2_hmac_update(ctx, (unsigned char *)host,
+ strlen(host));
+ libssh2_hmac_final(ctx, hash);
+ libssh2_hmac_cleanup(&ctx);
+
+ if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH))
+ /* this is a node we're interested in */
+ match = 1;
+ }
+ break;
+ default: /* unsupported type */
+ break;
+ }
+ if(match) {
+ int host_key_type = typemask & LIBSSH2_KNOWNHOST_KEY_MASK;
+ int known_key_type =
+ node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK;
+ /* match on key type as follows:
+ - never match on an unknown key type
+ - if key_type is set to zero, ignore it an match always
+ - otherwise match when both key types are equal
+ */
+ if ( (host_key_type != LIBSSH2_KNOWNHOST_KEY_UNKNOWN ) &&
+ ( (host_key_type == 0) ||
+ (host_key_type == known_key_type) ) ) {
+ /* host name and key type match, now compare the keys */
+ if(!strcmp(key, node->key)) {
+ /* they match! */
+ if (ext)
+ *ext = knownhost_to_external(node);
+ badkey = NULL;
+ rc = LIBSSH2_KNOWNHOST_CHECK_MATCH;
+ break;
+ }
+ else {
+ /* remember the first node that had a host match but a
+ failed key match since we continue our search from
+ here */
+ if(!badkey)
+ badkey = node;
+ }
+ }
+ match = 0; /* don't count this as a match anymore */
+ }
+ node= _libssh2_list_next(&node->node);
+ }
+ host = hostp;
+ } while(!match && --numcheck);
+
+ if(badkey) {
+ /* key mismatch */
+ if (ext)
+ *ext = knownhost_to_external(badkey);
+ rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+ }
+
+ if(keyalloc)
+ LIBSSH2_FREE(hosts->session, keyalloc);
+
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+LIBSSH2_API int
+libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *hostp, const char *key, size_t keylen,
+ int typemask,
+ struct libssh2_knownhost **ext)
+{
+ return knownhost_check(hosts, hostp, -1, key, keylen,
+ typemask, ext);
+}
+
+/*
+ * libssh2_knownhost_checkp
+ *
+ * Check a host+port and its associated key against the collection of known
+ * hosts.
+ *
+ * Note that if 'port' is specified as greater than zero, the check function
+ * will be able to check for a dedicated key for this particular host+port
+ * combo, and if 'port' is negative it only checks for the generic host key.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+LIBSSH2_API int
+libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *hostp, int port,
+ const char *key, size_t keylen,
+ int typemask,
+ struct libssh2_knownhost **ext)
+{
+ return knownhost_check(hosts, hostp, port, key, keylen,
+ typemask, ext);
+}
+
+
+/*
+ * libssh2_knownhost_del
+ *
+ * Remove a host from the collection of known hosts.
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts,
+ struct libssh2_knownhost *entry)
+{
+ struct known_host *node;
+
+ /* check that this was retrieved the right way or get out */
+ if(!entry || (entry->magic != KNOWNHOST_MAGIC))
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+ "Invalid host information");
+
+ /* get the internal node pointer */
+ node = entry->node;
+
+ /* unlink from the list of all hosts */
+ _libssh2_list_remove(&node->node);
+
+ /* clear the struct now since the memory in which it is allocated is
+ about to be freed! */
+ memset(entry, 0, sizeof(struct libssh2_knownhost));
+
+ /* free all resources */
+ free_host(hosts->session, node);
+
+ return 0;
+}
+
+/*
+ * libssh2_knownhost_free
+ *
+ * Free an entire collection of known hosts.
+ *
+ */
+LIBSSH2_API void
+libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts)
+{
+ struct known_host *node;
+ struct known_host *next;
+
+ for(node = _libssh2_list_first(&hosts->head); node; node = next) {
+ next = _libssh2_list_next(&node->node);
+ free_host(hosts->session, node);
+ }
+ LIBSSH2_FREE(hosts->session, hosts);
+}
+
+
+/* old style plain text: [name]([,][name])*
+
+ for the sake of simplicity, we add them as separate hosts with the same
+ key
+*/
+static int oldstyle_hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, size_t hostlen,
+ const char *key_type_name, size_t key_type_len,
+ const char *key, size_t keylen, int key_type,
+ const char *comment, size_t commentlen)
+{
+ int rc = 0;
+ size_t namelen = 0;
+ const char *name = host + hostlen;
+
+ if(hostlen < 1)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(no host names)");
+
+ while(name > host) {
+ --name;
+ ++namelen;
+
+ /* when we get the the start or see a comma coming up, add the host
+ name to the collection */
+ if((name == host) || (*(name-1) == ',')) {
+
+ char hostbuf[256];
+
+ /* make sure we don't overflow the buffer */
+ if(namelen >= sizeof(hostbuf)-1)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(unexpected length)");
+
+ /* copy host name to the temp buffer and zero terminate */
+ memcpy(hostbuf, name, namelen);
+ hostbuf[namelen]=0;
+
+ rc = knownhost_add(hosts, hostbuf, NULL,
+ key_type_name, key_type_len,
+ key, keylen,
+ comment, commentlen,
+ key_type | LIBSSH2_KNOWNHOST_TYPE_PLAIN |
+ LIBSSH2_KNOWNHOST_KEYENC_BASE64, NULL);
+ if(rc)
+ return rc;
+
+ if(name > host) {
+ namelen = 0;
+ --name; /* skip comma */
+ }
+ }
+ }
+
+ return rc;
+}
+
+/* |1|[salt]|[hash] */
+static int hashed_hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, size_t hostlen,
+ const char *key_type_name, size_t key_type_len,
+ const char *key, size_t keylen, int key_type,
+ const char *comment, size_t commentlen)
+{
+ const char *p;
+ char saltbuf[32];
+ char hostbuf[256];
+
+ const char *salt = &host[3]; /* skip the magic marker */
+ hostlen -= 3; /* deduct the marker */
+
+ /* this is where the salt starts, find the end of it */
+ for(p = salt; *p && (*p != '|'); p++)
+ ;
+
+ if(*p=='|') {
+ const char *hash = NULL;
+ size_t saltlen = p - salt;
+ if(saltlen >= (sizeof(saltbuf)-1)) /* weird length */
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(unexpectedly long salt)");
+
+ memcpy(saltbuf, salt, saltlen);
+ saltbuf[saltlen] = 0; /* zero terminate */
+ salt = saltbuf; /* point to the stack based buffer */
+
+ hash = p+1; /* the host hash is after the separator */
+
+ /* now make the host point to the hash */
+ host = hash;
+ hostlen -= saltlen+1; /* deduct the salt and separator */
+
+ /* check that the lengths seem sensible */
+ if(hostlen >= sizeof(hostbuf)-1)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(unexpected length)");
+
+ memcpy(hostbuf, host, hostlen);
+ hostbuf[hostlen]=0;
+
+ return knownhost_add(hosts, hostbuf, salt,
+ key_type_name, key_type_len,
+ key, keylen,
+ comment, commentlen,
+ key_type | LIBSSH2_KNOWNHOST_TYPE_SHA1 |
+ LIBSSH2_KNOWNHOST_KEYENC_BASE64, NULL);
+ }
+ else
+ return 0; /* XXX: This should be an error, shouldn't it? */
+}
+
+/*
+ * hostline()
+ *
+ * Parse a single known_host line pre-split into host and key.
+ *
+ * The key part may include an optional comment which will be parsed here
+ * for ssh-rsa and ssh-dsa keys. Comments in other key types aren't handled.
+ *
+ * The function assumes new-lines have already been removed from the arguments.
+ */
+static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, size_t hostlen,
+ const char *key, size_t keylen)
+{
+ const char *comment = NULL;
+ const char *key_type_name = NULL;
+ size_t commentlen = 0;
+ size_t key_type_len = 0;
+ int key_type;
+
+ /* make some checks that the lengths seem sensible */
+ if(keylen < 20)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(key too short)");
+
+ switch(key[0]) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ key_type = LIBSSH2_KNOWNHOST_KEY_RSA1;
+
+ /* Note that the old-style keys (RSA1) aren't truly base64, but we
+ * claim it is for now since we can get away with strcmp()ing the
+ * entire anything anyway! We need to check and fix these to make them
+ * work properly.
+ */
+ break;
+
+ default:
+ key_type_name = key;
+ while (keylen && *key &&
+ (*key != ' ') && (*key != '\t')) {
+ key++;
+ keylen--;
+ }
+ key_type_len = key - key_type_name;
+
+ if (!strncmp(key_type_name, "ssh-dss", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ else if (!strncmp(key_type_name, "ssh-rsa", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ else
+ key_type = LIBSSH2_KNOWNHOST_KEY_UNKNOWN;
+
+ /* skip whitespaces */
+ while((*key ==' ') || (*key == '\t')) {
+ key++;
+ keylen--;
+ }
+
+ comment = key;
+ commentlen = keylen;
+
+ /* move over key */
+ while(commentlen && *comment &&
+ (*comment != ' ') && (*comment != '\t')) {
+ comment++;
+ commentlen--;
+ }
+
+ /* reduce key by comment length */
+ keylen -= commentlen;
+
+ /* Distinguish empty comment (a space) from no comment (no space) */
+ if (commentlen == 0)
+ comment = NULL;
+
+ /* skip whitespaces */
+ while(commentlen && *comment &&
+ ((*comment ==' ') || (*comment == '\t'))) {
+ comment++;
+ commentlen--;
+ }
+ break;
+ }
+
+ /* Figure out host format */
+ if((hostlen >2) && memcmp(host, "|1|", 3)) {
+ /* old style plain text: [name]([,][name])*
+
+ for the sake of simplicity, we add them as separate hosts with the
+ same key
+ */
+ return oldstyle_hostline(hosts, host, hostlen, key_type_name,
+ key_type_len, key, keylen, key_type,
+ comment, commentlen);
+ }
+ else {
+ /* |1|[salt]|[hash] */
+ return hashed_hostline(hosts, host, hostlen, key_type_name,
+ key_type_len, key, keylen, key_type,
+ comment, commentlen);
+ }
+}
+
+/*
+ * libssh2_knownhost_readline()
+ *
+ * Pass in a line of a file of 'type'.
+ *
+ * LIBSSH2_KNOWNHOST_FILE_OPENSSH is the only supported type.
+ *
+ * OpenSSH line format:
+ *
+ * <host> <key>
+ *
+ * Where the two parts can be created like:
+ *
+ * <host> can be either
+ * <name> or <hash>
+ *
+ * <name> consists of
+ * [name] optionally followed by [,name] one or more times
+ *
+ * <hash> consists of
+ * |1|<salt>|hash
+ *
+ * <key> can be one of:
+ * [RSA bits] [e] [n as a decimal number]
+ * 'ssh-dss' [base64-encoded-key]
+ * 'ssh-rsa' [base64-encoded-key]
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *line, size_t len, int type)
+{
+ const char *cp;
+ const char *hostp;
+ const char *keyp;
+ size_t hostlen;
+ size_t keylen;
+ int rc;
+
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ cp = line;
+
+ /* skip leading whitespaces */
+ while(len && ((*cp==' ') || (*cp == '\t'))) {
+ cp++;
+ len--;
+ }
+
+ if(!len || !*cp || (*cp == '#') || (*cp == '\n'))
+ /* comment or empty line */
+ return LIBSSH2_ERROR_NONE;
+
+ /* the host part starts here */
+ hostp = cp;
+
+ /* move over the host to the separator */
+ while(len && *cp && (*cp!=' ') && (*cp != '\t')) {
+ cp++;
+ len--;
+ }
+
+ hostlen = cp - hostp;
+
+ /* the key starts after the whitespaces */
+ while(len && *cp && ((*cp==' ') || (*cp == '\t'))) {
+ cp++;
+ len--;
+ }
+
+ if(!*cp || !len) /* illegal line */
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line");
+
+ keyp = cp; /* the key starts here */
+ keylen = len;
+
+ /* check if the line (key) ends with a newline and if so kill it */
+ while(len && *cp && (*cp != '\n')) {
+ cp++;
+ len--;
+ }
+
+ /* zero terminate where the newline is */
+ if(*cp == '\n')
+ keylen--; /* don't include this in the count */
+
+ /* deal with this one host+key line */
+ rc = hostline(hosts, hostp, hostlen, keyp, keylen);
+ if(rc)
+ return rc; /* failed */
+
+ return LIBSSH2_ERROR_NONE; /* success */
+}
+
+/*
+ * libssh2_knownhost_readfile
+ *
+ * Read hosts+key pairs from a given file.
+ *
+ * Returns a negative value for error or number of successfully added hosts.
+ *
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *filename, int type)
+{
+ FILE *file;
+ int num = 0;
+ char buf[2048];
+
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ file = fopen(filename, "r");
+ if(file) {
+ while(fgets(buf, sizeof(buf), file)) {
+ if(libssh2_knownhost_readline(hosts, buf, strlen(buf), type)) {
+ num = _libssh2_error(hosts->session, LIBSSH2_ERROR_KNOWN_HOSTS,
+ "Failed to parse known hosts file");
+ break;
+ }
+ num++;
+ }
+ fclose(file);
+ }
+ else
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+ "Failed to open file");
+
+ return num;
+}
+
+/*
+ * knownhost_writeline()
+ *
+ * Ask libssh2 to convert a known host to an output line for storage.
+ *
+ * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given
+ * output buffer is too small to hold the desired output. The 'outlen' field
+ * will then contain the size libssh2 wanted to store, which then is the
+ * smallest sufficient buffer it would require.
+ *
+ */
+static int
+knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
+ struct known_host *node,
+ char *buf, size_t buflen,
+ size_t *outlen, int type)
+{
+ size_t required_size;
+
+ const char *key_type_name;
+ size_t key_type_len;
+
+ /* we only support this single file type for now, bail out on all other
+ attempts */
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ switch(node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) {
+ case LIBSSH2_KNOWNHOST_KEY_RSA1:
+ key_type_name = NULL;
+ key_type_len = 0;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
+ key_type_name = "ssh-rsa";
+ key_type_len = 7;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
+ key_type_name = "ssh-dss";
+ key_type_len = 7;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_UNKNOWN:
+ key_type_name = node->key_type_name;
+ if (key_type_name) {
+ key_type_len = node->key_type_len;
+ break;
+ }
+ /* otherwise fallback to default and error */
+ default:
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host entry");
+ }
+
+ /* When putting together the host line there are three aspects to consider:
+ - Hashed (SHA1) or unhashed hostname
+ - key name or no key name (RSA1)
+ - comment or no comment
+
+ This means there are 2^3 different formats:
+ ("|1|%s|%s %s %s %s\n", salt, hashed_host, key_name, key, comment)
+ ("|1|%s|%s %s %s\n", salt, hashed_host, key_name, key)
+ ("|1|%s|%s %s %s\n", salt, hashed_host, key, comment)
+ ("|1|%s|%s %s\n", salt, hashed_host, key)
+ ("%s %s %s %s\n", host, key_name, key, comment)
+ ("%s %s %s\n", host, key_name, key)
+ ("%s %s %s\n", host, key, comment)
+ ("%s %s\n", host, key)
+
+ Even if the buffer is too small, we have to set outlen to the number of
+ characters the complete line would have taken. We also don't write
+ anything to the buffer unless we are sure we can write everything to the
+ buffer. */
+
+ required_size = strlen(node->key);
+
+ if(key_type_len)
+ required_size += key_type_len + 1; /* ' ' = 1 */
+ if(node->comment)
+ required_size += node->comment_len + 1; /* ' ' = 1 */
+
+ if((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==
+ LIBSSH2_KNOWNHOST_TYPE_SHA1) {
+ char *namealloc;
+ size_t name_base64_len;
+ char *saltalloc;
+ size_t salt_base64_len;
+
+ name_base64_len = _libssh2_base64_encode(hosts->session, node->name,
+ node->name_len, &namealloc);
+ if(!name_base64_len)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "base64-encoded host name");
+
+ salt_base64_len = _libssh2_base64_encode(hosts->session,
+ node->salt, node->salt_len,
+ &saltalloc);
+ if(!salt_base64_len) {
+ LIBSSH2_FREE(hosts->session, namealloc);
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "base64-encoded salt");
+ }
+
+ required_size += salt_base64_len + name_base64_len + 7;
+ /* |1| + | + ' ' + \n + \0 = 7 */
+
+ if(required_size <= buflen) {
+ if(node->comment && key_type_len)
+ snprintf(buf, buflen, "|1|%s|%s %s %s %s\n", saltalloc,
+ namealloc, key_type_name, node->key, node->comment);
+ else if (node->comment)
+ snprintf(buf, buflen, "|1|%s|%s %s %s\n", saltalloc, namealloc,
+ node->key, node->comment);
+ else if (key_type_len)
+ snprintf(buf, buflen, "|1|%s|%s %s %s\n", saltalloc, namealloc,
+ key_type_name, node->key);
+ else
+ snprintf(buf, buflen, "|1|%s|%s %s\n", saltalloc, namealloc,
+ node->key);
+ }
+
+ LIBSSH2_FREE(hosts->session, namealloc);
+ LIBSSH2_FREE(hosts->session, saltalloc);
+ }
+ else {
+ required_size += node->name_len + 3;
+ /* ' ' + '\n' + \0 = 3 */
+
+ if(required_size <= buflen) {
+ if(node->comment && key_type_len)
+ snprintf(buf, buflen, "%s %s %s %s\n", node->name,
+ key_type_name, node->key, node->comment);
+ else if (node->comment)
+ snprintf(buf, buflen, "%s %s %s\n", node->name, node->key,
+ node->comment);
+ else if (key_type_len)
+ snprintf(buf, buflen, "%s %s %s\n", node->name, key_type_name,
+ node->key);
+ else
+ snprintf(buf, buflen, "%s %s\n", node->name, node->key);
+ }
+ }
+
+ /* we report the full length of the data with the trailing zero excluded */
+ *outlen = required_size-1;
+
+ if(required_size <= buflen)
+ return LIBSSH2_ERROR_NONE;
+ else
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Known-host write buffer too small");
+}
+
+/*
+ * libssh2_knownhost_writeline()
+ *
+ * Ask libssh2 to convert a known host to an output line for storage.
+ *
+ * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given
+ * output buffer is too small to hold the desired output.
+ */
+LIBSSH2_API int
+libssh2_knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
+ struct libssh2_knownhost *known,
+ char *buffer, size_t buflen,
+ size_t *outlen, /* the amount of written data */
+ int type)
+{
+ struct known_host *node;
+
+ if(known->magic != KNOWNHOST_MAGIC)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+ "Invalid host information");
+
+ node = known->node;
+
+ return knownhost_writeline(hosts, node, buffer, buflen, outlen, type);
+}
+
+/*
+ * libssh2_knownhost_writefile()
+ *
+ * Write hosts+key pairs to the given file.
+ */
+LIBSSH2_API int
+libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *filename, int type)
+{
+ struct known_host *node;
+ FILE *file;
+ int rc = LIBSSH2_ERROR_NONE;
+ char buffer[2048];
+
+ /* we only support this single file type for now, bail out on all other
+ attempts */
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ file = fopen(filename, "w");
+ if(!file)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+ "Failed to open file");
+
+ for(node = _libssh2_list_first(&hosts->head);
+ node;
+ node = _libssh2_list_next(&node->node)) {
+ size_t wrote = 0;
+ size_t nwrote;
+ rc = knownhost_writeline(hosts, node, buffer, sizeof(buffer), &wrote,
+ type);
+ if(rc)
+ break;
+
+ nwrote = fwrite(buffer, 1, wrote, file);
+ if(nwrote != wrote) {
+ /* failed to write the whole thing, bail out */
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+ "Write failed");
+ break;
+ }
+ }
+ fclose(file);
+
+ return rc;
+}
+
+
+/*
+ * libssh2_knownhost_get()
+ *
+ * Traverse the internal list of known hosts. Pass NULL to 'prev' to get
+ * the first one.
+ *
+ * Returns:
+ * 0 if a fine host was stored in 'store'
+ * 1 if end of hosts
+ * [negative] on errors
+ */
+LIBSSH2_API int
+libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS *hosts,
+ struct libssh2_knownhost **ext,
+ struct libssh2_knownhost *oprev)
+{
+ struct known_host *node;
+ if(oprev && oprev->node) {
+ /* we have a starting point */
+ struct known_host *prev = oprev->node;
+
+ /* get the next node in the list */
+ node = _libssh2_list_next(&prev->node);
+
+ }
+ else
+ node = _libssh2_list_first(&hosts->head);
+
+ if(!node)
+ /* no (more) node */
+ return 1;
+
+ *ext = knownhost_to_external(node);
+
+ return 0;
+}
diff --git a/libs/libssh2/src/libgcrypt.c b/libs/libssh2/src/libgcrypt.c
new file mode 100644
index 0000000000..366d007a34
--- /dev/null
+++ b/libs/libssh2/src/libgcrypt.c
@@ -0,0 +1,627 @@
+/* Copyright (C) 2008, 2009, Simon Josefsson
+ * Copyright (C) 2006, 2007, The Written Word, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#ifdef LIBSSH2_LIBGCRYPT /* compile only if we build with libgcrypt */
+
+#include <string.h>
+
+int
+_libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata, unsigned long coefflen)
+{
+ int rc;
+ (void) e1data;
+ (void) e1len;
+ (void) e2data;
+ (void) e2len;
+
+ if (ddata) {
+ rc = gcry_sexp_build
+ (rsa, NULL,
+ "(private-key(rsa(n%b)(e%b)(d%b)(q%b)(p%b)(u%b)))",
+ nlen, ndata, elen, edata, dlen, ddata, plen, pdata,
+ qlen, qdata, coefflen, coeffdata);
+ } else {
+ rc = gcry_sexp_build(rsa, NULL, "(public-key(rsa(n%b)(e%b)))",
+ nlen, ndata, elen, edata);
+ }
+ if (rc) {
+ *rsa = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ gcry_sexp_t s_sig, s_hash;
+ int rc = -1;
+
+ libssh2_sha1(m, m_len, hash);
+
+ rc = gcry_sexp_build(&s_hash, NULL,
+ "(data (flags pkcs1) (hash sha1 %b))",
+ SHA_DIGEST_LENGTH, hash);
+ if (rc != 0) {
+ return -1;
+ }
+
+ rc = gcry_sexp_build(&s_sig, NULL, "(sig-val(rsa(s %b)))", sig_len, sig);
+ if (rc != 0) {
+ gcry_sexp_release(s_hash);
+ return -1;
+ }
+
+ rc = gcry_pk_verify(s_sig, s_hash, rsa);
+ gcry_sexp_release(s_sig);
+ gcry_sexp_release(s_hash);
+
+ return (rc == 0) ? 0 : -1;
+}
+
+int
+_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx,
+ const unsigned char *p,
+ unsigned long p_len,
+ const unsigned char *q,
+ unsigned long q_len,
+ const unsigned char *g,
+ unsigned long g_len,
+ const unsigned char *y,
+ unsigned long y_len,
+ const unsigned char *x, unsigned long x_len)
+{
+ int rc;
+
+ if (x_len) {
+ rc = gcry_sexp_build
+ (dsactx, NULL,
+ "(private-key(dsa(p%b)(q%b)(g%b)(y%b)(x%b)))",
+ p_len, p, q_len, q, g_len, g, y_len, y, x_len, x);
+ } else {
+ rc = gcry_sexp_build(dsactx, NULL,
+ "(public-key(dsa(p%b)(q%b)(g%b)(y%b)))",
+ p_len, p, q_len, q, g_len, g, y_len, y);
+ }
+
+ if (rc) {
+ *dsactx = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+_libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract private key from memory: "
+ "Method unimplemented in libgcrypt backend");
+}
+
+int
+_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ FILE *fp;
+ unsigned char *data, *save_data;
+ unsigned int datalen;
+ int ret;
+ unsigned char *n, *e, *d, *p, *q, *e1, *e2, *coeff;
+ unsigned int nlen, elen, dlen, plen, qlen, e1len, e2len, coefflen;
+
+ (void) passphrase;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ ret = _libssh2_pem_parse(session,
+ "-----BEGIN RSA PRIVATE KEY-----",
+ "-----END RSA PRIVATE KEY-----",
+ fp, &data, &datalen);
+ fclose(fp);
+ if (ret) {
+ return -1;
+ }
+
+ save_data = data;
+
+ if (_libssh2_pem_decode_sequence(&data, &datalen)) {
+ ret = -1;
+ goto fail;
+ }
+/* First read Version field (should be 0). */
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &n, &nlen);
+ if (ret != 0 || (nlen != 1 && *n != '\0')) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &n, &nlen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &e, &elen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &d, &dlen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &q, &qlen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &e1, &e1len);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &e2, &e2len);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &coeff, &coefflen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ if (_libssh2_rsa_new(rsa, e, elen, n, nlen, d, dlen, p, plen,
+ q, qlen, e1, e1len, e2, e2len, coeff, coefflen)) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = 0;
+
+ fail:
+ LIBSSH2_FREE(session, save_data);
+ return ret;
+}
+
+int
+_libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract private key from memory: "
+ "Method unimplemented in libgcrypt backend");
+}
+
+int
+_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ FILE *fp;
+ unsigned char *data, *save_data;
+ unsigned int datalen;
+ int ret;
+ unsigned char *p, *q, *g, *y, *x;
+ unsigned int plen, qlen, glen, ylen, xlen;
+
+ (void) passphrase;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ ret = _libssh2_pem_parse(session,
+ "-----BEGIN DSA PRIVATE KEY-----",
+ "-----END DSA PRIVATE KEY-----",
+ fp, &data, &datalen);
+ fclose(fp);
+ if (ret) {
+ return -1;
+ }
+
+ save_data = data;
+
+ if (_libssh2_pem_decode_sequence(&data, &datalen)) {
+ ret = -1;
+ goto fail;
+ }
+
+/* First read Version field (should be 0). */
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen);
+ if (ret != 0 || (plen != 1 && *p != '\0')) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &q, &qlen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &g, &glen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &y, &ylen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = _libssh2_pem_decode_integer(&data, &datalen, &x, &xlen);
+ if (ret != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ if (datalen != 0) {
+ ret = -1;
+ goto fail;
+ }
+
+ if (_libssh2_dsa_new(dsa, p, plen, q, qlen, g, glen, y, ylen, x, xlen)) {
+ ret = -1;
+ goto fail;
+ }
+
+ ret = 0;
+
+ fail:
+ LIBSSH2_FREE(session, save_data);
+ return ret;
+}
+
+int
+_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature, size_t *signature_len)
+{
+ gcry_sexp_t sig_sexp;
+ gcry_sexp_t data;
+ int rc;
+ const char *tmp;
+ size_t size;
+
+ if (hash_len != SHA_DIGEST_LENGTH) {
+ return -1;
+ }
+
+ if (gcry_sexp_build(&data, NULL,
+ "(data (flags pkcs1) (hash sha1 %b))",
+ hash_len, hash)) {
+ return -1;
+ }
+
+ rc = gcry_pk_sign(&sig_sexp, data, rsactx);
+
+ gcry_sexp_release(data);
+
+ if (rc != 0) {
+ return -1;
+ }
+
+ data = gcry_sexp_find_token(sig_sexp, "s", 0);
+ if (!data) {
+ return -1;
+ }
+
+ tmp = gcry_sexp_nth_data(data, 1, &size);
+ if (!tmp) {
+ return -1;
+ }
+
+ if (tmp[0] == '\0') {
+ tmp++;
+ size--;
+ }
+
+ *signature = LIBSSH2_ALLOC(session, size);
+ if (!*signature) {
+ return -1;
+ }
+ memcpy(*signature, tmp, size);
+ *signature_len = size;
+
+ return rc;
+}
+
+int
+_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
+ const unsigned char *hash,
+ unsigned long hash_len, unsigned char *sig)
+{
+ unsigned char zhash[SHA_DIGEST_LENGTH + 1];
+ gcry_sexp_t sig_sexp;
+ gcry_sexp_t data;
+ int ret;
+ const char *tmp;
+ size_t size;
+
+ if (hash_len != SHA_DIGEST_LENGTH) {
+ return -1;
+ }
+
+ memcpy(zhash + 1, hash, hash_len);
+ zhash[0] = 0;
+
+ if (gcry_sexp_build(&data, NULL, "(data (value %b))", hash_len + 1, zhash)) {
+ return -1;
+ }
+
+ ret = gcry_pk_sign(&sig_sexp, data, dsactx);
+
+ gcry_sexp_release(data);
+
+ if (ret != 0) {
+ return -1;
+ }
+
+ memset(sig, 0, 40);
+
+/* Extract R. */
+
+ data = gcry_sexp_find_token(sig_sexp, "r", 0);
+ if (!data)
+ goto err;
+
+ tmp = gcry_sexp_nth_data(data, 1, &size);
+ if (!tmp)
+ goto err;
+
+ if (tmp[0] == '\0') {
+ tmp++;
+ size--;
+ }
+
+ if (size < 1 || size > 20)
+ goto err;
+
+ memcpy(sig + (20 - size), tmp, size);
+
+ gcry_sexp_release(data);
+
+/* Extract S. */
+
+ data = gcry_sexp_find_token(sig_sexp, "s", 0);
+ if (!data)
+ goto err;
+
+ tmp = gcry_sexp_nth_data(data, 1, &size);
+ if (!tmp)
+ goto err;
+
+ if (tmp[0] == '\0') {
+ tmp++;
+ size--;
+ }
+
+ if (size < 1 || size > 20)
+ goto err;
+
+ memcpy(sig + 20 + (20 - size), tmp, size);
+ goto out;
+
+ err:
+ ret = -1;
+
+ out:
+ if (sig_sexp) {
+ gcry_sexp_release(sig_sexp);
+ }
+ if (data) {
+ gcry_sexp_release(data);
+ }
+ return ret;
+}
+
+int
+_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx,
+ const unsigned char *sig,
+ const unsigned char *m, unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH + 1];
+ gcry_sexp_t s_sig, s_hash;
+ int rc = -1;
+
+ libssh2_sha1(m, m_len, hash + 1);
+ hash[0] = 0;
+
+ if (gcry_sexp_build(&s_hash, NULL, "(data(flags raw)(value %b))",
+ SHA_DIGEST_LENGTH + 1, hash)) {
+ return -1;
+ }
+
+ if (gcry_sexp_build(&s_sig, NULL, "(sig-val(dsa(r %b)(s %b)))",
+ 20, sig, 20, sig + 20)) {
+ gcry_sexp_release(s_hash);
+ return -1;
+ }
+
+ rc = gcry_pk_verify(s_sig, s_hash, dsactx);
+ gcry_sexp_release(s_sig);
+ gcry_sexp_release(s_hash);
+
+ return (rc == 0) ? 0 : -1;
+}
+
+int
+_libssh2_cipher_init(_libssh2_cipher_ctx * h,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv, unsigned char *secret, int encrypt)
+{
+ int ret;
+ int cipher = _libssh2_gcry_cipher (algo);
+ int mode = _libssh2_gcry_mode (algo);
+ int keylen = gcry_cipher_get_algo_keylen(cipher);
+
+ (void) encrypt;
+
+ ret = gcry_cipher_open(h, cipher, mode, 0);
+ if (ret) {
+ return -1;
+ }
+
+ ret = gcry_cipher_setkey(*h, secret, keylen);
+ if (ret) {
+ gcry_cipher_close(*h);
+ return -1;
+ }
+
+ if (mode != GCRY_CIPHER_MODE_STREAM) {
+ int blklen = gcry_cipher_get_algo_blklen(cipher);
+ if (mode == GCRY_CIPHER_MODE_CTR)
+ ret = gcry_cipher_setctr(*h, iv, blklen);
+ else
+ ret = gcry_cipher_setiv(*h, iv, blklen);
+ if (ret) {
+ gcry_cipher_close(*h);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt, unsigned char *block, size_t blklen)
+{
+ int cipher = _libssh2_gcry_cipher (algo);
+ int ret;
+
+ if (encrypt) {
+ ret = gcry_cipher_encrypt(*ctx, block, blklen, block, blklen);
+ } else {
+ ret = gcry_cipher_decrypt(*ctx, block, blklen, block, blklen);
+ }
+ return ret;
+}
+
+int
+_libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract public key from private key in memory: "
+ "Method unimplemented in libgcrypt backend");
+}
+
+int
+_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public key from private key file: "
+ "Method unimplemented in libgcrypt backend");
+}
+
+void _libssh2_init_aes_ctr(void)
+{
+ /* no implementation */
+}
+#endif /* LIBSSH2_LIBGCRYPT */
diff --git a/libs/libssh2/src/libgcrypt.h b/libs/libssh2/src/libgcrypt.h
new file mode 100644
index 0000000000..11d6ad2dc5
--- /dev/null
+++ b/libs/libssh2/src/libgcrypt.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007, The Written Word, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <gcrypt.h>
+
+#define LIBSSH2_MD5 1
+
+#define LIBSSH2_HMAC_RIPEMD 1
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#define LIBSSH2_AES 1
+#define LIBSSH2_AES_CTR 1
+#define LIBSSH2_BLOWFISH 1
+#define LIBSSH2_RC4 1
+#define LIBSSH2_CAST 1
+#define LIBSSH2_3DES 1
+
+#define LIBSSH2_RSA 1
+#define LIBSSH2_DSA 1
+
+#define MD5_DIGEST_LENGTH 16
+#define SHA_DIGEST_LENGTH 20
+#define SHA256_DIGEST_LENGTH 32
+
+#define _libssh2_random(buf, len) \
+ (gcry_randomize ((buf), (len), GCRY_STRONG_RANDOM), 1)
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+#define libssh2_sha1_ctx gcry_md_hd_t
+
+/* returns 0 in case of failure */
+#define libssh2_sha1_init(ctx) \
+ (GPG_ERR_NO_ERROR == gcry_md_open (ctx, GCRY_MD_SHA1, 0))
+#define libssh2_sha1_update(ctx, data, len) \
+ gcry_md_write (ctx, (unsigned char *) data, len)
+#define libssh2_sha1_final(ctx, out) \
+ memcpy (out, gcry_md_read (ctx, 0), SHA_DIGEST_LENGTH), gcry_md_close (ctx)
+#define libssh2_sha1(message, len, out) \
+ gcry_md_hash_buffer (GCRY_MD_SHA1, out, message, len)
+
+#define libssh2_sha256_ctx gcry_md_hd_t
+
+#define libssh2_sha256_init(ctx) \
+ (GPG_ERR_NO_ERROR == gcry_md_open (ctx, GCRY_MD_SHA256, 0))
+#define libssh2_sha256_update(ctx, data, len) \
+ gcry_md_write (ctx, (unsigned char *) data, len)
+#define libssh2_sha256_final(ctx, out) \
+ memcpy (out, gcry_md_read (ctx, 0), SHA256_DIGEST_LENGTH), gcry_md_close (ctx)
+#define libssh2_sha256(message, len, out) \
+ gcry_md_hash_buffer (GCRY_MD_SHA256, out, message, len)
+
+#define libssh2_md5_ctx gcry_md_hd_t
+
+/* returns 0 in case of failure */
+#define libssh2_md5_init(ctx) \
+ (GPG_ERR_NO_ERROR == gcry_md_open (ctx, GCRY_MD_MD5, 0))
+
+#define libssh2_md5_update(ctx, data, len) \
+ gcry_md_write (ctx, (unsigned char *) data, len)
+#define libssh2_md5_final(ctx, out) \
+ memcpy (out, gcry_md_read (ctx, 0), MD5_DIGEST_LENGTH), gcry_md_close (ctx)
+#define libssh2_md5(message, len, out) \
+ gcry_md_hash_buffer (GCRY_MD_MD5, out, message, len)
+
+#define libssh2_hmac_ctx gcry_md_hd_t
+#define libssh2_hmac_ctx_init(ctx)
+#define libssh2_hmac_sha1_init(ctx, key, keylen) \
+ gcry_md_open (ctx, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC), \
+ gcry_md_setkey (*ctx, key, keylen)
+#define libssh2_hmac_md5_init(ctx, key, keylen) \
+ gcry_md_open (ctx, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC), \
+ gcry_md_setkey (*ctx, key, keylen)
+#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \
+ gcry_md_open (ctx, GCRY_MD_RMD160, GCRY_MD_FLAG_HMAC), \
+ gcry_md_setkey (*ctx, key, keylen)
+#define libssh2_hmac_sha256_init(ctx, key, keylen) \
+ gcry_md_open (ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC), \
+ gcry_md_setkey (*ctx, key, keylen)
+#define libssh2_hmac_sha512_init(ctx, key, keylen) \
+ gcry_md_open (ctx, GCRY_MD_SHA512, GCRY_MD_FLAG_HMAC), \
+ gcry_md_setkey (*ctx, key, keylen)
+#define libssh2_hmac_update(ctx, data, datalen) \
+ gcry_md_write (ctx, (unsigned char *) data, datalen)
+#define libssh2_hmac_final(ctx, data) \
+ memcpy (data, gcry_md_read (ctx, 0), \
+ gcry_md_get_algo_dlen (gcry_md_get_algo (ctx)))
+#define libssh2_hmac_cleanup(ctx) gcry_md_close (*ctx);
+
+#define libssh2_crypto_init() gcry_control (GCRYCTL_DISABLE_SECMEM)
+#define libssh2_crypto_exit()
+
+#define libssh2_rsa_ctx struct gcry_sexp
+
+#define _libssh2_rsa_free(rsactx) gcry_sexp_release (rsactx)
+
+#define libssh2_dsa_ctx struct gcry_sexp
+
+#define _libssh2_dsa_free(dsactx) gcry_sexp_release (dsactx)
+
+#define _libssh2_cipher_type(name) int name
+#define _libssh2_cipher_ctx gcry_cipher_hd_t
+
+#define _libssh2_gcry_ciphermode(c,m) ((c << 8) | m)
+#define _libssh2_gcry_cipher(c) (c >> 8)
+#define _libssh2_gcry_mode(m) (m & 0xFF)
+
+#define _libssh2_cipher_aes256ctr \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CTR)
+#define _libssh2_cipher_aes192ctr \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CTR)
+#define _libssh2_cipher_aes128ctr \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR)
+#define _libssh2_cipher_aes256 \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC)
+#define _libssh2_cipher_aes192 \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC)
+#define _libssh2_cipher_aes128 \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC)
+#define _libssh2_cipher_blowfish \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC)
+#define _libssh2_cipher_arcfour \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM)
+#define _libssh2_cipher_cast5 \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC)
+#define _libssh2_cipher_3des \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC)
+
+
+#define _libssh2_cipher_dtor(ctx) gcry_cipher_close(*(ctx))
+
+#define _libssh2_bn struct gcry_mpi
+#define _libssh2_bn_ctx int
+#define _libssh2_bn_ctx_new() 0
+#define _libssh2_bn_ctx_free(bnctx) ((void)0)
+#define _libssh2_bn_init() gcry_mpi_new(0)
+#define _libssh2_bn_init_from_bin() NULL /* because gcry_mpi_scan() creates a new bignum */
+#define _libssh2_bn_rand(bn, bits, top, bottom) gcry_mpi_randomize (bn, bits, GCRY_WEAK_RANDOM)
+#define _libssh2_bn_mod_exp(r, a, p, m, ctx) gcry_mpi_powm (r, a, p, m)
+#define _libssh2_bn_set_word(bn, val) gcry_mpi_set_ui(bn, val)
+#define _libssh2_bn_from_bin(bn, len, val) gcry_mpi_scan(&((bn)), GCRYMPI_FMT_USG, val, len, NULL)
+#define _libssh2_bn_to_bin(bn, val) gcry_mpi_print (GCRYMPI_FMT_USG, val, _libssh2_bn_bytes(bn), NULL, bn)
+#define _libssh2_bn_bytes(bn) (gcry_mpi_get_nbits (bn) / 8 + ((gcry_mpi_get_nbits (bn) % 8 == 0) ? 0 : 1))
+#define _libssh2_bn_bits(bn) gcry_mpi_get_nbits (bn)
+#define _libssh2_bn_free(bn) gcry_mpi_release(bn)
+
diff --git a/libs/libssh2/src/libssh2.pc.in b/libs/libssh2/src/libssh2.pc.in
new file mode 100644
index 0000000000..c070988b93
--- /dev/null
+++ b/libs/libssh2/src/libssh2.pc.in
@@ -0,0 +1,17 @@
+###########################################################################
+# libssh2 installation details
+###########################################################################
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+
+Name: @PROJECT_NAME@
+URL: @PROJECT_URL@
+Description: @PROJECT_DESCRIPTION@
+Version: @LIBSSH2_VERSION@
+Requires.private: @PC_REQUIRES_PRIVATE@
+Libs: -L${libdir} -lssh2 @PC_LIBS@
+Libs.private: @PC_LIBS@
+Cflags: -I${includedir} \ No newline at end of file
diff --git a/libs/libssh2/src/libssh2_config.h b/libs/libssh2/src/libssh2_config.h
new file mode 100644
index 0000000000..c2e98356a4
--- /dev/null
+++ b/libs/libssh2/src/libssh2_config.h
@@ -0,0 +1,50 @@
+#ifndef LIBSSH2_CONFIG_H
+#define LIBSSH2_CONFIG_H
+
+#ifndef WIN32
+#define WIN32
+#endif
+#ifndef _CRT_SECURE_NO_DEPRECATE
+#define _CRT_SECURE_NO_DEPRECATE 1
+#endif /* _CRT_SECURE_NO_DEPRECATE */
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+
+#ifdef __MINGW32__
+#define HAVE_UNISTD_H
+#define HAVE_INTTYPES_H
+#define HAVE_SYS_TIME_H
+#define HAVE_GETTIMEOFDAY
+#endif /* __MINGW32__ */
+
+#define HAVE_WINSOCK2_H
+#define HAVE_WINDOWS_H
+#define HAVE_WS2TCPIP_H
+#define HAVE_IOCTLSOCKET
+#define HAVE_SELECT
+
+#define LIBSSH2_OPENSSL
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1900
+#define snprintf _snprintf
+#if _MSC_VER < 1500
+#define vsnprintf _vsnprintf
+#endif
+#define strdup _strdup
+#define strncasecmp _strnicmp
+#define strcasecmp _stricmp
+#endif
+#else
+#ifndef __MINGW32__
+#define strncasecmp strnicmp
+#define strcasecmp stricmp
+#endif /* __MINGW32__ */
+#endif /* _MSC_VER */
+
+/* Enable newer diffie-hellman-group-exchange-sha1 syntax */
+#define LIBSSH2_DH_GEX_NEW 1
+
+#endif /* LIBSSH2_CONFIG_H */
+
diff --git a/libs/libssh2/src/libssh2_config.h.in b/libs/libssh2/src/libssh2_config.h.in
new file mode 100644
index 0000000000..af4ab9ca0a
--- /dev/null
+++ b/libs/libssh2/src/libssh2_config.h.in
@@ -0,0 +1,261 @@
+/* src/libssh2_config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the declaration of `SecureZeroMemory', and to 0 if
+ you don't. */
+#undef HAVE_DECL_SECUREZEROMEMORY
+
+/* disabled non-blocking sockets */
+#undef HAVE_DISABLED_NONBLOCKING
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the `EVP_aes_128_ctr' function. */
+#undef HAVE_EVP_AES_128_CTR
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* use FIONBIO for non-blocking sockets */
+#undef HAVE_FIONBIO
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* use ioctlsocket() for non-blocking sockets */
+#undef HAVE_IOCTLSOCKET
+
+/* use Ioctlsocket() for non-blocking sockets */
+#undef HAVE_IOCTLSOCKET_CASE
+
+/* Define if you have the bcrypt library. */
+#undef HAVE_LIBBCRYPT
+
+/* Define if you have the crypt32 library. */
+#undef HAVE_LIBCRYPT32
+
+/* Define if you have the gcrypt library. */
+#undef HAVE_LIBGCRYPT
+
+/* Define if you have the mbedtls library. */
+#undef HAVE_LIBMBEDTLS
+
+/* Define if you have the ssl library. */
+#undef HAVE_LIBSSL
+
+/* Define if you have the z library. */
+#undef HAVE_LIBZ
+
+/* Define to 1 if the compiler supports the 'long long' data type. */
+#undef HAVE_LONGLONG
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the <ntdef.h> header file. */
+#undef HAVE_NTDEF_H
+
+/* Define to 1 if you have the <ntstatus.h> header file. */
+#undef HAVE_NTSTATUS_H
+
+/* use O_NONBLOCK for non-blocking sockets */
+#undef HAVE_O_NONBLOCK
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the select function. */
+#undef HAVE_SELECT
+
+/* use SO_NONBLOCK for non-blocking sockets */
+#undef HAVE_SO_NONBLOCK
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strtoll' function. */
+#undef HAVE_STRTOLL
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+#undef HAVE_WINSOCK2_H
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+#undef HAVE_WS2TCPIP_H
+
+/* to make a symbol visible */
+#undef LIBSSH2_API
+
+/* Enable clearing of memory before being freed */
+#undef LIBSSH2_CLEAR_MEMORY
+
+/* Enable "none" cipher -- NOT RECOMMENDED */
+#undef LIBSSH2_CRYPT_NONE
+
+/* Enable newer diffie-hellman-group-exchange-sha1 syntax */
+#undef LIBSSH2_DH_GEX_NEW
+
+/* Compile in zlib support */
+#undef LIBSSH2_HAVE_ZLIB
+
+/* Use libgcrypt */
+#undef LIBSSH2_LIBGCRYPT
+
+/* Enable "none" MAC -- NOT RECOMMENDED */
+#undef LIBSSH2_MAC_NONE
+
+/* Use mbedtls */
+#undef LIBSSH2_MBEDTLS
+
+/* Use OpenSSL */
+#undef LIBSSH2_OPENSSL
+
+/* Use Windows CNG */
+#undef LIBSSH2_WINCNG
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#undef LT_OBJDIR
+
+/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
+#undef NEED_REENTRANT
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
diff --git a/libs/libssh2/src/libssh2_config_cmake.h.in b/libs/libssh2/src/libssh2_config_cmake.h.in
new file mode 100644
index 0000000000..62723ede84
--- /dev/null
+++ b/libs/libssh2/src/libssh2_config_cmake.h.in
@@ -0,0 +1,105 @@
+/* Copyright (c) 2014 Alexander Lamaison <alexander.lamaison@gmail.com>
+ * Copyright (c) 1999-2011 Douglas Gilbert. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/* Headers */
+#cmakedefine HAVE_UNISTD_H
+#cmakedefine HAVE_INTTYPES_H
+#cmakedefine HAVE_STDLIB_H
+#cmakedefine HAVE_SYS_SELECT_H
+#cmakedefine HAVE_SYS_UIO_H
+#cmakedefine HAVE_SYS_SOCKET_H
+#cmakedefine HAVE_SYS_IOCTL_H
+#cmakedefine HAVE_SYS_TIME_H
+#cmakedefine HAVE_SYS_UN_H
+#cmakedefine HAVE_WINDOWS_H
+#cmakedefine HAVE_WS2TCPIP_H
+#cmakedefine HAVE_WINSOCK2_H
+#cmakedefine HAVE_NTDEF_H
+#cmakedefine HAVE_NTSTATUS_H
+
+/* Libraries */
+#cmakedefine HAVE_LIBCRYPT32
+
+/* Types */
+#cmakedefine HAVE_LONGLONG
+
+/* Functions */
+#cmakedefine HAVE_GETTIMEOFDAY
+#cmakedefine HAVE_INET_ADDR
+#cmakedefine HAVE_POLL
+#cmakedefine HAVE_SELECT
+#cmakedefine HAVE_SOCKET
+#cmakedefine HAVE_STRTOLL
+#cmakedefine HAVE_STRTOI64
+#cmakedefine HAVE_SNPRINTF
+
+/* OpenSSL functions */
+#cmakedefine HAVE_EVP_AES_128_CTR
+
+/* Socket non-blocking support */
+#cmakedefine HAVE_O_NONBLOCK
+#cmakedefine HAVE_FIONBIO
+#cmakedefine HAVE_IOCTLSOCKET
+#cmakedefine HAVE_IOCTLSOCKET_CASE
+#cmakedefine HAVE_SO_NONBLOCK
+#cmakedefine HAVE_DISABLED_NONBLOCKING
+
+/* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible.
+ We provide a safe wrapper if snprintf not found */
+#ifndef HAVE_SNPRINTF
+#include <stdio.h>
+#include <stdarg.h>
+/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1
+* then assume cp is pointing to a null char and do nothing. Returns number
+* number of chars placed in cp excluding the trailing null char. So for
+* cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len
+* <= 0 the return value is 0 (and no chars are written to cp). */
+static int snprintf(char * cp, int cp_max_len, const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ if (cp_max_len < 2)
+ return 0;
+ va_start(args, fmt);
+ n = vsnprintf(cp, cp_max_len, fmt, args);
+ va_end(args);
+ return (n < cp_max_len) ? n : (cp_max_len - 1);
+}
+
+#define HAVE_SNPRINTF
+#endif
diff --git a/libs/libssh2/src/libssh2_priv.h b/libs/libssh2/src/libssh2_priv.h
new file mode 100644
index 0000000000..841467773e
--- /dev/null
+++ b/libs/libssh2/src/libssh2_priv.h
@@ -0,0 +1,1066 @@
+/* Copyright (c) 2004-2008, 2010, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#ifndef LIBSSH2_PRIV_H
+#define LIBSSH2_PRIV_H 1
+
+#include "libssh2_config.h"
+
+#ifdef HAVE_WINDOWS_H
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+
+/* The following CPP block should really only be in session.c and
+ packet.c. However, AIX have #define's for 'events' and 'revents'
+ and we are using those names in libssh2.h, so we need to include
+ the AIX headers first, to make sure all code is compiled with
+ consistent names of these fields. While arguable the best would to
+ change libssh2.h to use other names, that would break backwards
+ compatibility. For more information, see:
+ https://www.mail-archive.com/libssh2-devel%40lists.sourceforge.net/msg00003.html
+ https://www.mail-archive.com/libssh2-devel%40lists.sourceforge.net/msg00224.html
+*/
+#ifdef HAVE_POLL
+# include <sys/poll.h>
+#else
+# if defined(HAVE_SELECT) && !defined(WIN32)
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# else
+# include <sys/time.h>
+# include <sys/types.h>
+# endif
+# endif
+#endif
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include "libssh2.h"
+#include "libssh2_publickey.h"
+#include "libssh2_sftp.h"
+#include "misc.h" /* for the linked list stuff */
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifdef _MSC_VER
+/* "inline" keyword is valid only with C++ engine! */
+#define inline __inline
+#endif
+
+/* Provide iovec / writev on WIN32 platform. */
+#ifdef WIN32
+
+struct iovec {
+ size_t iov_len;
+ void * iov_base;
+};
+
+static inline int writev(int sock, struct iovec *iov, int nvecs)
+{
+ DWORD ret;
+ if (WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) {
+ return ret;
+ }
+ return -1;
+}
+
+#endif /* WIN32 */
+
+#ifdef __OS400__
+/* Force parameter type. */
+#define send(s, b, l, f) send((s), (unsigned char *) (b), (l), (f))
+#endif
+
+#include "crypto.h"
+
+#ifdef HAVE_WINSOCK2_H
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#endif
+
+/* RFC4253 section 6.1 Maximum Packet Length says:
+ *
+ * "All implementations MUST be able to process packets with
+ * uncompressed payload length of 32768 bytes or less and
+ * total packet size of 35000 bytes or less (including length,
+ * padding length, payload, padding, and MAC.)."
+ */
+#define MAX_SSH_PACKET_LEN 35000
+#define MAX_SHA_DIGEST_LEN SHA256_DIGEST_LENGTH
+
+#define LIBSSH2_ALLOC(session, count) \
+ session->alloc((count), &(session)->abstract)
+#define LIBSSH2_CALLOC(session, count) _libssh2_calloc(session, count)
+#define LIBSSH2_REALLOC(session, ptr, count) \
+ ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : \
+ session->alloc((count), &(session)->abstract))
+#define LIBSSH2_FREE(session, ptr) \
+ session->free((ptr), &(session)->abstract)
+#define LIBSSH2_IGNORE(session, data, datalen) \
+ session->ssh_msg_ignore((session), (data), (datalen), &(session)->abstract)
+#define LIBSSH2_DEBUG(session, always_display, message, message_len, \
+ language, language_len) \
+ session->ssh_msg_debug((session), (always_display), (message), \
+ (message_len), (language), (language_len), \
+ &(session)->abstract)
+#define LIBSSH2_DISCONNECT(session, reason, message, message_len, \
+ language, language_len) \
+ session->ssh_msg_disconnect((session), (reason), (message), \
+ (message_len), (language), (language_len), \
+ &(session)->abstract)
+
+#define LIBSSH2_MACERROR(session, data, datalen) \
+ session->macerror((session), (data), (datalen), &(session)->abstract)
+#define LIBSSH2_X11_OPEN(channel, shost, sport) \
+ channel->session->x11(((channel)->session), (channel), \
+ (shost), (sport), (&(channel)->session->abstract))
+
+#define LIBSSH2_CHANNEL_CLOSE(session, channel) \
+ channel->close_cb((session), &(session)->abstract, \
+ (channel), &(channel)->abstract)
+
+#define LIBSSH2_SEND_FD(session, fd, buffer, length, flags) \
+ (session->send)(fd, buffer, length, flags, &session->abstract)
+#define LIBSSH2_RECV_FD(session, fd, buffer, length, flags) \
+ (session->recv)(fd, buffer, length, flags, &session->abstract)
+
+#define LIBSSH2_SEND(session, buffer, length, flags) \
+ LIBSSH2_SEND_FD(session, session->socket_fd, buffer, length, flags)
+#define LIBSSH2_RECV(session, buffer, length, flags) \
+ LIBSSH2_RECV_FD(session, session->socket_fd, buffer, length, flags)
+
+typedef struct _LIBSSH2_KEX_METHOD LIBSSH2_KEX_METHOD;
+typedef struct _LIBSSH2_HOSTKEY_METHOD LIBSSH2_HOSTKEY_METHOD;
+typedef struct _LIBSSH2_CRYPT_METHOD LIBSSH2_CRYPT_METHOD;
+typedef struct _LIBSSH2_COMP_METHOD LIBSSH2_COMP_METHOD;
+
+typedef struct _LIBSSH2_PACKET LIBSSH2_PACKET;
+
+typedef enum
+{
+ libssh2_NB_state_idle = 0,
+ libssh2_NB_state_allocated,
+ libssh2_NB_state_created,
+ libssh2_NB_state_sent,
+ libssh2_NB_state_sent1,
+ libssh2_NB_state_sent2,
+ libssh2_NB_state_sent3,
+ libssh2_NB_state_sent4,
+ libssh2_NB_state_sent5,
+ libssh2_NB_state_sent6,
+ libssh2_NB_state_sent7,
+ libssh2_NB_state_jump1,
+ libssh2_NB_state_jump2,
+ libssh2_NB_state_jump3,
+ libssh2_NB_state_jump4,
+ libssh2_NB_state_jump5,
+ libssh2_NB_state_end
+} libssh2_nonblocking_states;
+
+typedef struct packet_require_state_t
+{
+ libssh2_nonblocking_states state;
+ time_t start;
+} packet_require_state_t;
+
+typedef struct packet_requirev_state_t
+{
+ time_t start;
+} packet_requirev_state_t;
+
+typedef struct kmdhgGPshakex_state_t
+{
+ libssh2_nonblocking_states state;
+ unsigned char *e_packet;
+ unsigned char *s_packet;
+ unsigned char *tmp;
+ unsigned char h_sig_comp[MAX_SHA_DIGEST_LEN];
+ unsigned char c;
+ size_t e_packet_len;
+ size_t s_packet_len;
+ size_t tmp_len;
+ _libssh2_bn_ctx *ctx;
+ _libssh2_bn *x;
+ _libssh2_bn *e;
+ _libssh2_bn *f;
+ _libssh2_bn *k;
+ unsigned char *s;
+ unsigned char *f_value;
+ unsigned char *k_value;
+ unsigned char *h_sig;
+ size_t f_value_len;
+ size_t k_value_len;
+ size_t h_sig_len;
+ void *exchange_hash;
+ packet_require_state_t req_state;
+ libssh2_nonblocking_states burn_state;
+} kmdhgGPshakex_state_t;
+
+typedef struct key_exchange_state_low_t
+{
+ libssh2_nonblocking_states state;
+ packet_require_state_t req_state;
+ kmdhgGPshakex_state_t exchange_state;
+ _libssh2_bn *p; /* SSH2 defined value (p_value) */
+ _libssh2_bn *g; /* SSH2 defined value (2) */
+ unsigned char request[13];
+ unsigned char *data;
+ size_t request_len;
+ size_t data_len;
+} key_exchange_state_low_t;
+
+typedef struct key_exchange_state_t
+{
+ libssh2_nonblocking_states state;
+ packet_require_state_t req_state;
+ key_exchange_state_low_t key_state_low;
+ unsigned char *data;
+ size_t data_len;
+ unsigned char *oldlocal;
+ size_t oldlocal_len;
+} key_exchange_state_t;
+
+#define FwdNotReq "Forward not requested"
+
+typedef struct packet_queue_listener_state_t
+{
+ libssh2_nonblocking_states state;
+ unsigned char packet[17 + (sizeof(FwdNotReq) - 1)];
+ unsigned char *host;
+ unsigned char *shost;
+ uint32_t sender_channel;
+ uint32_t initial_window_size;
+ uint32_t packet_size;
+ uint32_t port;
+ uint32_t sport;
+ uint32_t host_len;
+ uint32_t shost_len;
+ LIBSSH2_CHANNEL *channel;
+} packet_queue_listener_state_t;
+
+#define X11FwdUnAvil "X11 Forward Unavailable"
+
+typedef struct packet_x11_open_state_t
+{
+ libssh2_nonblocking_states state;
+ unsigned char packet[17 + (sizeof(X11FwdUnAvil) - 1)];
+ unsigned char *shost;
+ uint32_t sender_channel;
+ uint32_t initial_window_size;
+ uint32_t packet_size;
+ uint32_t sport;
+ uint32_t shost_len;
+ LIBSSH2_CHANNEL *channel;
+} packet_x11_open_state_t;
+
+struct _LIBSSH2_PACKET
+{
+ struct list_node node; /* linked list header */
+
+ /* the raw unencrypted payload */
+ unsigned char *data;
+ size_t data_len;
+
+ /* Where to start reading data from,
+ * used for channel data that's been partially consumed */
+ size_t data_head;
+};
+
+typedef struct _libssh2_channel_data
+{
+ /* Identifier */
+ uint32_t id;
+
+ /* Limits and restrictions */
+ uint32_t window_size_initial, window_size, packet_size;
+
+ /* Set to 1 when CHANNEL_CLOSE / CHANNEL_EOF sent/received */
+ char close, eof, extended_data_ignore_mode;
+} libssh2_channel_data;
+
+struct _LIBSSH2_CHANNEL
+{
+ struct list_node node;
+
+ unsigned char *channel_type;
+ unsigned channel_type_len;
+
+ /* channel's program exit status */
+ int exit_status;
+
+ /* channel's program exit signal (without the SIG prefix) */
+ char *exit_signal;
+
+ libssh2_channel_data local, remote;
+ /* Amount of bytes to be refunded to receive window (but not yet sent) */
+ uint32_t adjust_queue;
+ /* Data immediately available for reading */
+ uint32_t read_avail;
+
+ LIBSSH2_SESSION *session;
+
+ void *abstract;
+ LIBSSH2_CHANNEL_CLOSE_FUNC((*close_cb));
+
+ /* State variables used in libssh2_channel_setenv_ex() */
+ libssh2_nonblocking_states setenv_state;
+ unsigned char *setenv_packet;
+ size_t setenv_packet_len;
+ unsigned char setenv_local_channel[4];
+ packet_requirev_state_t setenv_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_request_pty_ex()
+ libssh2_channel_request_pty_size_ex() */
+ libssh2_nonblocking_states reqPTY_state;
+ unsigned char reqPTY_packet[41 + 256];
+ size_t reqPTY_packet_len;
+ unsigned char reqPTY_local_channel[4];
+ packet_requirev_state_t reqPTY_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_x11_req_ex() */
+ libssh2_nonblocking_states reqX11_state;
+ unsigned char *reqX11_packet;
+ size_t reqX11_packet_len;
+ unsigned char reqX11_local_channel[4];
+ packet_requirev_state_t reqX11_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_process_startup() */
+ libssh2_nonblocking_states process_state;
+ unsigned char *process_packet;
+ size_t process_packet_len;
+ unsigned char process_local_channel[4];
+ packet_requirev_state_t process_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_flush_ex() */
+ libssh2_nonblocking_states flush_state;
+ size_t flush_refund_bytes;
+ size_t flush_flush_bytes;
+
+ /* State variables used in libssh2_channel_receive_window_adjust() */
+ libssh2_nonblocking_states adjust_state;
+ unsigned char adjust_adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */
+
+ /* State variables used in libssh2_channel_read_ex() */
+ libssh2_nonblocking_states read_state;
+
+ uint32_t read_local_id;
+
+ /* State variables used in libssh2_channel_write_ex() */
+ libssh2_nonblocking_states write_state;
+ unsigned char write_packet[13];
+ size_t write_packet_len;
+ size_t write_bufwrite;
+
+ /* State variables used in libssh2_channel_close() */
+ libssh2_nonblocking_states close_state;
+ unsigned char close_packet[5];
+
+ /* State variables used in libssh2_channel_wait_closedeof() */
+ libssh2_nonblocking_states wait_eof_state;
+
+ /* State variables used in libssh2_channel_wait_closed() */
+ libssh2_nonblocking_states wait_closed_state;
+
+ /* State variables used in libssh2_channel_free() */
+ libssh2_nonblocking_states free_state;
+
+ /* State variables used in libssh2_channel_handle_extended_data2() */
+ libssh2_nonblocking_states extData2_state;
+
+};
+
+struct _LIBSSH2_LISTENER
+{
+ struct list_node node; /* linked list header */
+
+ LIBSSH2_SESSION *session;
+
+ char *host;
+ int port;
+
+ /* a list of CHANNELs for this listener */
+ struct list_head queue;
+
+ int queue_size;
+ int queue_maxsize;
+
+ /* State variables used in libssh2_channel_forward_cancel() */
+ libssh2_nonblocking_states chanFwdCncl_state;
+ unsigned char *chanFwdCncl_data;
+ size_t chanFwdCncl_data_len;
+};
+
+typedef struct _libssh2_endpoint_data
+{
+ unsigned char *banner;
+
+ unsigned char *kexinit;
+ size_t kexinit_len;
+
+ const LIBSSH2_CRYPT_METHOD *crypt;
+ void *crypt_abstract;
+
+ const struct _LIBSSH2_MAC_METHOD *mac;
+ uint32_t seqno;
+ void *mac_abstract;
+
+ const LIBSSH2_COMP_METHOD *comp;
+ void *comp_abstract;
+
+ /* Method Preferences -- NULL yields "load order" */
+ char *crypt_prefs;
+ char *mac_prefs;
+ char *comp_prefs;
+ char *lang_prefs;
+} libssh2_endpoint_data;
+
+#define PACKETBUFSIZE (1024*16)
+
+struct transportpacket
+{
+ /* ------------- for incoming data --------------- */
+ unsigned char buf[PACKETBUFSIZE];
+ unsigned char init[5]; /* first 5 bytes of the incoming data stream,
+ still encrypted */
+ size_t writeidx; /* at what array index we do the next write into
+ the buffer */
+ size_t readidx; /* at what array index we do the next read from
+ the buffer */
+ uint32_t packet_length; /* the most recent packet_length as read from the
+ network data */
+ uint8_t padding_length; /* the most recent padding_length as read from the
+ network data */
+ size_t data_num; /* How much of the total package that has been read
+ so far. */
+ size_t total_num; /* How much a total package is supposed to be, in
+ number of bytes. A full package is
+ packet_length + padding_length + 4 +
+ mac_length. */
+ unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC()
+ area to which we write decrypted data */
+ unsigned char *wptr; /* write pointer into the payload to where we
+ are currently writing decrypted data */
+
+ /* ------------- for outgoing data --------------- */
+ unsigned char outbuf[MAX_SSH_PACKET_LEN]; /* area for the outgoing data */
+
+ int ototal_num; /* size of outbuf in number of bytes */
+ const unsigned char *odata; /* original pointer to the data */
+ size_t olen; /* original size of the data we stored in
+ outbuf */
+ size_t osent; /* number of bytes already sent */
+};
+
+struct _LIBSSH2_PUBLICKEY
+{
+ LIBSSH2_CHANNEL *channel;
+ uint32_t version;
+
+ /* State variables used in libssh2_publickey_packet_receive() */
+ libssh2_nonblocking_states receive_state;
+ unsigned char *receive_packet;
+ size_t receive_packet_len;
+
+ /* State variables used in libssh2_publickey_add_ex() */
+ libssh2_nonblocking_states add_state;
+ unsigned char *add_packet;
+ unsigned char *add_s;
+
+ /* State variables used in libssh2_publickey_remove_ex() */
+ libssh2_nonblocking_states remove_state;
+ unsigned char *remove_packet;
+ unsigned char *remove_s;
+
+ /* State variables used in libssh2_publickey_list_fetch() */
+ libssh2_nonblocking_states listFetch_state;
+ unsigned char *listFetch_s;
+ unsigned char listFetch_buffer[12];
+ unsigned char *listFetch_data;
+ size_t listFetch_data_len;
+};
+
+#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
+
+struct flags {
+ int sigpipe; /* LIBSSH2_FLAG_SIGPIPE */
+ int compress; /* LIBSSH2_FLAG_COMPRESS */
+};
+
+struct _LIBSSH2_SESSION
+{
+ /* Memory management callbacks */
+ void *abstract;
+ LIBSSH2_ALLOC_FUNC((*alloc));
+ LIBSSH2_REALLOC_FUNC((*realloc));
+ LIBSSH2_FREE_FUNC((*free));
+
+ /* Other callbacks */
+ LIBSSH2_IGNORE_FUNC((*ssh_msg_ignore));
+ LIBSSH2_DEBUG_FUNC((*ssh_msg_debug));
+ LIBSSH2_DISCONNECT_FUNC((*ssh_msg_disconnect));
+ LIBSSH2_MACERROR_FUNC((*macerror));
+ LIBSSH2_X11_OPEN_FUNC((*x11));
+ LIBSSH2_SEND_FUNC((*send));
+ LIBSSH2_RECV_FUNC((*recv));
+
+ /* Method preferences -- NULL yields "load order" */
+ char *kex_prefs;
+ char *hostkey_prefs;
+
+ int state;
+
+ /* Flag options */
+ struct flags flag;
+
+ /* Agreed Key Exchange Method */
+ const LIBSSH2_KEX_METHOD *kex;
+ unsigned int burn_optimistic_kexinit:1;
+
+ unsigned char *session_id;
+ uint32_t session_id_len;
+
+ /* this is set to TRUE if a blocking API behavior is requested */
+ int api_block_mode;
+
+ /* Timeout used when blocking API behavior is active */
+ long api_timeout;
+
+ /* Server's public key */
+ const LIBSSH2_HOSTKEY_METHOD *hostkey;
+ void *server_hostkey_abstract;
+
+ /* Either set with libssh2_session_hostkey() (for server mode)
+ * Or read from server in (eg) KEXDH_INIT (for client mode)
+ */
+ unsigned char *server_hostkey;
+ uint32_t server_hostkey_len;
+#if LIBSSH2_MD5
+ unsigned char server_hostkey_md5[MD5_DIGEST_LENGTH];
+ int server_hostkey_md5_valid;
+#endif /* ! LIBSSH2_MD5 */
+ unsigned char server_hostkey_sha1[SHA_DIGEST_LENGTH];
+ int server_hostkey_sha1_valid;
+
+ /* (remote as source of data -- packet_read ) */
+ libssh2_endpoint_data remote;
+
+ /* (local as source of data -- packet_write ) */
+ libssh2_endpoint_data local;
+
+ /* Inbound Data linked list -- Sometimes the packet that comes in isn't the
+ packet we're ready for */
+ struct list_head packets;
+
+ /* Active connection channels */
+ struct list_head channels;
+
+ uint32_t next_channel;
+
+ struct list_head listeners; /* list of LIBSSH2_LISTENER structs */
+
+ /* Actual I/O socket */
+ libssh2_socket_t socket_fd;
+ int socket_state;
+ int socket_block_directions;
+ int socket_prev_blockstate; /* stores the state of the socket blockiness
+ when libssh2_session_startup() is called */
+
+ /* Error tracking */
+ const char *err_msg;
+ int err_code;
+ int err_flags;
+
+ /* struct members for packet-level reading */
+ struct transportpacket packet;
+#ifdef LIBSSH2DEBUG
+ int showmask; /* what debug/trace messages to display */
+ libssh2_trace_handler_func tracehandler; /* callback to display trace messages */
+ void* tracehandler_context; /* context for the trace handler */
+#endif
+
+ /* State variables used in libssh2_banner_send() */
+ libssh2_nonblocking_states banner_TxRx_state;
+ char banner_TxRx_banner[256];
+ ssize_t banner_TxRx_total_send;
+
+ /* State variables used in libssh2_kexinit() */
+ libssh2_nonblocking_states kexinit_state;
+ unsigned char *kexinit_data;
+ size_t kexinit_data_len;
+
+ /* State variables used in libssh2_session_startup() */
+ libssh2_nonblocking_states startup_state;
+ unsigned char *startup_data;
+ size_t startup_data_len;
+ unsigned char startup_service[sizeof("ssh-userauth") + 5 - 1];
+ size_t startup_service_length;
+ packet_require_state_t startup_req_state;
+ key_exchange_state_t startup_key_state;
+
+ /* State variables used in libssh2_session_free() */
+ libssh2_nonblocking_states free_state;
+
+ /* State variables used in libssh2_session_disconnect_ex() */
+ libssh2_nonblocking_states disconnect_state;
+ unsigned char disconnect_data[256 + 13];
+ size_t disconnect_data_len;
+
+ /* State variables used in libssh2_packet_read() */
+ libssh2_nonblocking_states readPack_state;
+ int readPack_encrypted;
+
+ /* State variables used in libssh2_userauth_list() */
+ libssh2_nonblocking_states userauth_list_state;
+ unsigned char *userauth_list_data;
+ size_t userauth_list_data_len;
+ packet_requirev_state_t userauth_list_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_password_ex() */
+ libssh2_nonblocking_states userauth_pswd_state;
+ unsigned char *userauth_pswd_data;
+ unsigned char userauth_pswd_data0;
+ size_t userauth_pswd_data_len;
+ char *userauth_pswd_newpw;
+ int userauth_pswd_newpw_len;
+ packet_requirev_state_t userauth_pswd_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_hostbased_fromfile_ex() */
+ libssh2_nonblocking_states userauth_host_state;
+ unsigned char *userauth_host_data;
+ size_t userauth_host_data_len;
+ unsigned char *userauth_host_packet;
+ size_t userauth_host_packet_len;
+ unsigned char *userauth_host_method;
+ size_t userauth_host_method_len;
+ unsigned char *userauth_host_s;
+ packet_requirev_state_t userauth_host_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_publickey_fromfile_ex() */
+ libssh2_nonblocking_states userauth_pblc_state;
+ unsigned char *userauth_pblc_data;
+ size_t userauth_pblc_data_len;
+ unsigned char *userauth_pblc_packet;
+ size_t userauth_pblc_packet_len;
+ unsigned char *userauth_pblc_method;
+ size_t userauth_pblc_method_len;
+ unsigned char *userauth_pblc_s;
+ unsigned char *userauth_pblc_b;
+ packet_requirev_state_t userauth_pblc_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_keyboard_interactive_ex() */
+ libssh2_nonblocking_states userauth_kybd_state;
+ unsigned char *userauth_kybd_data;
+ size_t userauth_kybd_data_len;
+ unsigned char *userauth_kybd_packet;
+ size_t userauth_kybd_packet_len;
+ unsigned int userauth_kybd_auth_name_len;
+ char *userauth_kybd_auth_name;
+ unsigned userauth_kybd_auth_instruction_len;
+ char *userauth_kybd_auth_instruction;
+ unsigned int userauth_kybd_num_prompts;
+ int userauth_kybd_auth_failure;
+ LIBSSH2_USERAUTH_KBDINT_PROMPT *userauth_kybd_prompts;
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE *userauth_kybd_responses;
+ packet_requirev_state_t userauth_kybd_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_open_ex() */
+ libssh2_nonblocking_states open_state;
+ packet_requirev_state_t open_packet_requirev_state;
+ LIBSSH2_CHANNEL *open_channel;
+ unsigned char *open_packet;
+ size_t open_packet_len;
+ unsigned char *open_data;
+ size_t open_data_len;
+ uint32_t open_local_channel;
+
+ /* State variables used in libssh2_channel_direct_tcpip_ex() */
+ libssh2_nonblocking_states direct_state;
+ unsigned char *direct_message;
+ size_t direct_host_len;
+ size_t direct_shost_len;
+ size_t direct_message_len;
+
+ /* State variables used in libssh2_channel_forward_listen_ex() */
+ libssh2_nonblocking_states fwdLstn_state;
+ unsigned char *fwdLstn_packet;
+ uint32_t fwdLstn_host_len;
+ uint32_t fwdLstn_packet_len;
+ packet_requirev_state_t fwdLstn_packet_requirev_state;
+
+ /* State variables used in libssh2_publickey_init() */
+ libssh2_nonblocking_states pkeyInit_state;
+ LIBSSH2_PUBLICKEY *pkeyInit_pkey;
+ LIBSSH2_CHANNEL *pkeyInit_channel;
+ unsigned char *pkeyInit_data;
+ size_t pkeyInit_data_len;
+ /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */
+ unsigned char pkeyInit_buffer[19];
+ size_t pkeyInit_buffer_sent; /* how much of buffer that has been sent */
+
+ /* State variables used in libssh2_packet_add() */
+ libssh2_nonblocking_states packAdd_state;
+ LIBSSH2_CHANNEL *packAdd_channelp; /* keeper of the channel during EAGAIN
+ states */
+ packet_queue_listener_state_t packAdd_Qlstn_state;
+ packet_x11_open_state_t packAdd_x11open_state;
+
+ /* State variables used in fullpacket() */
+ libssh2_nonblocking_states fullpacket_state;
+ int fullpacket_macstate;
+ size_t fullpacket_payload_len;
+ int fullpacket_packet_type;
+
+ /* State variables used in libssh2_sftp_init() */
+ libssh2_nonblocking_states sftpInit_state;
+ LIBSSH2_SFTP *sftpInit_sftp;
+ LIBSSH2_CHANNEL *sftpInit_channel;
+ unsigned char sftpInit_buffer[9]; /* sftp_header(5){excludes request_id}
+ + version_id(4) */
+ int sftpInit_sent; /* number of bytes from the buffer that have been
+ sent */
+
+ /* State variables used in libssh2_scp_recv() / libssh_scp_recv2() */
+ libssh2_nonblocking_states scpRecv_state;
+ unsigned char *scpRecv_command;
+ size_t scpRecv_command_len;
+ unsigned char scpRecv_response[LIBSSH2_SCP_RESPONSE_BUFLEN];
+ size_t scpRecv_response_len;
+ long scpRecv_mode;
+#if defined(HAVE_LONGLONG) && defined(HAVE_STRTOLL)
+ /* we have the type and we can parse such numbers */
+ long long scpRecv_size;
+#define scpsize_strtol strtoll
+#elif defined(HAVE_STRTOI64)
+ __int64 scpRecv_size;
+#define scpsize_strtol _strtoi64
+#else
+ long scpRecv_size;
+#define scpsize_strtol strtol
+#endif
+ long scpRecv_mtime;
+ long scpRecv_atime;
+ LIBSSH2_CHANNEL *scpRecv_channel;
+
+ /* State variables used in libssh2_scp_send_ex() */
+ libssh2_nonblocking_states scpSend_state;
+ unsigned char *scpSend_command;
+ size_t scpSend_command_len;
+ unsigned char scpSend_response[LIBSSH2_SCP_RESPONSE_BUFLEN];
+ size_t scpSend_response_len;
+ LIBSSH2_CHANNEL *scpSend_channel;
+
+ /* Keepalive variables used by keepalive.c. */
+ int keepalive_interval;
+ int keepalive_want_reply;
+ time_t keepalive_last_sent;
+};
+
+/* session.state bits */
+#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001
+#define LIBSSH2_STATE_NEWKEYS 0x00000002
+#define LIBSSH2_STATE_AUTHENTICATED 0x00000004
+#define LIBSSH2_STATE_KEX_ACTIVE 0x00000008
+
+/* session.flag helpers */
+#ifdef MSG_NOSIGNAL
+#define LIBSSH2_SOCKET_SEND_FLAGS(session) \
+ (((session)->flag.sigpipe) ? 0 : MSG_NOSIGNAL)
+#define LIBSSH2_SOCKET_RECV_FLAGS(session) \
+ (((session)->flag.sigpipe) ? 0 : MSG_NOSIGNAL)
+#else
+/* If MSG_NOSIGNAL isn't defined we're SOL on blocking SIGPIPE */
+#define LIBSSH2_SOCKET_SEND_FLAGS(session) 0
+#define LIBSSH2_SOCKET_RECV_FLAGS(session) 0
+#endif
+
+/* --------- */
+
+/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional
+ methods via .so/.dll */
+
+struct _LIBSSH2_KEX_METHOD
+{
+ const char *name;
+
+ /* Key exchange, populates session->* and returns 0 on success, non-0 on error */
+ int (*exchange_keys) (LIBSSH2_SESSION * session,
+ key_exchange_state_low_t * key_state);
+
+ long flags;
+};
+
+struct _LIBSSH2_HOSTKEY_METHOD
+{
+ const char *name;
+ unsigned long hash_len;
+
+ int (*init) (LIBSSH2_SESSION * session, const unsigned char *hostkey_data,
+ size_t hostkey_data_len, void **abstract);
+ int (*initPEM) (LIBSSH2_SESSION * session, const char *privkeyfile,
+ unsigned const char *passphrase, void **abstract);
+ int (*initPEMFromMemory) (LIBSSH2_SESSION * session,
+ const char *privkeyfiledata, size_t privkeyfiledata_len,
+ unsigned const char *passphrase, void **abstract);
+ int (*sig_verify) (LIBSSH2_SESSION * session, const unsigned char *sig,
+ size_t sig_len, const unsigned char *m,
+ size_t m_len, void **abstract);
+ int (*signv) (LIBSSH2_SESSION * session, unsigned char **signature,
+ size_t *signature_len, int veccount,
+ const struct iovec datavec[], void **abstract);
+ int (*encrypt) (LIBSSH2_SESSION * session, unsigned char **dst,
+ size_t *dst_len, const unsigned char *src,
+ size_t src_len, void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
+};
+
+struct _LIBSSH2_CRYPT_METHOD
+{
+ const char *name;
+
+ int blocksize;
+
+ /* iv and key sizes (-1 for variable length) */
+ int iv_len;
+ int secret_len;
+
+ long flags;
+
+ int (*init) (LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method, unsigned char *iv,
+ int *free_iv, unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract);
+ int (*crypt) (LIBSSH2_SESSION * session, unsigned char *block,
+ size_t blocksize, void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
+
+ _libssh2_cipher_type(algo);
+};
+
+struct _LIBSSH2_COMP_METHOD
+{
+ const char *name;
+ int compress; /* 1 if it does compress, 0 if it doesn't */
+ int use_in_auth; /* 1 if compression should be used in userauth */
+ int (*init) (LIBSSH2_SESSION *session, int compress, void **abstract);
+ int (*comp) (LIBSSH2_SESSION *session,
+ unsigned char *dest,
+ size_t *dest_len,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract);
+ int (*decomp) (LIBSSH2_SESSION *session,
+ unsigned char **dest,
+ size_t *dest_len,
+ size_t payload_limit,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, int compress, void **abstract);
+};
+
+#ifdef LIBSSH2DEBUG
+void _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format,
+ ...);
+#else
+#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__GNUC__)
+/* C99 supported and also by older GCC */
+#define _libssh2_debug(x,y,z,...) do {} while (0)
+#else
+/* no gcc and not C99, do static and hopefully inline */
+static inline void
+_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
+{
+ (void)session;
+ (void)context;
+ (void)format;
+}
+#endif
+#endif
+
+#define LIBSSH2_SOCKET_UNKNOWN 1
+#define LIBSSH2_SOCKET_CONNECTED 0
+#define LIBSSH2_SOCKET_DISCONNECTED -1
+
+/* Initial packet state, prior to MAC check */
+#define LIBSSH2_MAC_UNCONFIRMED 1
+/* When MAC type is "none" (proto initiation phase) all packets are deemed "confirmed" */
+#define LIBSSH2_MAC_CONFIRMED 0
+/* Something very bad is going on */
+#define LIBSSH2_MAC_INVALID -1
+
+/* Flags for _libssh2_error_flags */
+/* Error message is allocated on the heap */
+#define LIBSSH2_ERR_FLAG_DUP 1
+
+/* SSH Packet Types -- Defined by internet draft */
+/* Transport Layer */
+#define SSH_MSG_DISCONNECT 1
+#define SSH_MSG_IGNORE 2
+#define SSH_MSG_UNIMPLEMENTED 3
+#define SSH_MSG_DEBUG 4
+#define SSH_MSG_SERVICE_REQUEST 5
+#define SSH_MSG_SERVICE_ACCEPT 6
+
+#define SSH_MSG_KEXINIT 20
+#define SSH_MSG_NEWKEYS 21
+
+/* diffie-hellman-group1-sha1 */
+#define SSH_MSG_KEXDH_INIT 30
+#define SSH_MSG_KEXDH_REPLY 31
+
+/* diffie-hellman-group-exchange-sha1 and diffie-hellman-group-exchange-sha256 */
+#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD 30
+#define SSH_MSG_KEX_DH_GEX_REQUEST 34
+#define SSH_MSG_KEX_DH_GEX_GROUP 31
+#define SSH_MSG_KEX_DH_GEX_INIT 32
+#define SSH_MSG_KEX_DH_GEX_REPLY 33
+
+/* User Authentication */
+#define SSH_MSG_USERAUTH_REQUEST 50
+#define SSH_MSG_USERAUTH_FAILURE 51
+#define SSH_MSG_USERAUTH_SUCCESS 52
+#define SSH_MSG_USERAUTH_BANNER 53
+
+/* "public key" method */
+#define SSH_MSG_USERAUTH_PK_OK 60
+/* "password" method */
+#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60
+/* "keyboard-interactive" method */
+#define SSH_MSG_USERAUTH_INFO_REQUEST 60
+#define SSH_MSG_USERAUTH_INFO_RESPONSE 61
+
+/* Channels */
+#define SSH_MSG_GLOBAL_REQUEST 80
+#define SSH_MSG_REQUEST_SUCCESS 81
+#define SSH_MSG_REQUEST_FAILURE 82
+
+#define SSH_MSG_CHANNEL_OPEN 90
+#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91
+#define SSH_MSG_CHANNEL_OPEN_FAILURE 92
+#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93
+#define SSH_MSG_CHANNEL_DATA 94
+#define SSH_MSG_CHANNEL_EXTENDED_DATA 95
+#define SSH_MSG_CHANNEL_EOF 96
+#define SSH_MSG_CHANNEL_CLOSE 97
+#define SSH_MSG_CHANNEL_REQUEST 98
+#define SSH_MSG_CHANNEL_SUCCESS 99
+#define SSH_MSG_CHANNEL_FAILURE 100
+
+/* Error codes returned in SSH_MSG_CHANNEL_OPEN_FAILURE message
+ (see RFC4254) */
+#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1
+#define SSH_OPEN_CONNECT_FAILED 2
+#define SSH_OPEN_UNKNOWN_CHANNELTYPE 3
+#define SSH_OPEN_RESOURCE_SHORTAGE 4
+
+ssize_t _libssh2_recv(libssh2_socket_t socket, void *buffer,
+ size_t length, int flags, void **abstract);
+ssize_t _libssh2_send(libssh2_socket_t socket, const void *buffer,
+ size_t length, int flags, void **abstract);
+
+#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when
+ waiting for more data to arrive */
+
+
+int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
+ key_exchange_state_t * state);
+
+/* Let crypt.c/hostkey.c expose their method structs */
+const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
+const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void);
+
+/* pem.c */
+int _libssh2_pem_parse(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ FILE * fp, unsigned char **data, unsigned int *datalen);
+int _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ const char *filedata, size_t filedata_len,
+ unsigned char **data, unsigned int *datalen);
+int _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen);
+int _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
+ unsigned char **i, unsigned int *ilen);
+
+/* global.c */
+void _libssh2_init_if_needed (void);
+
+
+#define ARRAY_SIZE(a) (sizeof ((a)) / sizeof ((a)[0]))
+
+/* define to output the libssh2_int64_t type in a *printf() */
+#if defined( __BORLANDC__ ) || defined( _MSC_VER ) || defined( __MINGW32__ )
+#define LIBSSH2_INT64_T_FORMAT "I64d"
+#else
+#define LIBSSH2_INT64_T_FORMAT "lld"
+#endif
+
+#endif /* LIBSSH2_H */
diff --git a/libs/libssh2/src/mac.c b/libs/libssh2/src/mac.c
new file mode 100644
index 0000000000..5ec26eb3b4
--- /dev/null
+++ b/libs/libssh2/src/mac.c
@@ -0,0 +1,414 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include "mac.h"
+
+#ifdef LIBSSH2_MAC_NONE
+/* mac_none_MAC
+ * Minimalist MAC: No MAC
+ */
+static int
+mac_none_MAC(LIBSSH2_SESSION * session, unsigned char *buf,
+ uint32_t seqno, const unsigned char *packet,
+ uint32_t packet_len, const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ return 0;
+}
+
+
+
+
+static LIBSSH2_MAC_METHOD mac_method_none = {
+ "none",
+ 0,
+ 0,
+ NULL,
+ mac_none_MAC,
+ NULL
+};
+#endif /* LIBSSH2_MAC_NONE */
+
+/* mac_method_common_init
+ * Initialize simple mac methods
+ */
+static int
+mac_method_common_init(LIBSSH2_SESSION * session, unsigned char *key,
+ int *free_key, void **abstract)
+{
+ *abstract = key;
+ *free_key = 0;
+ (void) session;
+
+ return 0;
+}
+
+
+
+/* mac_method_common_dtor
+ * Cleanup simple mac methods
+ */
+static int
+mac_method_common_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ if (*abstract) {
+ LIBSSH2_FREE(session, *abstract);
+ }
+ *abstract = NULL;
+
+ return 0;
+}
+
+
+
+#if LIBSSH2_HMAC_SHA512
+/* mac_method_hmac_sha512_hash
+ * Calculate hash using full sha512 value
+ */
+static int
+mac_method_hmac_sha2_512_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_sha512_init(&ctx, *abstract, 64);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if (addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_512 = {
+ "hmac-sha2-512",
+ 64,
+ 64,
+ mac_method_common_init,
+ mac_method_hmac_sha2_512_hash,
+ mac_method_common_dtor,
+};
+#endif
+
+
+
+#if LIBSSH2_HMAC_SHA256
+/* mac_method_hmac_sha256_hash
+ * Calculate hash using full sha256 value
+ */
+static int
+mac_method_hmac_sha2_256_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_sha256_init(&ctx, *abstract, 32);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if (addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_256 = {
+ "hmac-sha2-256",
+ 32,
+ 32,
+ mac_method_common_init,
+ mac_method_hmac_sha2_256_hash,
+ mac_method_common_dtor,
+};
+#endif
+
+
+
+
+/* mac_method_hmac_sha1_hash
+ * Calculate hash using full sha1 value
+ */
+static int
+mac_method_hmac_sha1_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_sha1_init(&ctx, *abstract, 20);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if (addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1 = {
+ "hmac-sha1",
+ 20,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_sha1_hash,
+ mac_method_common_dtor,
+};
+
+/* mac_method_hmac_sha1_96_hash
+ * Calculate hash using first 96 bits of sha1 value
+ */
+static int
+mac_method_hmac_sha1_96_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ unsigned char temp[SHA_DIGEST_LENGTH];
+
+ mac_method_hmac_sha1_hash(session, temp, seqno, packet, packet_len,
+ addtl, addtl_len, abstract);
+ memcpy(buf, (char *) temp, 96 / 8);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1_96 = {
+ "hmac-sha1-96",
+ 12,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_sha1_96_hash,
+ mac_method_common_dtor,
+};
+
+#if LIBSSH2_MD5
+/* mac_method_hmac_md5_hash
+ * Calculate hash using full md5 value
+ */
+static int
+mac_method_hmac_md5_hash(LIBSSH2_SESSION * session, unsigned char *buf,
+ uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_md5_init(&ctx, *abstract, 16);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if (addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_md5 = {
+ "hmac-md5",
+ 16,
+ 16,
+ mac_method_common_init,
+ mac_method_hmac_md5_hash,
+ mac_method_common_dtor,
+};
+
+/* mac_method_hmac_md5_96_hash
+ * Calculate hash using first 96 bits of md5 value
+ */
+static int
+mac_method_hmac_md5_96_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ unsigned char temp[MD5_DIGEST_LENGTH];
+ mac_method_hmac_md5_hash(session, temp, seqno, packet, packet_len,
+ addtl, addtl_len, abstract);
+ memcpy(buf, (char *) temp, 96 / 8);
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_md5_96 = {
+ "hmac-md5-96",
+ 12,
+ 16,
+ mac_method_common_init,
+ mac_method_hmac_md5_96_hash,
+ mac_method_common_dtor,
+};
+#endif /* LIBSSH2_MD5 */
+
+#if LIBSSH2_HMAC_RIPEMD
+/* mac_method_hmac_ripemd160_hash
+ * Calculate hash using ripemd160 value
+ */
+static int
+mac_method_hmac_ripemd160_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len,
+ void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_ripemd160_init(&ctx, *abstract, 20);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if (addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160 = {
+ "hmac-ripemd160",
+ 20,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_ripemd160_hash,
+ mac_method_common_dtor,
+};
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160_openssh_com = {
+ "hmac-ripemd160@openssh.com",
+ 20,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_ripemd160_hash,
+ mac_method_common_dtor,
+};
+#endif /* LIBSSH2_HMAC_RIPEMD */
+
+static const LIBSSH2_MAC_METHOD *mac_methods[] = {
+#if LIBSSH2_HMAC_SHA256
+ &mac_method_hmac_sha2_256,
+#endif
+#if LIBSSH2_HMAC_SHA512
+ &mac_method_hmac_sha2_512,
+#endif
+ &mac_method_hmac_sha1,
+ &mac_method_hmac_sha1_96,
+#if LIBSSH2_MD5
+ &mac_method_hmac_md5,
+ &mac_method_hmac_md5_96,
+#endif
+#if LIBSSH2_HMAC_RIPEMD
+ &mac_method_hmac_ripemd160,
+ &mac_method_hmac_ripemd160_openssh_com,
+#endif /* LIBSSH2_HMAC_RIPEMD */
+#ifdef LIBSSH2_MAC_NONE
+ &mac_method_none,
+#endif /* LIBSSH2_MAC_NONE */
+ NULL
+};
+
+const LIBSSH2_MAC_METHOD **
+_libssh2_mac_methods(void)
+{
+ return mac_methods;
+}
diff --git a/libs/libssh2/src/mac.h b/libs/libssh2/src/mac.h
new file mode 100644
index 0000000000..66d3e61011
--- /dev/null
+++ b/libs/libssh2/src/mac.h
@@ -0,0 +1,67 @@
+#ifndef __LIBSSH2_MAC_H
+#define __LIBSSH2_MAC_H
+
+/* Copyright (C) 2009-2010 by Daniel Stenberg
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+#include "libssh2_priv.h"
+
+struct _LIBSSH2_MAC_METHOD
+{
+ const char *name;
+
+ /* The length of a given MAC packet */
+ int mac_len;
+
+ /* integrity key length */
+ int key_len;
+
+ /* Message Authentication Code Hashing algo */
+ int (*init) (LIBSSH2_SESSION * session, unsigned char *key, int *free_key,
+ void **abstract);
+ int (*hash) (LIBSSH2_SESSION * session, unsigned char *buf,
+ uint32_t seqno, const unsigned char *packet,
+ uint32_t packet_len, const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
+};
+
+typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
+
+const LIBSSH2_MAC_METHOD **_libssh2_mac_methods(void);
+
+#endif /* __LIBSSH2_MAC_H */
diff --git a/libs/libssh2/src/mbedtls.c b/libs/libssh2/src/mbedtls.c
new file mode 100644
index 0000000000..1d181e18ff
--- /dev/null
+++ b/libs/libssh2/src/mbedtls.c
@@ -0,0 +1,606 @@
+#include "libssh2_priv.h"
+
+#ifdef LIBSSH2_MBEDTLS /* compile only if we build with mbedtls */
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Generic functions
+ */
+
+void
+_libssh2_mbedtls_init(void)
+{
+ int ret;
+
+ mbedtls_entropy_init(&_libssh2_mbedtls_entropy);
+ mbedtls_ctr_drbg_init(&_libssh2_mbedtls_ctr_drbg);
+
+ ret = mbedtls_ctr_drbg_seed(&_libssh2_mbedtls_ctr_drbg,
+ mbedtls_entropy_func,
+ &_libssh2_mbedtls_entropy, NULL, 0);
+ if (ret != 0)
+ mbedtls_ctr_drbg_free(&_libssh2_mbedtls_ctr_drbg);
+}
+
+void
+_libssh2_mbedtls_free(void)
+{
+ mbedtls_ctr_drbg_free(&_libssh2_mbedtls_ctr_drbg);
+ mbedtls_entropy_free(&_libssh2_mbedtls_entropy);
+}
+
+int
+_libssh2_mbedtls_random(unsigned char *buf, int len)
+{
+ int ret;
+ ret = mbedtls_ctr_drbg_random(&_libssh2_mbedtls_ctr_drbg, buf, len);
+ return ret == 0 ? 0 : -1;
+}
+
+static void
+_libssh2_mbedtls_safe_free(void *buf, int len)
+{
+#ifndef LIBSSH2_CLEAR_MEMORY
+ (void)len;
+#endif
+
+ if (!buf)
+ return;
+
+#ifdef LIBSSH2_CLEAR_MEMORY
+ if (len > 0)
+ memset(buf, 0, len);
+#endif
+
+ mbedtls_free(buf);
+}
+
+int
+_libssh2_mbedtls_cipher_init(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv,
+ unsigned char *secret,
+ int encrypt)
+{
+ const mbedtls_cipher_info_t *cipher_info;
+ int ret, op;
+
+ if (!ctx)
+ return -1;
+
+ op = encrypt == 0 ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT;
+
+ cipher_info = mbedtls_cipher_info_from_type(algo);
+ if(!cipher_info)
+ return -1;
+
+ mbedtls_cipher_init(ctx);
+ ret = mbedtls_cipher_setup(ctx, cipher_info);
+ if(!ret)
+ ret = mbedtls_cipher_setkey(ctx, secret, cipher_info->key_bitlen, op);
+
+ if(!ret)
+ ret = mbedtls_cipher_set_iv(ctx, iv, cipher_info->iv_size);
+
+ return ret == 0 ? 0 : -1;
+}
+
+int
+_libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt,
+ unsigned char *block,
+ size_t blocklen)
+{
+ int ret;
+ unsigned char *output;
+ size_t osize, olen, finish_olen;
+
+ (void) encrypt;
+ (void) algo;
+
+ osize = blocklen+mbedtls_cipher_get_block_size(ctx);
+
+ output = (unsigned char *)mbedtls_calloc(osize, sizeof(char));
+ if(output)
+ {
+ ret = mbedtls_cipher_reset(ctx);
+
+ if(!ret)
+ ret = mbedtls_cipher_update(ctx, block, blocklen, output, &olen);
+
+ if(!ret)
+ ret = mbedtls_cipher_finish(ctx, output + olen, &finish_olen);
+
+ if (!ret) {
+ olen += finish_olen;
+ memcpy(block, output, olen);
+ }
+
+ _libssh2_mbedtls_safe_free(output, osize);
+ }
+ else
+ ret = -1;
+
+ return ret == 0 ? 0 : -1;
+}
+
+void
+_libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx)
+{
+ mbedtls_cipher_free(ctx);
+}
+
+
+int
+_libssh2_mbedtls_hash_init(mbedtls_md_context_t *ctx,
+ mbedtls_md_type_t mdtype,
+ const unsigned char *key, unsigned long keylen)
+{
+ const mbedtls_md_info_t *md_info;
+ int ret, hmac;
+
+ md_info = mbedtls_md_info_from_type(mdtype);
+ if(!md_info)
+ return 0;
+
+ hmac = key == NULL ? 0 : 1;
+
+ mbedtls_md_init(ctx);
+ ret = mbedtls_md_setup(ctx, md_info, hmac);
+ if (!ret){
+ if (hmac)
+ ret = mbedtls_md_hmac_starts(ctx, key, keylen);
+ else
+ ret = mbedtls_md_starts(ctx);
+ }
+
+ return ret == 0 ? 1 : 0;
+}
+
+int
+_libssh2_mbedtls_hash_final(mbedtls_md_context_t *ctx, unsigned char *hash)
+{
+ int ret;
+
+ ret = mbedtls_md_finish(ctx, hash);
+ mbedtls_md_free(ctx);
+
+ return ret == 0 ? 0 : -1;
+}
+
+int
+_libssh2_mbedtls_hash(const unsigned char *data, unsigned long datalen,
+ mbedtls_md_type_t mdtype, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info;
+ int ret;
+
+ md_info = mbedtls_md_info_from_type(mdtype);
+ if(!md_info)
+ return 0;
+
+ ret = mbedtls_md(md_info, data, datalen, hash);
+
+ return ret == 0 ? 0 : -1;
+}
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: BigNumber functions
+ */
+
+_libssh2_bn *
+_libssh2_mbedtls_bignum_init(void)
+{
+ _libssh2_bn *bignum;
+
+ bignum = (_libssh2_bn *)mbedtls_calloc(1, sizeof(_libssh2_bn));
+ if (bignum) {
+ mbedtls_mpi_init(bignum);
+ }
+
+ return bignum;
+}
+
+int
+_libssh2_mbedtls_bignum_random(_libssh2_bn *bn, int bits, int top, int bottom)
+{
+ size_t len;
+ int err;
+ int i;
+
+ if (!bn || bits <= 0)
+ return -1;
+
+ len = (bits + 7) >> 3;
+ err = mbedtls_mpi_fill_random(bn, len, mbedtls_ctr_drbg_random, &_libssh2_mbedtls_ctr_drbg);
+ if (err)
+ return -1;
+
+ /* Zero unsued bits above the most significant bit*/
+ for(i=len*8-1;bits<=i;--i) {
+ err = mbedtls_mpi_set_bit(bn, i, 0);
+ if (err)
+ return -1;
+ }
+
+ /* If `top` is -1, the most significant bit of the random number can be zero.
+ If top is 0, the most significant bit of the random number is set to 1,
+ and if top is 1, the two most significant bits of the number will be set
+ to 1, so that the product of two such random numbers will always have 2*bits length.
+ */
+ for(i=0;i<=top;++i) {
+ err = mbedtls_mpi_set_bit(bn, bits-i-1, 1);
+ if (err)
+ return -1;
+ }
+
+ /* make odd by setting first bit in least significant byte */
+ if (bottom) {
+ err = mbedtls_mpi_set_bit(bn, 0, 1);
+ if (err)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: RSA functions
+ */
+
+int
+_libssh2_mbedtls_rsa_new(libssh2_rsa_ctx **rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata,
+ unsigned long coefflen)
+{
+ int ret;
+ libssh2_rsa_ctx *ctx;
+
+ ctx = (libssh2_rsa_ctx *) mbedtls_calloc(1, sizeof(libssh2_rsa_ctx));
+ if (ctx != NULL) {
+ mbedtls_rsa_init(ctx, MBEDTLS_RSA_PKCS_V15, 0);
+ }
+ else
+ return -1;
+
+ if( (ret = mbedtls_mpi_read_binary(&(ctx->E), edata, elen) ) != 0 ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->N), ndata, nlen) ) != 0 )
+ {
+ ret = -1;
+ }
+
+ if (!ret)
+ {
+ ctx->len = mbedtls_mpi_size(&(ctx->N));
+ }
+
+ if (!ret && ddata)
+ {
+ if( (ret = mbedtls_mpi_read_binary(&(ctx->D) , ddata, dlen) ) != 0 ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->P) , pdata, plen) ) != 0 ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->Q) , qdata, qlen) ) != 0 ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->DP), e1data, e1len) ) != 0 ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->DQ), e2data, e2len) ) != 0 ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->QP), coeffdata, coefflen) ) != 0 )
+ {
+ ret = -1;
+ }
+ ret = mbedtls_rsa_check_privkey(ctx);
+ }
+ else if (!ret)
+ {
+ ret = mbedtls_rsa_check_pubkey(ctx);
+ }
+
+ if (ret && ctx) {
+ _libssh2_mbedtls_rsa_free(ctx);
+ ctx = NULL;
+ }
+ *rsa = ctx;
+ return ret;
+}
+
+int
+_libssh2_mbedtls_rsa_new_private(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase)
+{
+ int ret;
+ mbedtls_pk_context pkey;
+
+ *rsa = (libssh2_rsa_ctx *) LIBSSH2_ALLOC(session, sizeof(libssh2_rsa_ctx));
+ if (*rsa == NULL)
+ return -1;
+
+ mbedtls_rsa_init(*rsa, MBEDTLS_RSA_PKCS_V15, 0);
+ mbedtls_pk_init(&pkey);
+
+ ret = mbedtls_pk_parse_keyfile(&pkey, filename, (char *)passphrase);
+ if( ret != 0 || mbedtls_pk_get_type(&pkey) != MBEDTLS_PK_RSA)
+ {
+ mbedtls_pk_free(&pkey);
+ mbedtls_rsa_free(*rsa);
+ LIBSSH2_FREE(session, *rsa);
+ *rsa = NULL;
+ return -1;
+ }
+
+ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(pkey);
+ mbedtls_rsa_copy(*rsa, pk_rsa);
+ mbedtls_pk_free(&pkey);
+
+ return 0;
+}
+
+int
+_libssh2_mbedtls_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ int ret;
+ mbedtls_pk_context pkey;
+
+ *rsa = (libssh2_rsa_ctx *) mbedtls_calloc( 1, sizeof( libssh2_rsa_ctx ) );
+ if (*rsa == NULL)
+ return -1;
+
+ mbedtls_pk_init(&pkey);
+
+ ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)filedata,
+ filedata_len, NULL, 0);
+ if( ret != 0 || mbedtls_pk_get_type(&pkey) != MBEDTLS_PK_RSA)
+ {
+ mbedtls_pk_free(&pkey);
+ mbedtls_rsa_free(*rsa);
+ LIBSSH2_FREE(session, *rsa);
+ *rsa = NULL;
+ return -1;
+ }
+
+ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(pkey);
+ mbedtls_rsa_copy(*rsa, pk_rsa);
+ mbedtls_pk_free(&pkey);
+
+ return 0;
+}
+
+int
+_libssh2_mbedtls_rsa_sha1_verify(libssh2_rsa_ctx *rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m,
+ unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ int ret;
+
+ ret = _libssh2_mbedtls_hash(m, m_len, MBEDTLS_MD_SHA1, hash);
+ if(ret)
+ return -1; /* failure */
+
+ ret = mbedtls_rsa_pkcs1_verify(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC,
+ MBEDTLS_MD_SHA1, SHA_DIGEST_LENGTH, hash, sig);
+
+ return (ret == 0) ? 0 : -1;
+}
+
+int
+_libssh2_mbedtls_rsa_sha1_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ int ret;
+ unsigned char *sig;
+ unsigned int sig_len;
+
+ (void)hash_len;
+
+ sig_len = rsa->len;
+ sig = LIBSSH2_ALLOC(session, sig_len);
+ if (!sig) {
+ return -1;
+ }
+
+ ret = mbedtls_rsa_pkcs1_sign(rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE,
+ MBEDTLS_MD_SHA1, SHA_DIGEST_LENGTH,
+ hash, sig);
+ if (ret) {
+ LIBSSH2_FREE(session, sig);
+ return -1;
+ }
+
+ *signature = sig;
+ *signature_len = sig_len;
+
+ return (ret == 0) ? 0 : -1;
+}
+
+void
+_libssh2_mbedtls_rsa_free(libssh2_rsa_ctx *ctx)
+{
+ mbedtls_rsa_free(ctx);
+ mbedtls_free(ctx);
+}
+
+static unsigned char *
+gen_publickey_from_rsa(LIBSSH2_SESSION *session,
+ mbedtls_rsa_context *rsa,
+ size_t *keylen)
+{
+ int e_bytes, n_bytes;
+ unsigned long len;
+ unsigned char* key;
+ unsigned char* p;
+
+ e_bytes = mbedtls_mpi_size(&rsa->E);
+ n_bytes = mbedtls_mpi_size(&rsa->N);
+
+ /* Key form is "ssh-rsa" + e + n. */
+ len = 4 + 7 + 4 + e_bytes + 4 + n_bytes;
+
+ key = LIBSSH2_ALLOC(session, len);
+ if (!key) {
+ return NULL;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ _libssh2_htonu32(p, 7); /* Key type. */
+ p += 4;
+ memcpy(p, "ssh-rsa", 7);
+ p += 7;
+
+ _libssh2_htonu32(p, e_bytes);
+ p += 4;
+ mbedtls_mpi_write_binary(&rsa->E, p, e_bytes);
+
+ _libssh2_htonu32(p, n_bytes);
+ p += 4;
+ mbedtls_mpi_write_binary(&rsa->N, p, n_bytes);
+
+ *keylen = (size_t)(p - key);
+ return key;
+}
+
+static int
+_libssh2_mbedtls_pub_priv_key(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ mbedtls_pk_context *pkey)
+{
+ unsigned char *key = NULL, *mth = NULL;
+ size_t keylen = 0, mthlen = 0;
+ int ret;
+
+ if( mbedtls_pk_get_type(pkey) != MBEDTLS_PK_RSA )
+ {
+ mbedtls_pk_free(pkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Key type not supported");
+ }
+
+ // write method
+ mthlen = 7;
+ mth = LIBSSH2_ALLOC(session, mthlen);
+ if (mth) {
+ memcpy(mth, "ssh-rsa", mthlen);
+ } else {
+ ret = -1;
+ }
+
+ mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*pkey);
+ key = gen_publickey_from_rsa(session, rsa, &keylen);
+ if (key == NULL) {
+ ret = -1;
+ }
+
+ // write output
+ if (ret) {
+ if (mth)
+ LIBSSH2_FREE(session, mth);
+ if (key)
+ LIBSSH2_FREE(session, key);
+ } else {
+ *method = mth;
+ *method_len = mthlen;
+ *pubkeydata = key;
+ *pubkeydata_len = keylen;
+ }
+
+ return ret;
+}
+
+int
+_libssh2_mbedtls_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+ mbedtls_pk_context pkey;
+ char buf[1024];
+ int ret;
+
+ mbedtls_pk_init(&pkey);
+ ret = mbedtls_pk_parse_keyfile(&pkey, privatekey, passphrase);
+ if( ret != 0 )
+ {
+ mbedtls_strerror(ret, (char *)buf, sizeof(buf));
+ mbedtls_pk_free(&pkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE, buf);
+ }
+
+ ret = _libssh2_mbedtls_pub_priv_key(session, method, method_len,
+ pubkeydata, pubkeydata_len, &pkey);
+
+ mbedtls_pk_free(&pkey);
+
+ return ret;
+}
+
+int
+_libssh2_mbedtls_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ mbedtls_pk_context pkey;
+ char buf[1024];
+ int ret;
+
+ mbedtls_pk_init(&pkey);
+ ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)privatekeydata,
+ privatekeydata_len, NULL, 0);
+ if( ret != 0 )
+ {
+ mbedtls_strerror(ret, (char *)buf, sizeof(buf));
+ mbedtls_pk_free(&pkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE, buf);
+ }
+
+ ret = _libssh2_mbedtls_pub_priv_key(session, method, method_len,
+ pubkeydata, pubkeydata_len, &pkey);
+
+ mbedtls_pk_free(&pkey);
+
+ return ret;
+}
+
+void _libssh2_init_aes_ctr(void)
+{
+ /* no implementation */
+}
+#endif /* LIBSSH2_MBEDTLS */
diff --git a/libs/libssh2/src/mbedtls.h b/libs/libssh2/src/mbedtls.h
new file mode 100644
index 0000000000..248583ed3c
--- /dev/null
+++ b/libs/libssh2/src/mbedtls.h
@@ -0,0 +1,371 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <mbedtls/platform.h>
+#include <mbedtls/md.h>
+#include <mbedtls/rsa.h>
+#include <mbedtls/bignum.h>
+#include <mbedtls/cipher.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/pk.h>
+#include <mbedtls/error.h>
+
+/* Define which features are supported. */
+#define LIBSSH2_MD5 1
+
+#define LIBSSH2_HMAC_RIPEMD 1
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#define LIBSSH2_AES 1
+#define LIBSSH2_AES_CTR 1
+#define LIBSSH2_BLOWFISH 1
+#define LIBSSH2_RC4 1
+#define LIBSSH2_CAST 0
+#define LIBSSH2_3DES 1
+
+#define LIBSSH2_RSA 1
+#define LIBSSH2_DSA 0
+
+#define MD5_DIGEST_LENGTH 16
+#define SHA_DIGEST_LENGTH 20
+#define SHA256_DIGEST_LENGTH 32
+#define SHA512_DIGEST_LENGTH 64
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Global context handles
+ */
+
+mbedtls_entropy_context _libssh2_mbedtls_entropy;
+mbedtls_ctr_drbg_context _libssh2_mbedtls_ctr_drbg;
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Generic functions
+ */
+
+#define libssh2_crypto_init() \
+ _libssh2_mbedtls_init()
+#define libssh2_crypto_exit() \
+ _libssh2_mbedtls_free()
+
+#define _libssh2_random(buf, len) \
+ _libssh2_mbedtls_random(buf, len)
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: HMAC functions
+ */
+
+#define libssh2_hmac_ctx mbedtls_md_context_t
+
+#define libssh2_hmac_ctx_init(ctx)
+#define libssh2_hmac_cleanup(pctx) \
+ mbedtls_md_free(pctx)
+#define libssh2_hmac_update(ctx, data, datalen) \
+ mbedtls_md_hmac_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_hmac_final(ctx, hash) \
+ mbedtls_md_hmac_finish(&ctx, hash)
+
+#define libssh2_hmac_sha1_init(pctx, key, keylen) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA1, key, keylen)
+#define libssh2_hmac_md5_init(pctx, key, keylen) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_MD5, key, keylen)
+#define libssh2_hmac_ripemd160_init(pctx, key, keylen) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_RIPEMD160, key, keylen)
+#define libssh2_hmac_sha256_init(pctx, key, keylen) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA256, key, keylen)
+#define libssh2_hmac_sha512_init(pctx, key, keylen) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA512, key, keylen)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA1 functions
+ */
+
+#define libssh2_sha1_ctx mbedtls_md_context_t
+
+#define libssh2_sha1_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA1, NULL, 0)
+#define libssh2_sha1_update(ctx, data, datalen) \
+ mbedtls_md_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_sha1_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha1(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA1, hash)
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA256 functions
+ */
+
+#define libssh2_sha256_ctx mbedtls_md_context_t
+
+#define libssh2_sha256_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA256, NULL, 0)
+#define libssh2_sha256_update(ctx, data, datalen) \
+ mbedtls_md_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_sha256_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha256(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA256, hash)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA512 functions
+ */
+
+#define libssh2_sha512_ctx mbedtls_md_context_t
+
+#define libssh2_sha512_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA512, NULL, 0)
+#define libssh2_sha512_update(ctx, data, datalen) \
+ mbedtls_md_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_sha512_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha512(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA512, hash)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: MD5 functions
+ */
+
+#define libssh2_md5_ctx mbedtls_md_context_t
+
+#define libssh2_md5_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_MD5, NULL, 0)
+#define libssh2_md5_update(ctx, data, datalen) \
+ mbedtls_md_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_md5_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_md5(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_MD5, hash)
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: RSA structure
+ */
+
+#define libssh2_rsa_ctx mbedtls_rsa_context
+
+#define _libssh2_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len) \
+ _libssh2_mbedtls_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len)
+
+#define _libssh2_rsa_new_private(rsactx, s, filename, passphrase) \
+ _libssh2_mbedtls_rsa_new_private(rsactx, s, filename, passphrase)
+
+#define _libssh2_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_mbedtls_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase)
+
+#define _libssh2_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len) \
+ _libssh2_mbedtls_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len)
+
+#define _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len) \
+ _libssh2_mbedtls_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len)
+
+#define _libssh2_rsa_free(rsactx) \
+ _libssh2_mbedtls_rsa_free(rsactx)
+
+/*
+ * mbedTLS backend: Key functions
+ */
+
+#define _libssh2_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw) \
+ _libssh2_mbedtls_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw)
+#define _libssh2_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw) \
+ _libssh2_mbedtls_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw)
+
+
+ /*******************************************************************/
+/*
+ * mbedTLS backend: Cipher Context structure
+ */
+#define _libssh2_cipher_ctx mbedtls_cipher_context_t
+
+#define _libssh2_cipher_type(algo) mbedtls_cipher_type_t algo
+
+#define _libssh2_cipher_aes256ctr MBEDTLS_CIPHER_AES_256_CTR
+#define _libssh2_cipher_aes192ctr MBEDTLS_CIPHER_AES_192_CTR
+#define _libssh2_cipher_aes128ctr MBEDTLS_CIPHER_AES_128_CTR
+#define _libssh2_cipher_aes256 MBEDTLS_CIPHER_AES_256_CBC
+#define _libssh2_cipher_aes192 MBEDTLS_CIPHER_AES_192_CBC
+#define _libssh2_cipher_aes128 MBEDTLS_CIPHER_AES_128_CBC
+#define _libssh2_cipher_blowfish MBEDTLS_CIPHER_BLOWFISH_CBC
+#define _libssh2_cipher_arcfour MBEDTLS_CIPHER_ARC4_128
+#define _libssh2_cipher_cast5 MBEDTLS_CIPHER_NULL
+#define _libssh2_cipher_3des MBEDTLS_CIPHER_DES_EDE3_CBC
+
+/*
+ * mbedTLS backend: Cipher functions
+ */
+
+#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
+ _libssh2_mbedtls_cipher_init(ctx, type, iv, secret, encrypt)
+#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen) \
+ _libssh2_mbedtls_cipher_crypt(ctx, type, encrypt, block, blocklen)
+#define _libssh2_cipher_dtor(ctx) \
+ _libssh2_mbedtls_cipher_dtor(ctx)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: BigNumber Support
+ */
+
+#define _libssh2_bn_ctx int /* not used */
+#define _libssh2_bn_ctx_new() 0 /* not used */
+#define _libssh2_bn_ctx_free(bnctx) ((void)0) /* not used */
+
+#define _libssh2_bn mbedtls_mpi
+
+#define _libssh2_bn_init() \
+ _libssh2_mbedtls_bignum_init()
+#define _libssh2_bn_init_from_bin() \
+ _libssh2_mbedtls_bignum_init()
+#define _libssh2_bn_rand(bn, bits, top, bottom) \
+ _libssh2_mbedtls_bignum_random(bn, bits, top, bottom)
+#define _libssh2_bn_mod_exp(r, a, p, m, ctx) \
+ mbedtls_mpi_exp_mod(r, a, p, m, NULL)
+#define _libssh2_bn_set_word(bn, word) \
+ mbedtls_mpi_lset(bn, word)
+#define _libssh2_bn_from_bin(bn, len, bin) \
+ mbedtls_mpi_read_binary(bn, bin, len)
+#define _libssh2_bn_to_bin(bn, bin) \
+ mbedtls_mpi_write_binary(bn, bin, mbedtls_mpi_size(bn))
+#define _libssh2_bn_bytes(bn) \
+ mbedtls_mpi_size(bn)
+#define _libssh2_bn_bits(bn) \
+ mbedtls_mpi_bitlen(bn)
+#define _libssh2_bn_free(bn) \
+ mbedtls_mpi_free(bn)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: forward declarations
+ */
+void
+_libssh2_mbedtls_init(void);
+
+void
+_libssh2_mbedtls_free(void);
+
+int
+_libssh2_mbedtls_random(unsigned char *buf, int len);
+
+int
+_libssh2_mbedtls_cipher_init(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ unsigned char *iv,
+ unsigned char *secret,
+ int encrypt);
+int
+_libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ int encrypt,
+ unsigned char *block,
+ size_t blocklen);
+void
+_libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx);
+
+int
+_libssh2_mbedtls_hash_init(mbedtls_md_context_t *ctx,
+ mbedtls_md_type_t mdtype,
+ const unsigned char *key, unsigned long keylen);
+
+int
+_libssh2_mbedtls_hash_final(mbedtls_md_context_t *ctx, unsigned char *hash);
+int
+_libssh2_mbedtls_hash(const unsigned char *data, unsigned long datalen,
+ mbedtls_md_type_t mdtype, unsigned char *hash);
+
+_libssh2_bn *
+_libssh2_mbedtls_bignum_init(void);
+
+void
+_libssh2_mbedtls_bignum_free(_libssh2_bn *bn);
+
+int
+_libssh2_mbedtls_bignum_random(_libssh2_bn *bn, int bits, int top, int bottom);
+
+int
+_libssh2_mbedtls_rsa_new(libssh2_rsa_ctx **rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata,
+ unsigned long coefflen);
+
+int
+_libssh2_mbedtls_rsa_new_private(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase);
+
+int
+_libssh2_mbedtls_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+int
+_libssh2_mbedtls_rsa_sha1_verify(libssh2_rsa_ctx *rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m,
+ unsigned long m_len);
+int
+_libssh2_mbedtls_rsa_sha1_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len);
+void
+_libssh2_mbedtls_rsa_free(libssh2_rsa_ctx *rsa);
+
+int
+_libssh2_mbedtls_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase);
+int
+_libssh2_mbedtls_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase);
diff --git a/libs/libssh2/src/misc.c b/libs/libssh2/src/misc.c
new file mode 100644
index 0000000000..f7faae7b6a
--- /dev/null
+++ b/libs/libssh2/src/misc.c
@@ -0,0 +1,645 @@
+/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include "misc.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+int _libssh2_error_flags(LIBSSH2_SESSION* session, int errcode, const char* errmsg, int errflags)
+{
+ if (session->err_flags & LIBSSH2_ERR_FLAG_DUP)
+ LIBSSH2_FREE(session, (char *)session->err_msg);
+
+ session->err_code = errcode;
+ session->err_flags = 0;
+
+ if ((errmsg != NULL) && ((errflags & LIBSSH2_ERR_FLAG_DUP) != 0)) {
+ size_t len = strlen(errmsg);
+ char *copy = LIBSSH2_ALLOC(session, len + 1);
+ if (copy) {
+ memcpy(copy, errmsg, len + 1);
+ session->err_flags = LIBSSH2_ERR_FLAG_DUP;
+ session->err_msg = copy;
+ }
+ else
+ /* Out of memory: this code path is very unlikely */
+ session->err_msg = "former error forgotten (OOM)";
+ }
+ else
+ session->err_msg = errmsg;
+
+#ifdef LIBSSH2DEBUG
+ if((errcode == LIBSSH2_ERROR_EAGAIN) && !session->api_block_mode)
+ /* if this is EAGAIN and we're in non-blocking mode, don't generate
+ a debug output for this */
+ return errcode;
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR, "%d - %s", session->err_code,
+ session->err_msg);
+#endif
+
+ return errcode;
+}
+
+int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char* errmsg)
+{
+ return _libssh2_error_flags(session, errcode, errmsg, 0);
+}
+
+#ifdef WIN32
+static int wsa2errno(void)
+{
+ switch (WSAGetLastError()) {
+ case WSAEWOULDBLOCK:
+ return EAGAIN;
+
+ case WSAENOTSOCK:
+ return EBADF;
+
+ case WSAEINTR:
+ return EINTR;
+
+ default:
+ /* It is most important to ensure errno does not stay at EAGAIN
+ * when a different error occurs so just set errno to a generic
+ * error */
+ return EIO;
+ }
+}
+#endif
+
+/* _libssh2_recv
+ *
+ * Replacement for the standard recv, return -errno on failure.
+ */
+ssize_t
+_libssh2_recv(libssh2_socket_t sock, void *buffer, size_t length,
+ int flags, void **abstract)
+{
+ ssize_t rc;
+
+ (void) abstract;
+
+ rc = recv(sock, buffer, length, flags);
+#ifdef WIN32
+ if (rc < 0 )
+ return -wsa2errno();
+#elif defined(__VMS)
+ if (rc < 0 ){
+ if ( errno == EWOULDBLOCK )
+ return -EAGAIN;
+ else
+ return -errno;
+ }
+#else
+ if (rc < 0 ){
+ /* Sometimes the first recv() function call sets errno to ENOENT on
+ Solaris and HP-UX */
+ if ( errno == ENOENT )
+ return -EAGAIN;
+ else
+ return -errno;
+ }
+#endif
+ return rc;
+}
+
+/* _libssh2_send
+ *
+ * Replacement for the standard send, return -errno on failure.
+ */
+ssize_t
+_libssh2_send(libssh2_socket_t sock, const void *buffer, size_t length,
+ int flags, void **abstract)
+{
+ ssize_t rc;
+
+ (void) abstract;
+
+ rc = send(sock, buffer, length, flags);
+#ifdef WIN32
+ if (rc < 0 )
+ return -wsa2errno();
+#elif defined(__VMS)
+ if (rc < 0 ) {
+ if ( errno == EWOULDBLOCK )
+ return -EAGAIN;
+ else
+ return -errno;
+ }
+#else
+ if (rc < 0 )
+ return -errno;
+#endif
+ return rc;
+}
+
+/* libssh2_ntohu32
+ */
+unsigned int
+_libssh2_ntohu32(const unsigned char *buf)
+{
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+
+/* _libssh2_ntohu64
+ */
+libssh2_uint64_t
+_libssh2_ntohu64(const unsigned char *buf)
+{
+ unsigned long msl, lsl;
+
+ msl = ((libssh2_uint64_t)buf[0] << 24) | ((libssh2_uint64_t)buf[1] << 16)
+ | ((libssh2_uint64_t)buf[2] << 8) | (libssh2_uint64_t)buf[3];
+ lsl = ((libssh2_uint64_t)buf[4] << 24) | ((libssh2_uint64_t)buf[5] << 16)
+ | ((libssh2_uint64_t)buf[6] << 8) | (libssh2_uint64_t)buf[7];
+
+ return ((libssh2_uint64_t)msl <<32) | lsl;
+}
+
+/* _libssh2_htonu32
+ */
+void
+_libssh2_htonu32(unsigned char *buf, uint32_t value)
+{
+ buf[0] = (value >> 24) & 0xFF;
+ buf[1] = (value >> 16) & 0xFF;
+ buf[2] = (value >> 8) & 0xFF;
+ buf[3] = value & 0xFF;
+}
+
+/* _libssh2_store_u32
+ */
+void _libssh2_store_u32(unsigned char **buf, uint32_t value)
+{
+ _libssh2_htonu32(*buf, value);
+ *buf += sizeof(uint32_t);
+}
+
+/* _libssh2_store_str
+ */
+void _libssh2_store_str(unsigned char **buf, const char *str, size_t len)
+{
+ _libssh2_store_u32(buf, (uint32_t)len);
+ if(len) {
+ memcpy(*buf, str, len);
+ *buf += len;
+ }
+}
+
+/* Base64 Conversion */
+
+static const short base64_reverse_table[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/* libssh2_base64_decode
+ *
+ * Decode a base64 chunk and store it into a newly alloc'd buffer
+ */
+LIBSSH2_API int
+libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,
+ unsigned int *datalen, const char *src,
+ unsigned int src_len)
+{
+ unsigned char *s, *d;
+ short v;
+ int i = 0, len = 0;
+
+ *data = LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1);
+ d = (unsigned char *) *data;
+ if (!d) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for base64 decoding");
+ }
+
+ for(s = (unsigned char *) src; ((char *) s) < (src + src_len); s++) {
+ if ((v = base64_reverse_table[*s]) < 0)
+ continue;
+ switch (i % 4) {
+ case 0:
+ d[len] = (unsigned char)(v << 2);
+ break;
+ case 1:
+ d[len++] |= v >> 4;
+ d[len] = (unsigned char)(v << 4);
+ break;
+ case 2:
+ d[len++] |= v >> 2;
+ d[len] = (unsigned char)(v << 6);
+ break;
+ case 3:
+ d[len++] |= v;
+ break;
+ }
+ i++;
+ }
+ if ((i % 4) == 1) {
+ /* Invalid -- We have a byte which belongs exclusively to a partial
+ octet */
+ LIBSSH2_FREE(session, *data);
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid base64");
+ }
+
+ *datalen = len;
+ return 0;
+}
+
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * _libssh2_base64_encode()
+ *
+ * Returns the length of the newly created base64 string. The third argument
+ * is a pointer to an allocated area holding the base64 data. If something
+ * went wrong, 0 is returned.
+ *
+ */
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+ const char *inp, size_t insize, char **outptr)
+{
+ unsigned char ibuf[3];
+ unsigned char obuf[4];
+ int i;
+ int inputparts;
+ char *output;
+ char *base64data;
+ const char *indata = inp;
+
+ *outptr = NULL; /* set to NULL in case of failure before we reach the end */
+
+ if(0 == insize)
+ insize = strlen(indata);
+
+ base64data = output = LIBSSH2_ALLOC(session, insize*4/3+4);
+ if(NULL == output)
+ return 0;
+
+ while(insize > 0) {
+ for (i = inputparts = 0; i < 3; i++) {
+ if(insize > 0) {
+ inputparts++;
+ ibuf[i] = *indata;
+ indata++;
+ insize--;
+ }
+ else
+ ibuf[i] = 0;
+ }
+
+ obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2);
+ obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+ ((ibuf[1] & 0xF0) >> 4));
+ obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+ ((ibuf[2] & 0xC0) >> 6));
+ obuf[3] = (unsigned char) (ibuf[2] & 0x3F);
+
+ switch(inputparts) {
+ case 1: /* only one byte read */
+ snprintf(output, 5, "%c%c==",
+ table64[obuf[0]],
+ table64[obuf[1]]);
+ break;
+ case 2: /* two bytes read */
+ snprintf(output, 5, "%c%c%c=",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]]);
+ break;
+ default:
+ snprintf(output, 5, "%c%c%c%c",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]],
+ table64[obuf[3]] );
+ break;
+ }
+ output += 4;
+ }
+ *output=0;
+ *outptr = base64data; /* make it return the actual data memory */
+
+ return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
+LIBSSH2_API void
+libssh2_free(LIBSSH2_SESSION *session, void *ptr)
+{
+ LIBSSH2_FREE(session, ptr);
+}
+
+#ifdef LIBSSH2DEBUG
+#include <stdarg.h>
+
+LIBSSH2_API int
+libssh2_trace(LIBSSH2_SESSION * session, int bitmask)
+{
+ session->showmask = bitmask;
+ return 0;
+}
+
+LIBSSH2_API int
+libssh2_trace_sethandler(LIBSSH2_SESSION *session, void* handler_context,
+ libssh2_trace_handler_func callback)
+{
+ session->tracehandler = callback;
+ session->tracehandler_context = handler_context;
+ return 0;
+}
+
+void
+_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
+{
+ char buffer[1536];
+ int len, msglen, buflen = sizeof(buffer);
+ va_list vargs;
+ struct timeval now;
+ static int firstsec;
+ static const char *const contexts[] = {
+ "Unknown",
+ "Transport",
+ "Key Ex",
+ "Userauth",
+ "Conn",
+ "SCP",
+ "SFTP",
+ "Failure Event",
+ "Publickey",
+ "Socket",
+ };
+ const char* contexttext = contexts[0];
+ unsigned int contextindex;
+
+ if (!(session->showmask & context)) {
+ /* no such output asked for */
+ return;
+ }
+
+ /* Find the first matching context string for this message */
+ for (contextindex = 0; contextindex < ARRAY_SIZE(contexts);
+ contextindex++) {
+ if ((context & (1 << contextindex)) != 0) {
+ contexttext = contexts[contextindex];
+ break;
+ }
+ }
+
+ _libssh2_gettimeofday(&now, NULL);
+ if(!firstsec) {
+ firstsec = now.tv_sec;
+ }
+ now.tv_sec -= firstsec;
+
+ len = snprintf(buffer, buflen, "[libssh2] %d.%06d %s: ",
+ (int)now.tv_sec, (int)now.tv_usec, contexttext);
+
+ if (len >= buflen)
+ msglen = buflen - 1;
+ else {
+ buflen -= len;
+ msglen = len;
+ va_start(vargs, format);
+ len = vsnprintf(buffer + msglen, buflen, format, vargs);
+ va_end(vargs);
+ msglen += len < buflen ? len : buflen - 1;
+ }
+
+ if (session->tracehandler)
+ (session->tracehandler)(session, session->tracehandler_context, buffer,
+ msglen);
+ else
+ fprintf(stderr, "%s\n", buffer);
+}
+
+#else
+LIBSSH2_API int
+libssh2_trace(LIBSSH2_SESSION * session, int bitmask)
+{
+ (void) session;
+ (void) bitmask;
+ return 0;
+}
+
+LIBSSH2_API int
+libssh2_trace_sethandler(LIBSSH2_SESSION *session, void* handler_context,
+ libssh2_trace_handler_func callback)
+{
+ (void) session;
+ (void) handler_context;
+ (void) callback;
+ return 0;
+}
+#endif
+
+/* init the list head */
+void _libssh2_list_init(struct list_head *head)
+{
+ head->first = head->last = NULL;
+}
+
+/* add a node to the list */
+void _libssh2_list_add(struct list_head *head,
+ struct list_node *entry)
+{
+ /* store a pointer to the head */
+ entry->head = head;
+
+ /* we add this entry at the "top" so it has no next */
+ entry->next = NULL;
+
+ /* make our prev point to what the head thinks is last */
+ entry->prev = head->last;
+
+ /* and make head's last be us now */
+ head->last = entry;
+
+ /* make sure our 'prev' node points to us next */
+ if(entry->prev)
+ entry->prev->next = entry;
+ else
+ head->first = entry;
+}
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head)
+{
+ return head->first;
+}
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node)
+{
+ return node->next;
+}
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node)
+{
+ return node->prev;
+}
+
+/* remove this node from the list */
+void _libssh2_list_remove(struct list_node *entry)
+{
+ if(entry->prev)
+ entry->prev->next = entry->next;
+ else
+ entry->head->first = entry->next;
+
+ if(entry->next)
+ entry->next->prev = entry->prev;
+ else
+ entry->head->last = entry->prev;
+}
+
+#if 0
+/* insert a node before the given 'after' entry */
+void _libssh2_list_insert(struct list_node *after, /* insert before this */
+ struct list_node *entry)
+{
+ /* 'after' is next to 'entry' */
+ bentry->next = after;
+
+ /* entry's prev is then made to be the prev after current has */
+ entry->prev = after->prev;
+
+ /* the node that is now before 'entry' was previously before 'after'
+ and must be made to point to 'entry' correctly */
+ if(entry->prev)
+ entry->prev->next = entry;
+ else
+ /* there was no node before this, so we make sure we point the head
+ pointer to this node */
+ after->head->first = entry;
+
+ /* after's prev entry points back to entry */
+ after->prev = entry;
+
+ /* after's next entry is still the same as before */
+
+ /* entry's head is the same as after's */
+ entry->head = after->head;
+}
+
+#endif
+
+/* this define is defined in misc.h for the correct platforms */
+#ifdef LIBSSH2_GETTIMEOFDAY_WIN32
+/*
+ * gettimeofday
+ * Implementation according to:
+ * The Open Group Base Specifications Issue 6
+ * IEEE Std 1003.1, 2004 Edition
+ */
+
+/*
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Contributed by:
+ * Danny Smith <dannysmith@users.sourceforge.net>
+ */
+
+/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
+#define _W32_FT_OFFSET (116444736000000000)
+
+int __cdecl _libssh2_gettimeofday(struct timeval *tp, void *tzp)
+ {
+ union {
+ unsigned __int64 ns100; /*time since 1 Jan 1601 in 100ns units */
+ FILETIME ft;
+ } _now;
+ (void)tzp;
+ if(tp)
+ {
+ GetSystemTimeAsFileTime (&_now.ft);
+ tp->tv_usec=(long)((_now.ns100 / 10) % 1000000 );
+ tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000);
+ }
+ /* Always return 0 as per Open Group Base Specifications Issue 6.
+ Do not set errno on error. */
+ return 0;
+}
+
+
+#endif
+
+void *_libssh2_calloc(LIBSSH2_SESSION* session, size_t size)
+{
+ void *p = LIBSSH2_ALLOC(session, size);
+ if(p) {
+ memset(p, 0, size);
+ }
+ return p;
+}
diff --git a/libs/libssh2/src/misc.h b/libs/libssh2/src/misc.h
new file mode 100644
index 0000000000..54ae5461d6
--- /dev/null
+++ b/libs/libssh2/src/misc.h
@@ -0,0 +1,96 @@
+#ifndef __LIBSSH2_MISC_H
+#define __LIBSSH2_MISC_H
+/* Copyright (c) 2009-2014 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+struct list_head {
+ struct list_node *last;
+ struct list_node *first;
+};
+
+struct list_node {
+ struct list_node *next;
+ struct list_node *prev;
+ struct list_head *head;
+};
+
+int _libssh2_error_flags(LIBSSH2_SESSION* session, int errcode, const char* errmsg, int errflags);
+int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char* errmsg);
+
+void _libssh2_list_init(struct list_head *head);
+
+/* add a node last in the list */
+void _libssh2_list_add(struct list_head *head,
+ struct list_node *entry);
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head);
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node);
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node);
+
+/* remove this node from the list */
+void _libssh2_list_remove(struct list_node *entry);
+
+size_t _libssh2_base64_encode(struct _LIBSSH2_SESSION *session,
+ const char *inp, size_t insize, char **outptr);
+
+unsigned int _libssh2_ntohu32(const unsigned char *buf);
+libssh2_uint64_t _libssh2_ntohu64(const unsigned char *buf);
+void _libssh2_htonu32(unsigned char *buf, uint32_t val);
+void _libssh2_store_u32(unsigned char **buf, uint32_t value);
+void _libssh2_store_str(unsigned char **buf, const char *str, size_t len);
+void *_libssh2_calloc(LIBSSH2_SESSION* session, size_t size);
+
+#if defined(LIBSSH2_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
+/* provide a private one */
+#undef HAVE_GETTIMEOFDAY
+int __cdecl _libssh2_gettimeofday(struct timeval *tp, void *tzp);
+#define HAVE_LIBSSH2_GETTIMEOFDAY
+#define LIBSSH2_GETTIMEOFDAY_WIN32 /* enable the win32 implementation */
+#else
+#ifdef HAVE_GETTIMEOFDAY
+#define _libssh2_gettimeofday(x,y) gettimeofday(x,y)
+#define HAVE_LIBSSH2_GETTIMEOFDAY
+#endif
+#endif
+
+#endif /* _LIBSSH2_MISC_H */
diff --git a/libs/libssh2/src/openssl.c b/libs/libssh2/src/openssl.c
new file mode 100644
index 0000000000..4f63ef92bc
--- /dev/null
+++ b/libs/libssh2/src/openssl.c
@@ -0,0 +1,1192 @@
+/* Copyright (C) 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
+ *
+ * Author: Simon Josefsson
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#ifdef LIBSSH2_OPENSSL /* compile only if we build with openssl */
+
+#include <string.h>
+
+#ifndef EVP_MAX_BLOCK_LENGTH
+#define EVP_MAX_BLOCK_LENGTH 32
+#endif
+
+int
+_libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata, unsigned long coefflen)
+{
+ BIGNUM * e;
+ BIGNUM * n;
+ BIGNUM * d = 0;
+ BIGNUM * p = 0;
+ BIGNUM * q = 0;
+ BIGNUM * dmp1 = 0;
+ BIGNUM * dmq1 = 0;
+ BIGNUM * iqmp = 0;
+
+ e = BN_new();
+ BN_bin2bn(edata, elen, e);
+
+ n = BN_new();
+ BN_bin2bn(ndata, nlen, n);
+
+ if (ddata) {
+ d = BN_new();
+ BN_bin2bn(ddata, dlen, d);
+
+ p = BN_new();
+ BN_bin2bn(pdata, plen, p);
+
+ q = BN_new();
+ BN_bin2bn(qdata, qlen, q);
+
+ dmp1 = BN_new();
+ BN_bin2bn(e1data, e1len, dmp1);
+
+ dmq1 = BN_new();
+ BN_bin2bn(e2data, e2len, dmq1);
+
+ iqmp = BN_new();
+ BN_bin2bn(coeffdata, coefflen, iqmp);
+ }
+
+ *rsa = RSA_new();
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_key(*rsa, n, e, d);
+#else
+ (*rsa)->e = e;
+ (*rsa)->n = n;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_factors(*rsa, p, q);
+#else
+ (*rsa)->p = p;
+ (*rsa)->q = q;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_crt_params(*rsa, dmp1, dmq1, iqmp);
+#else
+ (*rsa)->dmp1 = dmp1;
+ (*rsa)->dmq1 = dmq1;
+ (*rsa)->iqmp = iqmp;
+#endif
+ return 0;
+}
+
+int
+_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ int ret;
+
+ if (_libssh2_sha1(m, m_len, hash))
+ return -1; /* failure */
+ ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
+ (unsigned char *) sig, sig_len, rsactx);
+ return (ret == 1) ? 0 : -1;
+}
+
+#if LIBSSH2_DSA
+int
+_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx,
+ const unsigned char *p,
+ unsigned long p_len,
+ const unsigned char *q,
+ unsigned long q_len,
+ const unsigned char *g,
+ unsigned long g_len,
+ const unsigned char *y,
+ unsigned long y_len,
+ const unsigned char *x, unsigned long x_len)
+{
+ BIGNUM * p_bn;
+ BIGNUM * q_bn;
+ BIGNUM * g_bn;
+ BIGNUM * pub_key;
+ BIGNUM * priv_key = NULL;
+
+ p_bn = BN_new();
+ BN_bin2bn(p, p_len, p_bn);
+
+ q_bn = BN_new();
+ BN_bin2bn(q, q_len, q_bn);
+
+ g_bn = BN_new();
+ BN_bin2bn(g, g_len, g_bn);
+
+ pub_key = BN_new();
+ BN_bin2bn(y, y_len, pub_key);
+
+ if (x_len) {
+ priv_key = BN_new();
+ BN_bin2bn(x, x_len, priv_key);
+ }
+
+ *dsactx = DSA_new();
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_set0_pqg(*dsactx, p_bn, q_bn, g_bn);
+#else
+ (*dsactx)->p = p_bn;
+ (*dsactx)->g = g_bn;
+ (*dsactx)->q = q_bn;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_set0_key(*dsactx, pub_key, priv_key);
+#else
+ (*dsactx)->pub_key = pub_key;
+ (*dsactx)->priv_key = priv_key;
+#endif
+ return 0;
+}
+
+int
+_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx,
+ const unsigned char *sig,
+ const unsigned char *m, unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ DSA_SIG * dsasig;
+ BIGNUM * r;
+ BIGNUM * s;
+ int ret = -1;
+
+ r = BN_new();
+ BN_bin2bn(sig, 20, r);
+ s = BN_new();
+ BN_bin2bn(sig + 20, 20, s);
+
+ dsasig = DSA_SIG_new();
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_SIG_set0(dsasig, r, s);
+#else
+ dsasig->r = r;
+ dsasig->s = s;
+#endif
+ if (!_libssh2_sha1(m, m_len, hash))
+ /* _libssh2_sha1() succeeded */
+ ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, dsasig, dsactx);
+
+ DSA_SIG_free(dsasig);
+
+ return (ret == 1) ? 0 : -1;
+}
+#endif /* LIBSSH_DSA */
+
+int
+_libssh2_cipher_init(_libssh2_cipher_ctx * h,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv, unsigned char *secret, int encrypt)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *h = EVP_CIPHER_CTX_new();
+ return !EVP_CipherInit(*h, algo(), secret, iv, encrypt);
+#else
+ EVP_CIPHER_CTX_init(h);
+ return !EVP_CipherInit(h, algo(), secret, iv, encrypt);
+#endif
+}
+
+int
+_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt, unsigned char *block, size_t blocksize)
+{
+ unsigned char buf[EVP_MAX_BLOCK_LENGTH];
+ int ret;
+ (void) algo;
+ (void) encrypt;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ ret = EVP_Cipher(*ctx, buf, block, blocksize);
+#else
+ ret = EVP_Cipher(ctx, buf, block, blocksize);
+#endif
+ if (ret == 1) {
+ memcpy(block, buf, blocksize);
+ }
+ return ret == 1 ? 0 : 1;
+}
+
+#if LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR)
+
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+
+typedef struct
+{
+ AES_KEY key;
+ EVP_CIPHER_CTX *aes_ctx;
+ unsigned char ctr[AES_BLOCK_SIZE];
+} aes_ctr_ctx;
+
+static int
+aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc) /* init key */
+{
+ /*
+ * variable "c" is leaked from this scope, but is later freed
+ * in aes_ctr_cleanup
+ */
+ aes_ctr_ctx *c;
+ const EVP_CIPHER *aes_cipher;
+ (void) enc;
+
+ switch (EVP_CIPHER_CTX_key_length(ctx)) {
+ case 16:
+ aes_cipher = EVP_aes_128_ecb();
+ break;
+ case 24:
+ aes_cipher = EVP_aes_192_ecb();
+ break;
+ case 32:
+ aes_cipher = EVP_aes_256_ecb();
+ break;
+ default:
+ return 0;
+ }
+
+ c = malloc(sizeof(*c));
+ if (c == NULL)
+ return 0;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ c->aes_ctx = EVP_CIPHER_CTX_new();
+#else
+ c->aes_ctx = malloc(sizeof(EVP_CIPHER_CTX));
+#endif
+ if (c->aes_ctx == NULL) {
+ free(c);
+ return 0;
+ }
+
+ if (EVP_EncryptInit(c->aes_ctx, aes_cipher, key, NULL) != 1) {
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_CIPHER_CTX_free(c->aes_ctx);
+#else
+ free(c->aes_ctx);
+#endif
+ free(c);
+ return 0;
+ }
+
+ EVP_CIPHER_CTX_set_padding(c->aes_ctx, 0);
+
+ memcpy(c->ctr, iv, AES_BLOCK_SIZE);
+
+ EVP_CIPHER_CTX_set_app_data(ctx, c);
+
+ return 1;
+}
+
+static int
+aes_ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in,
+ size_t inl) /* encrypt/decrypt data */
+{
+ aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx);
+ unsigned char b1[AES_BLOCK_SIZE];
+ size_t i = 0;
+ int outlen = 0;
+
+ if (inl != 16) /* libssh2 only ever encrypt one block */
+ return 0;
+
+ if (c == NULL) {
+ return 0;
+ }
+
+/*
+ To encrypt a packet P=P1||P2||...||Pn (where P1, P2, ..., Pn are each
+ blocks of length L), the encryptor first encrypts <X> with <cipher>
+ to obtain a block B1. The block B1 is then XORed with P1 to generate
+ the ciphertext block C1. The counter X is then incremented
+*/
+
+ if (EVP_EncryptUpdate(c->aes_ctx, b1, &outlen, c->ctr, AES_BLOCK_SIZE) != 1) {
+ return 0;
+ }
+
+ for (i = 0; i < 16; i++)
+ *out++ = *in++ ^ b1[i];
+
+ i = 15;
+ while (c->ctr[i]++ == 0xFF) {
+ if (i == 0)
+ break;
+ i--;
+ }
+
+ return 1;
+}
+
+static int
+aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) /* cleanup ctx */
+{
+ aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx);
+
+ if (c == NULL) {
+ return 1;
+ }
+
+ if (c->aes_ctx != NULL) {
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_CIPHER_CTX_free(c->aes_ctx);
+#else
+ _libssh2_cipher_dtor(c->aes_ctx);
+ free(c->aes_ctx);
+#endif
+ }
+
+ free(c);
+
+ return 1;
+}
+
+static const EVP_CIPHER *
+make_ctr_evp (size_t keylen, EVP_CIPHER *aes_ctr_cipher, int type)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ aes_ctr_cipher = EVP_CIPHER_meth_new(type, 16, keylen);
+ if (aes_ctr_cipher) {
+ EVP_CIPHER_meth_set_iv_length(aes_ctr_cipher, 16);
+ EVP_CIPHER_meth_set_init(aes_ctr_cipher, aes_ctr_init);
+ EVP_CIPHER_meth_set_do_cipher(aes_ctr_cipher, aes_ctr_do_cipher);
+ EVP_CIPHER_meth_set_cleanup(aes_ctr_cipher, aes_ctr_cleanup);
+ }
+#else
+ aes_ctr_cipher->nid = type;
+ aes_ctr_cipher->block_size = 16;
+ aes_ctr_cipher->key_len = keylen;
+ aes_ctr_cipher->iv_len = 16;
+ aes_ctr_cipher->init = aes_ctr_init;
+ aes_ctr_cipher->do_cipher = aes_ctr_do_cipher;
+ aes_ctr_cipher->cleanup = aes_ctr_cleanup;
+#endif
+
+ return aes_ctr_cipher;
+}
+
+const EVP_CIPHER *
+_libssh2_EVP_aes_128_ctr(void)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ static EVP_CIPHER * aes_ctr_cipher;
+ return !aes_ctr_cipher?
+ make_ctr_evp (16, aes_ctr_cipher, NID_aes_128_ctr) : aes_ctr_cipher;
+#else
+ static EVP_CIPHER aes_ctr_cipher;
+ return !aes_ctr_cipher.key_len?
+ make_ctr_evp (16, &aes_ctr_cipher, 0) : &aes_ctr_cipher;
+#endif
+}
+
+const EVP_CIPHER *
+_libssh2_EVP_aes_192_ctr(void)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ static EVP_CIPHER * aes_ctr_cipher;
+ return !aes_ctr_cipher?
+ make_ctr_evp (24, aes_ctr_cipher, NID_aes_192_ctr) : aes_ctr_cipher;
+#else
+ static EVP_CIPHER aes_ctr_cipher;
+ return !aes_ctr_cipher.key_len?
+ make_ctr_evp (24, &aes_ctr_cipher, 0) : &aes_ctr_cipher;
+#endif
+}
+
+const EVP_CIPHER *
+_libssh2_EVP_aes_256_ctr(void)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ static EVP_CIPHER * aes_ctr_cipher;
+ return !aes_ctr_cipher?
+ make_ctr_evp (32, aes_ctr_cipher, NID_aes_256_ctr) : aes_ctr_cipher;
+#else
+ static EVP_CIPHER aes_ctr_cipher;
+ return !aes_ctr_cipher.key_len?
+ make_ctr_evp (32, &aes_ctr_cipher, 0) : &aes_ctr_cipher;
+#endif
+}
+
+void _libssh2_init_aes_ctr(void)
+{
+ _libssh2_EVP_aes_128_ctr();
+ _libssh2_EVP_aes_192_ctr();
+ _libssh2_EVP_aes_256_ctr();
+}
+
+#else
+void _libssh2_init_aes_ctr(void) {}
+#endif /* LIBSSH2_AES_CTR */
+
+/* TODO: Optionally call a passphrase callback specified by the
+ * calling program
+ */
+static int
+passphrase_cb(char *buf, int size, int rwflag, char *passphrase)
+{
+ int passphrase_len = strlen(passphrase);
+ (void) rwflag;
+
+ if (passphrase_len > (size - 1)) {
+ passphrase_len = size - 1;
+ }
+ memcpy(buf, passphrase, passphrase_len);
+ buf[passphrase_len] = '\0';
+
+ return passphrase_len;
+}
+
+typedef void * (*pem_read_bio_func)(BIO *, void **, pem_password_cb *,
+ void * u);
+
+static int
+read_private_key_from_memory(void ** key_ctx,
+ pem_read_bio_func read_private_key,
+ const char * filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ BIO * bp;
+
+ *key_ctx = NULL;
+
+ bp = BIO_new_mem_buf((char *)filedata, filedata_len);
+ if (!bp) {
+ return -1;
+ }
+ *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb,
+ (void *) passphrase);
+
+ BIO_free(bp);
+ return (*key_ctx) ? 0 : -1;
+}
+
+static int
+read_private_key_from_file(void ** key_ctx,
+ pem_read_bio_func read_private_key,
+ const char * filename,
+ unsigned const char *passphrase)
+{
+ BIO * bp;
+
+ *key_ctx = NULL;
+
+ bp = BIO_new_file(filename, "r");
+ if (!bp) {
+ return -1;
+ }
+
+ *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb,
+ (void *) passphrase);
+
+ BIO_free(bp);
+ return (*key_ctx) ? 0 : -1;
+}
+
+int
+_libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ pem_read_bio_func read_rsa =
+ (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey;
+ (void) session;
+
+ _libssh2_init_if_needed();
+
+ return read_private_key_from_memory((void **) rsa, read_rsa,
+ filedata, filedata_len, passphrase);
+}
+
+int
+_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ pem_read_bio_func read_rsa =
+ (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey;
+ (void) session;
+
+ _libssh2_init_if_needed ();
+
+ return read_private_key_from_file((void **) rsa, read_rsa,
+ filename, passphrase);
+}
+
+#if LIBSSH2_DSA
+int
+_libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ pem_read_bio_func read_dsa =
+ (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey;
+ (void) session;
+
+ _libssh2_init_if_needed();
+
+ return read_private_key_from_memory((void **) dsa, read_dsa,
+ filedata, filedata_len, passphrase);
+}
+
+int
+_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ pem_read_bio_func read_dsa =
+ (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey;
+ (void) session;
+
+ _libssh2_init_if_needed ();
+
+ return read_private_key_from_file((void **) dsa, read_dsa,
+ filename, passphrase);
+}
+#endif /* LIBSSH_DSA */
+
+int
+_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature, size_t *signature_len)
+{
+ int ret;
+ unsigned char *sig;
+ unsigned int sig_len;
+
+ sig_len = RSA_size(rsactx);
+ sig = LIBSSH2_ALLOC(session, sig_len);
+
+ if (!sig) {
+ return -1;
+ }
+
+ ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx);
+
+ if (!ret) {
+ LIBSSH2_FREE(session, sig);
+ return -1;
+ }
+
+ *signature = sig;
+ *signature_len = sig_len;
+
+ return 0;
+}
+
+#if LIBSSH2_DSA
+int
+_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
+ const unsigned char *hash,
+ unsigned long hash_len, unsigned char *signature)
+{
+ DSA_SIG *sig;
+ const BIGNUM * r;
+ const BIGNUM * s;
+ int r_len, s_len;
+ (void) hash_len;
+
+ sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx);
+ if (!sig) {
+ return -1;
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_SIG_get0(sig, &r, &s);
+#else
+ r = sig->r;
+ s = sig->s;
+#endif
+ r_len = BN_num_bytes(r);
+ if (r_len < 1 || r_len > 20) {
+ DSA_SIG_free(sig);
+ return -1;
+ }
+ s_len = BN_num_bytes(s);
+ if (s_len < 1 || s_len > 20) {
+ DSA_SIG_free(sig);
+ return -1;
+ }
+
+ memset(signature, 0, 40);
+
+ BN_bn2bin(r, signature + (20 - r_len));
+ BN_bn2bin(s, signature + 20 + (20 - s_len));
+
+ DSA_SIG_free(sig);
+
+ return 0;
+}
+#endif /* LIBSSH_DSA */
+
+int
+_libssh2_sha1_init(libssh2_sha1_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if (*ctx == NULL)
+ return 0;
+
+ if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha1")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"));
+#endif
+}
+
+int
+_libssh2_sha1(const unsigned char *message, unsigned long len,
+ unsigned char *out)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_MD_CTX * ctx = EVP_MD_CTX_new();
+
+ if (ctx == NULL)
+ return 1; /* error */
+
+ if (EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"))) {
+ EVP_DigestUpdate(ctx, message, len);
+ EVP_DigestFinal(ctx, out, NULL);
+ EVP_MD_CTX_free(ctx);
+ return 0; /* success */
+ }
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if (EVP_DigestInit(&ctx, EVP_get_digestbyname("sha1"))) {
+ EVP_DigestUpdate(&ctx, message, len);
+ EVP_DigestFinal(&ctx, out, NULL);
+ return 0; /* success */
+ }
+#endif
+ return 1; /* error */
+}
+
+int
+_libssh2_sha256_init(libssh2_sha256_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if (*ctx == NULL)
+ return 0;
+
+ if (EVP_DigestInit(*ctx, EVP_get_digestbyname("sha256")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"));
+#endif
+}
+
+int
+_libssh2_sha256(const unsigned char *message, unsigned long len,
+ unsigned char *out)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_MD_CTX * ctx = EVP_MD_CTX_new();
+
+ if (ctx == NULL)
+ return 1; /* error */
+
+ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"))) {
+ EVP_DigestUpdate(ctx, message, len);
+ EVP_DigestFinal(ctx, out, NULL);
+ EVP_MD_CTX_free(ctx);
+ return 0; /* success */
+ }
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha256"))) {
+ EVP_DigestUpdate(&ctx, message, len);
+ EVP_DigestFinal(&ctx, out, NULL);
+ return 0; /* success */
+ }
+#endif
+ return 1; /* error */
+}
+
+int
+_libssh2_md5_init(libssh2_md5_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if (*ctx == NULL)
+ return 0;
+
+ if (EVP_DigestInit(*ctx, EVP_get_digestbyname("md5")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("md5"));
+#endif
+}
+
+static unsigned char *
+write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes)
+{
+ unsigned char *p = buf;
+
+ /* Left space for bn size which will be written below. */
+ p += 4;
+
+ *p = 0;
+ BN_bn2bin(bn, p + 1);
+ if (!(*(p + 1) & 0x80)) {
+ memmove(p, p + 1, --bn_bytes);
+ }
+ _libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */
+
+ return p + bn_bytes;
+}
+
+static unsigned char *
+gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa,
+ size_t *key_len)
+{
+ int e_bytes, n_bytes;
+ unsigned long len;
+ unsigned char* key;
+ unsigned char* p;
+ const BIGNUM * e;
+ const BIGNUM * n;
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_get0_key(rsa, &n, &e, NULL);
+#else
+ e = rsa->e;
+ n = rsa->n;
+#endif
+ e_bytes = BN_num_bytes(e) + 1;
+ n_bytes = BN_num_bytes(n) + 1;
+
+ /* Key form is "ssh-rsa" + e + n. */
+ len = 4 + 7 + 4 + e_bytes + 4 + n_bytes;
+
+ key = LIBSSH2_ALLOC(session, len);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ _libssh2_htonu32(p, 7); /* Key type. */
+ p += 4;
+ memcpy(p, "ssh-rsa", 7);
+ p += 7;
+
+ p = write_bn(p, e, e_bytes);
+ p = write_bn(p, n, n_bytes);
+
+ *key_len = (size_t)(p - key);
+ return key;
+}
+
+#if LIBSSH2_DSA
+static unsigned char *
+gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa,
+ size_t *key_len)
+{
+ int p_bytes, q_bytes, g_bytes, k_bytes;
+ unsigned long len;
+ unsigned char* key;
+ unsigned char* p;
+
+ const BIGNUM * p_bn;
+ const BIGNUM * q;
+ const BIGNUM * g;
+ const BIGNUM * pub_key;
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_get0_pqg(dsa, &p_bn, &q, &g);
+#else
+ p_bn = dsa->p;
+ q = dsa->q;
+ g = dsa->g;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_get0_key(dsa, &pub_key, NULL);
+#else
+ pub_key = dsa->pub_key;
+#endif
+ p_bytes = BN_num_bytes(p_bn) + 1;
+ q_bytes = BN_num_bytes(q) + 1;
+ g_bytes = BN_num_bytes(g) + 1;
+ k_bytes = BN_num_bytes(pub_key) + 1;
+
+ /* Key form is "ssh-dss" + p + q + g + pub_key. */
+ len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes;
+
+ key = LIBSSH2_ALLOC(session, len);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ _libssh2_htonu32(p, 7); /* Key type. */
+ p += 4;
+ memcpy(p, "ssh-dss", 7);
+ p += 7;
+
+ p = write_bn(p, p_bn, p_bytes);
+ p = write_bn(p, q, q_bytes);
+ p = write_bn(p, g, g_bytes);
+ p = write_bn(p, pub_key, k_bytes);
+
+ *key_len = (size_t)(p - key);
+ return key;
+}
+#endif /* LIBSSH_DSA */
+
+static int
+gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ EVP_PKEY *pk)
+{
+ RSA* rsa = NULL;
+ unsigned char* key;
+ unsigned char* method_buf = NULL;
+ size_t key_len;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from RSA private key envelop");
+
+ rsa = EVP_PKEY_get1_RSA(pk);
+ if (rsa == NULL) {
+ /* Assume memory allocation error... what else could it be ? */
+ goto __alloc_error;
+ }
+
+ method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */
+ if (method_buf == NULL) {
+ goto __alloc_error;
+ }
+
+ key = gen_publickey_from_rsa(session, rsa, &key_len);
+ if (key == NULL) {
+ goto __alloc_error;
+ }
+ RSA_free(rsa);
+
+ memcpy(method_buf, "ssh-rsa", 7);
+ *method = method_buf;
+ *method_len = 7;
+ *pubkeydata = key;
+ *pubkeydata_len = key_len;
+ return 0;
+
+ __alloc_error:
+ if (rsa != NULL) {
+ RSA_free(rsa);
+ }
+ if (method_buf != NULL) {
+ LIBSSH2_FREE(session, method_buf);
+ }
+
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+}
+
+#if LIBSSH2_DSA
+static int
+gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ EVP_PKEY *pk)
+{
+ DSA* dsa = NULL;
+ unsigned char* key;
+ unsigned char* method_buf = NULL;
+ size_t key_len;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from DSA private key envelop");
+
+ dsa = EVP_PKEY_get1_DSA(pk);
+ if (dsa == NULL) {
+ /* Assume memory allocation error... what else could it be ? */
+ goto __alloc_error;
+ }
+
+ method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */
+ if (method_buf == NULL) {
+ goto __alloc_error;
+ }
+
+ key = gen_publickey_from_dsa(session, dsa, &key_len);
+ if (key == NULL) {
+ goto __alloc_error;
+ }
+ DSA_free(dsa);
+
+ memcpy(method_buf, "ssh-dss", 7);
+ *method = method_buf;
+ *method_len = 7;
+ *pubkeydata = key;
+ *pubkeydata_len = key_len;
+ return 0;
+
+ __alloc_error:
+ if (dsa != NULL) {
+ DSA_free(dsa);
+ }
+ if (method_buf != NULL) {
+ LIBSSH2_FREE(session, method_buf);
+ }
+
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+}
+#endif /* LIBSSH_DSA */
+
+int
+_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+ int st;
+ BIO* bp;
+ EVP_PKEY* pk;
+ int pktype;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from private key file: %s",
+ privatekey);
+
+ bp = BIO_new_file(privatekey, "r");
+ if (bp == NULL) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key from private key "
+ "file: Unable to open private key file");
+ }
+ if (!EVP_get_cipherbyname("des")) {
+ /* If this cipher isn't loaded it's a pretty good indication that none
+ * are. I have *NO DOUBT* that there's a better way to deal with this
+ * ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on
+ * it.
+ */
+ OpenSSL_add_all_ciphers();
+ }
+ BIO_reset(bp);
+ pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase);
+ BIO_free(bp);
+
+ if (pk == NULL) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Wrong passphrase or invalid/unrecognized "
+ "private key file format");
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ pktype = EVP_PKEY_id(pk);
+#else
+ pktype = pk->type;
+#endif
+
+ switch (pktype) {
+ case EVP_PKEY_RSA :
+ st = gen_publickey_from_rsa_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+
+#if LIBSSH2_DSA
+ case EVP_PKEY_DSA :
+ st = gen_publickey_from_dsa_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH_DSA */
+
+ default :
+ st = _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Unsupported private key file format");
+ break;
+ }
+
+ EVP_PKEY_free(pk);
+ return st;
+}
+
+int
+_libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ int st;
+ BIO* bp;
+ EVP_PKEY* pk;
+ int pktype;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from private key.");
+
+ bp = BIO_new_mem_buf((char *)privatekeydata, privatekeydata_len);
+ if (!bp) {
+ return -1;
+ }
+ if (!EVP_get_cipherbyname("des")) {
+ /* If this cipher isn't loaded it's a pretty good indication that none
+ * are. I have *NO DOUBT* that there's a better way to deal with this
+ * ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on
+ * it.
+ */
+ OpenSSL_add_all_ciphers();
+ }
+ BIO_reset(bp);
+ pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase);
+ BIO_free(bp);
+
+ if (pk == NULL) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Wrong passphrase or invalid/unrecognized "
+ "private key file format");
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ pktype = EVP_PKEY_id(pk);
+#else
+ pktype = pk->type;
+#endif
+
+ switch (pktype) {
+ case EVP_PKEY_RSA :
+ st = gen_publickey_from_rsa_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len, pk);
+ break;
+#if LIBSSH2_DSA
+ case EVP_PKEY_DSA :
+ st = gen_publickey_from_dsa_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH_DSA */
+ default :
+ st = _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Unsupported private key file format");
+ break;
+ }
+
+ EVP_PKEY_free(pk);
+ return st;
+}
+
+#endif /* LIBSSH2_OPENSSL */
diff --git a/libs/libssh2/src/openssl.h b/libs/libssh2/src/openssl.h
new file mode 100644
index 0000000000..3ca71fa8a4
--- /dev/null
+++ b/libs/libssh2/src/openssl.h
@@ -0,0 +1,293 @@
+/* Copyright (C) 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved.
+ *
+ * Author: Simon Josefsson
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <openssl/opensslconf.h>
+#include <openssl/sha.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_MD5
+#include <openssl/md5.h>
+#endif
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+# define HAVE_OPAQUE_STRUCTS 1
+#endif
+
+#ifdef OPENSSL_NO_RSA
+# define LIBSSH2_RSA 0
+#else
+# define LIBSSH2_RSA 1
+#endif
+
+#ifdef OPENSSL_NO_DSA
+# define LIBSSH2_DSA 0
+#else
+# define LIBSSH2_DSA 1
+#endif
+
+#ifdef OPENSSL_NO_MD5
+# define LIBSSH2_MD5 0
+#else
+# define LIBSSH2_MD5 1
+#endif
+
+#ifdef OPENSSL_NO_RIPEMD
+# define LIBSSH2_HMAC_RIPEMD 0
+#else
+# define LIBSSH2_HMAC_RIPEMD 1
+#endif
+
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES)
+# define LIBSSH2_AES_CTR 1
+# define LIBSSH2_AES 1
+#else
+# define LIBSSH2_AES_CTR 0
+# define LIBSSH2_AES 0
+#endif
+
+#ifdef OPENSSL_NO_BF
+# define LIBSSH2_BLOWFISH 0
+#else
+# define LIBSSH2_BLOWFISH 1
+#endif
+
+#ifdef OPENSSL_NO_RC4
+# define LIBSSH2_RC4 0
+#else
+# define LIBSSH2_RC4 1
+#endif
+
+#ifdef OPENSSL_NO_CAST
+# define LIBSSH2_CAST 0
+#else
+# define LIBSSH2_CAST 1
+#endif
+
+#ifdef OPENSSL_NO_DES
+# define LIBSSH2_3DES 0
+#else
+# define LIBSSH2_3DES 1
+#endif
+
+#define _libssh2_random(buf, len) RAND_bytes ((buf), (len))
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha1_ctx EVP_MD_CTX *
+#else
+#define libssh2_sha1_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_sha1_init(libssh2_sha1_ctx *ctx);
+#define libssh2_sha1_init(x) _libssh2_sha1_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha1_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_sha1_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_sha1_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha1_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+int _libssh2_sha1(const unsigned char *message, unsigned long len,
+ unsigned char *out);
+#define libssh2_sha1(x,y,z) _libssh2_sha1(x,y,z)
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha256_ctx EVP_MD_CTX *
+#else
+#define libssh2_sha256_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_sha256_init(libssh2_sha256_ctx *ctx);
+#define libssh2_sha256_init(x) _libssh2_sha256_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha256_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_sha256_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_sha256_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha256_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+int _libssh2_sha256(const unsigned char *message, unsigned long len,
+ unsigned char *out);
+#define libssh2_sha256(x,y,z) _libssh2_sha256(x,y,z)
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_md5_ctx EVP_MD_CTX *
+#else
+#define libssh2_md5_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_md5_init(libssh2_md5_ctx *ctx);
+#define libssh2_md5_init(x) _libssh2_md5_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_md5_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_md5_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_md5_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_md5_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_hmac_ctx HMAC_CTX *
+#define libssh2_hmac_ctx_init(ctx) ctx = HMAC_CTX_new()
+#define libssh2_hmac_sha1_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_sha1(), NULL)
+#define libssh2_hmac_md5_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_md5(), NULL)
+#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_ripemd160(), NULL)
+#define libssh2_hmac_sha256_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_sha256(), NULL)
+#define libssh2_hmac_sha512_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_sha512(), NULL)
+
+#define libssh2_hmac_update(ctx, data, datalen) \
+ HMAC_Update(ctx, data, datalen)
+#define libssh2_hmac_final(ctx, data) HMAC_Final(ctx, data, NULL)
+#define libssh2_hmac_cleanup(ctx) HMAC_CTX_free(*(ctx))
+#else
+#define libssh2_hmac_ctx HMAC_CTX
+#define libssh2_hmac_ctx_init(ctx) \
+ HMAC_CTX_init(&ctx)
+#define libssh2_hmac_sha1_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_sha1(), NULL)
+#define libssh2_hmac_md5_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_md5(), NULL)
+#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_ripemd160(), NULL)
+#define libssh2_hmac_sha256_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_sha256(), NULL)
+#define libssh2_hmac_sha512_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_sha512(), NULL)
+
+#define libssh2_hmac_update(ctx, data, datalen) \
+ HMAC_Update(&(ctx), data, datalen)
+#define libssh2_hmac_final(ctx, data) HMAC_Final(&(ctx), data, NULL)
+#define libssh2_hmac_cleanup(ctx) HMAC_cleanup(ctx)
+#endif
+
+#define libssh2_crypto_init() \
+ OpenSSL_add_all_algorithms(); \
+ ENGINE_load_builtin_engines(); \
+ ENGINE_register_all_complete()
+
+#define libssh2_crypto_exit()
+
+#define libssh2_rsa_ctx RSA
+
+#define _libssh2_rsa_free(rsactx) RSA_free(rsactx)
+
+#define libssh2_dsa_ctx DSA
+
+
+#define _libssh2_dsa_free(dsactx) DSA_free(dsactx)
+
+#define _libssh2_cipher_type(name) const EVP_CIPHER *(*name)(void)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define _libssh2_cipher_ctx EVP_CIPHER_CTX *
+#else
+#define _libssh2_cipher_ctx EVP_CIPHER_CTX
+#endif
+
+#define _libssh2_cipher_aes256 EVP_aes_256_cbc
+#define _libssh2_cipher_aes192 EVP_aes_192_cbc
+#define _libssh2_cipher_aes128 EVP_aes_128_cbc
+#ifdef HAVE_EVP_AES_128_CTR
+#define _libssh2_cipher_aes128ctr EVP_aes_128_ctr
+#define _libssh2_cipher_aes192ctr EVP_aes_192_ctr
+#define _libssh2_cipher_aes256ctr EVP_aes_256_ctr
+#else
+#define _libssh2_cipher_aes128ctr _libssh2_EVP_aes_128_ctr
+#define _libssh2_cipher_aes192ctr _libssh2_EVP_aes_192_ctr
+#define _libssh2_cipher_aes256ctr _libssh2_EVP_aes_256_ctr
+#endif
+#define _libssh2_cipher_blowfish EVP_bf_cbc
+#define _libssh2_cipher_arcfour EVP_rc4
+#define _libssh2_cipher_cast5 EVP_cast5_cbc
+#define _libssh2_cipher_3des EVP_des_ede3_cbc
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_reset(*(ctx))
+#else
+#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_cleanup(ctx)
+#endif
+
+#define _libssh2_bn BIGNUM
+#define _libssh2_bn_ctx BN_CTX
+#define _libssh2_bn_ctx_new() BN_CTX_new()
+#define _libssh2_bn_ctx_free(bnctx) BN_CTX_free(bnctx)
+#define _libssh2_bn_init() BN_new()
+#define _libssh2_bn_init_from_bin() _libssh2_bn_init()
+#define _libssh2_bn_rand(bn, bits, top, bottom) BN_rand(bn, bits, top, bottom)
+#define _libssh2_bn_mod_exp(r, a, p, m, ctx) BN_mod_exp(r, a, p, m, ctx)
+#define _libssh2_bn_set_word(bn, val) BN_set_word(bn, val)
+#define _libssh2_bn_from_bin(bn, len, val) BN_bin2bn(val, len, bn)
+#define _libssh2_bn_to_bin(bn, val) BN_bn2bin(bn, val)
+#define _libssh2_bn_bytes(bn) BN_num_bytes(bn)
+#define _libssh2_bn_bits(bn) BN_num_bits(bn)
+#define _libssh2_bn_free(bn) BN_clear_free(bn)
+
+const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void);
+const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void);
+const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void);
+
diff --git a/libs/libssh2/src/packet.c b/libs/libssh2/src/packet.c
new file mode 100644
index 0000000000..5f1feb8c6b
--- /dev/null
+++ b/libs/libssh2/src/packet.c
@@ -0,0 +1,1267 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2005,2006 Mikhail Gusarov
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include <sys/types.h>
+
+#include "transport.h"
+#include "channel.h"
+#include "packet.h"
+
+/*
+ * libssh2_packet_queue_listener
+ *
+ * Queue a connection request for a listener
+ */
+static inline int
+packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long datalen,
+ packet_queue_listener_state_t *listen_state)
+{
+ /*
+ * Look for a matching listener
+ */
+ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
+ unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1);
+ unsigned char *p;
+ LIBSSH2_LISTENER *listn = _libssh2_list_first(&session->listeners);
+ char failure_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+ int rc;
+
+ (void) datalen;
+
+ if (listen_state->state == libssh2_NB_state_idle) {
+ unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5;
+ listen_state->sender_channel = _libssh2_ntohu32(s);
+ s += 4;
+
+ listen_state->initial_window_size = _libssh2_ntohu32(s);
+ s += 4;
+ listen_state->packet_size = _libssh2_ntohu32(s);
+ s += 4;
+
+ listen_state->host_len = _libssh2_ntohu32(s);
+ s += 4;
+ listen_state->host = s;
+ s += listen_state->host_len;
+ listen_state->port = _libssh2_ntohu32(s);
+ s += 4;
+
+ listen_state->shost_len = _libssh2_ntohu32(s);
+ s += 4;
+ listen_state->shost = s;
+ s += listen_state->shost_len;
+ listen_state->sport = _libssh2_ntohu32(s);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Remote received connection from %s:%ld to %s:%ld",
+ listen_state->shost, listen_state->sport,
+ listen_state->host, listen_state->port);
+
+ listen_state->state = libssh2_NB_state_allocated;
+ }
+
+ if (listen_state->state != libssh2_NB_state_sent) {
+ while (listn) {
+ if ((listn->port == (int) listen_state->port) &&
+ (strlen(listn->host) == listen_state->host_len) &&
+ (memcmp (listn->host, listen_state->host,
+ listen_state->host_len) == 0)) {
+ /* This is our listener */
+ LIBSSH2_CHANNEL *channel = NULL;
+ listen_state->channel = NULL;
+
+ if (listen_state->state == libssh2_NB_state_allocated) {
+ if (listn->queue_maxsize &&
+ (listn->queue_maxsize <= listn->queue_size)) {
+ /* Queue is full */
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Listener queue full, ignoring");
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+
+ channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if (!channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a channel for "
+ "new connection");
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+ listen_state->channel = channel;
+
+ channel->session = session;
+ channel->channel_type_len = sizeof("forwarded-tcpip") - 1;
+ channel->channel_type = LIBSSH2_ALLOC(session,
+ channel->
+ channel_type_len +
+ 1);
+ if (!channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a channel for new"
+ " connection");
+ LIBSSH2_FREE(session, channel);
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+ memcpy(channel->channel_type, "forwarded-tcpip",
+ channel->channel_type_len + 1);
+
+ channel->remote.id = listen_state->sender_channel;
+ channel->remote.window_size_initial =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.window_size =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.packet_size =
+ LIBSSH2_CHANNEL_PACKET_DEFAULT;
+
+ channel->local.id = _libssh2_channel_nextid(session);
+ channel->local.window_size_initial =
+ listen_state->initial_window_size;
+ channel->local.window_size =
+ listen_state->initial_window_size;
+ channel->local.packet_size = listen_state->packet_size;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Connection queued: channel %lu/%lu "
+ "win %lu/%lu packet %lu/%lu",
+ channel->local.id, channel->remote.id,
+ channel->local.window_size,
+ channel->remote.window_size,
+ channel->local.packet_size,
+ channel->remote.packet_size);
+
+ p = listen_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
+ _libssh2_store_u32(&p, channel->remote.id);
+ _libssh2_store_u32(&p, channel->local.id);
+ _libssh2_store_u32(&p,
+ channel->remote.window_size_initial);
+ _libssh2_store_u32(&p, channel->remote.packet_size);
+
+ listen_state->state = libssh2_NB_state_created;
+ }
+
+ if (listen_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, listen_state->packet,
+ 17, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if (rc) {
+ listen_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send channel "
+ "open confirmation");
+ }
+
+ /* Link the channel into the end of the queue list */
+ if (listen_state->channel) {
+ _libssh2_list_add(&listn->queue,
+ &listen_state->channel->node);
+ listn->queue_size++;
+ }
+
+ listen_state->state = libssh2_NB_state_idle;
+ return 0;
+ }
+ }
+
+ listn = _libssh2_list_next(&listn->node);
+ }
+
+ listen_state->state = libssh2_NB_state_sent;
+ }
+
+ /* We're not listening to you */
+ p = listen_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
+ _libssh2_store_u32(&p, listen_state->sender_channel);
+ _libssh2_store_u32(&p, failure_code);
+ _libssh2_store_str(&p, FwdNotReq, sizeof(FwdNotReq) - 1);
+ _libssh2_htonu32(p, 0);
+
+ rc = _libssh2_transport_send(session, listen_state->packet,
+ packet_len, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ listen_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc, "Unable to send open failure");
+
+ }
+ listen_state->state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * packet_x11_open
+ *
+ * Accept a forwarded X11 connection
+ */
+static inline int
+packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long datalen,
+ packet_x11_open_state_t *x11open_state)
+{
+ int failure_code = SSH_OPEN_CONNECT_FAILED;
+ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
+ unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1);
+ unsigned char *p;
+ LIBSSH2_CHANNEL *channel = x11open_state->channel;
+ int rc;
+
+ (void) datalen;
+
+ if (x11open_state->state == libssh2_NB_state_idle) {
+ unsigned char *s = data + (sizeof("x11") - 1) + 5;
+ x11open_state->sender_channel = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->initial_window_size = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->packet_size = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->shost_len = _libssh2_ntohu32(s);
+ s += 4;
+ x11open_state->shost = s;
+ s += x11open_state->shost_len;
+ x11open_state->sport = _libssh2_ntohu32(s);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "X11 Connection Received from %s:%ld on channel %lu",
+ x11open_state->shost, x11open_state->sport,
+ x11open_state->sender_channel);
+
+ x11open_state->state = libssh2_NB_state_allocated;
+ }
+
+ if (session->x11) {
+ if (x11open_state->state == libssh2_NB_state_allocated) {
+ channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if (!channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "allocate a channel for new connection");
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ goto x11_exit;
+ }
+
+ channel->session = session;
+ channel->channel_type_len = sizeof("x11") - 1;
+ channel->channel_type = LIBSSH2_ALLOC(session,
+ channel->channel_type_len +
+ 1);
+ if (!channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "allocate a channel for new connection");
+ LIBSSH2_FREE(session, channel);
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ goto x11_exit;
+ }
+ memcpy(channel->channel_type, "x11",
+ channel->channel_type_len + 1);
+
+ channel->remote.id = x11open_state->sender_channel;
+ channel->remote.window_size_initial =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT;
+
+ channel->local.id = _libssh2_channel_nextid(session);
+ channel->local.window_size_initial =
+ x11open_state->initial_window_size;
+ channel->local.window_size = x11open_state->initial_window_size;
+ channel->local.packet_size = x11open_state->packet_size;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "X11 Connection established: channel %lu/%lu "
+ "win %lu/%lu packet %lu/%lu",
+ channel->local.id, channel->remote.id,
+ channel->local.window_size,
+ channel->remote.window_size,
+ channel->local.packet_size,
+ channel->remote.packet_size);
+ p = x11open_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
+ _libssh2_store_u32(&p, channel->remote.id);
+ _libssh2_store_u32(&p, channel->local.id);
+ _libssh2_store_u32(&p, channel->remote.window_size_initial);
+ _libssh2_store_u32(&p, channel->remote.packet_size);
+
+ x11open_state->state = libssh2_NB_state_created;
+ }
+
+ if (x11open_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, x11open_state->packet, 17,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ x11open_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send channel open "
+ "confirmation");
+ }
+
+ /* Link the channel into the session */
+ _libssh2_list_add(&session->channels, &channel->node);
+
+ /*
+ * Pass control to the callback, they may turn right around and
+ * free the channel, or actually use it
+ */
+ LIBSSH2_X11_OPEN(channel, (char *)x11open_state->shost,
+ x11open_state->sport);
+
+ x11open_state->state = libssh2_NB_state_idle;
+ return 0;
+ }
+ }
+ else
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ /* fall-trough */
+ x11_exit:
+ p = x11open_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
+ _libssh2_store_u32(&p, x11open_state->sender_channel);
+ _libssh2_store_u32(&p, failure_code);
+ _libssh2_store_str(&p, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1);
+ _libssh2_htonu32(p, 0);
+
+ rc = _libssh2_transport_send(session, x11open_state->packet, packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ x11open_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc, "Unable to send open failure");
+ }
+ x11open_state->state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * _libssh2_packet_add
+ *
+ * Create a new packet and attach it to the brigade. Called from the transport
+ * layer when it has received a packet.
+ *
+ * The input pointer 'data' is pointing to allocated data that this function
+ * is asked to deal with so on failure OR success, it must be freed fine.
+ * The only exception is when the return code is LIBSSH2_ERROR_EAGAIN.
+ *
+ * This function will always be called with 'datalen' greater than zero.
+ */
+int
+_libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
+ size_t datalen, int macstate)
+{
+ int rc = 0;
+ char *message=NULL;
+ char *language=NULL;
+ size_t message_len=0;
+ size_t language_len=0;
+ LIBSSH2_CHANNEL *channelp = NULL;
+ size_t data_head = 0;
+ unsigned char msg = data[0];
+
+ switch(session->packAdd_state) {
+ case libssh2_NB_state_idle:
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Packet type %d received, length=%d",
+ (int) msg, (int) datalen);
+
+ if ((macstate == LIBSSH2_MAC_INVALID) &&
+ (!session->macerror ||
+ LIBSSH2_MACERROR(session, (char *) data, datalen))) {
+ /* Bad MAC input, but no callback set or non-zero return from the
+ callback */
+
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC,
+ "Invalid MAC received");
+ }
+ session->packAdd_state = libssh2_NB_state_allocated;
+ break;
+ case libssh2_NB_state_jump1:
+ goto libssh2_packet_add_jump_point1;
+ case libssh2_NB_state_jump2:
+ goto libssh2_packet_add_jump_point2;
+ case libssh2_NB_state_jump3:
+ goto libssh2_packet_add_jump_point3;
+ case libssh2_NB_state_jump4:
+ goto libssh2_packet_add_jump_point4;
+ case libssh2_NB_state_jump5:
+ goto libssh2_packet_add_jump_point5;
+ default: /* nothing to do */
+ break;
+ }
+
+ if (session->packAdd_state == libssh2_NB_state_allocated) {
+ /* A couple exceptions to the packet adding rule: */
+ switch (msg) {
+
+ /*
+ byte SSH_MSG_DISCONNECT
+ uint32 reason code
+ string description in ISO-10646 UTF-8 encoding [RFC3629]
+ string language tag [RFC3066]
+ */
+
+ case SSH_MSG_DISCONNECT:
+ if(datalen >= 5) {
+ size_t reason = _libssh2_ntohu32(data + 1);
+
+ if(datalen >= 9) {
+ message_len = _libssh2_ntohu32(data + 5);
+
+ if(message_len < datalen-13) {
+ /* 9 = packet_type(1) + reason(4) + message_len(4) */
+ message = (char *) data + 9;
+
+ language_len = _libssh2_ntohu32(data + 9 + message_len);
+ language = (char *) data + 9 + message_len + 4;
+
+ if(language_len > (datalen-13-message_len)) {
+ /* bad input, clear info */
+ language = message = NULL;
+ language_len = message_len = 0;
+ }
+ }
+ else
+ /* bad size, clear it */
+ message_len=0;
+ }
+ if (session->ssh_msg_disconnect) {
+ LIBSSH2_DISCONNECT(session, reason, message,
+ message_len, language, language_len);
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Disconnect(%d): %s(%s)", reason,
+ message, language);
+ }
+
+ LIBSSH2_FREE(session, data);
+ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
+ session->packAdd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "socket disconnect");
+ /*
+ byte SSH_MSG_IGNORE
+ string data
+ */
+
+ case SSH_MSG_IGNORE:
+ if (datalen >= 2) {
+ if (session->ssh_msg_ignore) {
+ LIBSSH2_IGNORE(session, (char *) data + 1, datalen - 1);
+ }
+ } else if (session->ssh_msg_ignore) {
+ LIBSSH2_IGNORE(session, "", 0);
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_DEBUG
+ boolean always_display
+ string message in ISO-10646 UTF-8 encoding [RFC3629]
+ string language tag [RFC3066]
+ */
+
+ case SSH_MSG_DEBUG:
+ if(datalen >= 2) {
+ int always_display= data[1];
+
+ if(datalen >= 6) {
+ message_len = _libssh2_ntohu32(data + 2);
+
+ if(message_len <= (datalen - 10)) {
+ /* 6 = packet_type(1) + display(1) + message_len(4) */
+ message = (char *) data + 6;
+ language_len = _libssh2_ntohu32(data + 6 + message_len);
+
+ if(language_len <= (datalen - 10 - message_len))
+ language = (char *) data + 10 + message_len;
+ }
+ }
+
+ if (session->ssh_msg_debug) {
+ LIBSSH2_DEBUG(session, always_display, message,
+ message_len, language, language_len);
+ }
+ }
+ /*
+ * _libssh2_debug will actually truncate this for us so
+ * that it's not an inordinate about of data
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Debug Packet: %s", message);
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_GLOBAL_REQUEST
+ string request name in US-ASCII only
+ boolean want reply
+ .... request-specific data follows
+ */
+
+ case SSH_MSG_GLOBAL_REQUEST:
+ if(datalen >= 5) {
+ uint32_t len =0;
+ unsigned char want_reply=0;
+ len = _libssh2_ntohu32(data + 1);
+ if(datalen >= (6 + len)) {
+ want_reply = data[5 + len];
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "Received global request type %.*s (wr %X)",
+ len, data + 5, want_reply);
+ }
+
+
+ if (want_reply) {
+ static const unsigned char packet =
+ SSH_MSG_REQUEST_FAILURE;
+ libssh2_packet_add_jump_point5:
+ session->packAdd_state = libssh2_NB_state_jump5;
+ rc = _libssh2_transport_send(session, &packet, 1, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_EXTENDED_DATA
+ uint32 recipient channel
+ uint32 data_type_code
+ string data
+ */
+
+ case SSH_MSG_CHANNEL_EXTENDED_DATA:
+ /* streamid(4) */
+ data_head += 4;
+
+ /* fall-through */
+
+ /*
+ byte SSH_MSG_CHANNEL_DATA
+ uint32 recipient channel
+ string data
+ */
+
+ case SSH_MSG_CHANNEL_DATA:
+ /* packet_type(1) + channelno(4) + datalen(4) */
+ data_head += 9;
+
+ if(datalen >= data_head)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+
+ if (!channelp) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN,
+ "Packet received for unknown channel");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ uint32_t stream_id = 0;
+ if (msg == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ stream_id = _libssh2_ntohu32(data + 5);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "%d bytes packet_add() for %lu/%lu/%lu",
+ (int) (datalen - data_head),
+ channelp->local.id,
+ channelp->remote.id,
+ stream_id);
+ }
+#endif
+ if ((channelp->remote.extended_data_ignore_mode ==
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) &&
+ (msg == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
+ /* Pretend we didn't receive this */
+ LIBSSH2_FREE(session, data);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Ignoring extended data and refunding %d bytes",
+ (int) (datalen - 13));
+ if (channelp->read_avail + datalen - data_head >=
+ channelp->remote.window_size)
+ datalen = channelp->remote.window_size -
+ channelp->read_avail + data_head;
+
+ channelp->remote.window_size -= datalen - data_head;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "shrinking window size by %lu bytes to %lu, read_avail %lu",
+ datalen - data_head,
+ channelp->remote.window_size,
+ channelp->read_avail);
+
+ session->packAdd_channelp = channelp;
+
+ /* Adjust the window based on the block we just freed */
+ libssh2_packet_add_jump_point1:
+ session->packAdd_state = libssh2_NB_state_jump1;
+ rc = _libssh2_channel_receive_window_adjust(session->
+ packAdd_channelp,
+ datalen - 13,
+ 1, NULL);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ /*
+ * REMEMBER! remote means remote as source of data,
+ * NOT remote window!
+ */
+ if (channelp->remote.packet_size < (datalen - data_head)) {
+ /*
+ * Spec says we MAY ignore bytes sent beyond
+ * packet_size
+ */
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
+ "Packet contains more data than we offered"
+ " to receive, truncating");
+ datalen = channelp->remote.packet_size + data_head;
+ }
+ if (channelp->remote.window_size <= channelp->read_avail) {
+ /*
+ * Spec says we MAY ignore bytes sent beyond
+ * window_size
+ */
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
+ "The current receive window is full,"
+ " data ignored");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ /* Reset EOF status */
+ channelp->remote.eof = 0;
+
+ if (channelp->read_avail + datalen - data_head >
+ channelp->remote.window_size) {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
+ "Remote sent more data than current "
+ "window allows, truncating");
+ datalen = channelp->remote.window_size -
+ channelp->read_avail + data_head;
+ }
+
+ /* Update the read_avail counter. The window size will be
+ * updated once the data is actually read from the queue
+ * from an upper layer */
+ channelp->read_avail += datalen - data_head;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "increasing read_avail by %lu bytes to %lu/%lu",
+ (long)(datalen - data_head),
+ (long)channelp->read_avail,
+ (long)channelp->remote.window_size);
+
+ break;
+
+ /*
+ byte SSH_MSG_CHANNEL_EOF
+ uint32 recipient channel
+ */
+
+ case SSH_MSG_CHANNEL_EOF:
+ if(datalen >= 5)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if (!channelp)
+ /* We may have freed already, just quietly ignore this... */
+ ;
+ else {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "EOF received for channel %lu/%lu",
+ channelp->local.id,
+ channelp->remote.id);
+ channelp->remote.eof = 1;
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_REQUEST
+ uint32 recipient channel
+ string request type in US-ASCII characters only
+ boolean want reply
+ .... type-specific data follows
+ */
+
+ case SSH_MSG_CHANNEL_REQUEST:
+ if(datalen >= 9) {
+ uint32_t channel = _libssh2_ntohu32(data + 1);
+ uint32_t len = _libssh2_ntohu32(data + 5);
+ unsigned char want_reply = 1;
+
+ if(len < (datalen - 10))
+ want_reply = data[9 + len];
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "Channel %d received request type %.*s (wr %X)",
+ channel, len, data + 9, want_reply);
+
+ if (len == sizeof("exit-status") - 1
+ && !memcmp("exit-status", data + 9,
+ sizeof("exit-status") - 1)) {
+
+ /* we've got "exit-status" packet. Set the session value */
+ if(datalen >= 20)
+ channelp =
+ _libssh2_channel_locate(session, channel);
+
+ if (channelp) {
+ channelp->exit_status =
+ _libssh2_ntohu32(data + 9 + sizeof("exit-status"));
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Exit status %lu received for "
+ "channel %lu/%lu",
+ channelp->exit_status,
+ channelp->local.id,
+ channelp->remote.id);
+ }
+
+ }
+ else if (len == sizeof("exit-signal") - 1
+ && !memcmp("exit-signal", data + 9,
+ sizeof("exit-signal") - 1)) {
+ /* command terminated due to signal */
+ if(datalen >= 20)
+ channelp = _libssh2_channel_locate(session, channel);
+
+ if (channelp) {
+ /* set signal name (without SIG prefix) */
+ uint32_t namelen =
+ _libssh2_ntohu32(data + 9 + sizeof("exit-signal"));
+ channelp->exit_signal =
+ LIBSSH2_ALLOC(session, namelen + 1);
+ if (!channelp->exit_signal)
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "memory for signal name");
+ else {
+ memcpy(channelp->exit_signal,
+ data + 13 + sizeof("exit_signal"), namelen);
+ channelp->exit_signal[namelen] = '\0';
+ /* TODO: save error message and language tag */
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Exit signal %s received for "
+ "channel %lu/%lu",
+ channelp->exit_signal,
+ channelp->local.id,
+ channelp->remote.id);
+ }
+ }
+ }
+
+
+ if (want_reply) {
+ unsigned char packet[5];
+ libssh2_packet_add_jump_point4:
+ session->packAdd_state = libssh2_NB_state_jump4;
+ packet[0] = SSH_MSG_CHANNEL_FAILURE;
+ memcpy(&packet[1], data+1, 4);
+ rc = _libssh2_transport_send(session, packet, 5, NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
+ byte SSH_MSG_CHANNEL_CLOSE
+ uint32 recipient channel
+ */
+
+ case SSH_MSG_CHANNEL_CLOSE:
+ if(datalen >= 5)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if (!channelp) {
+ /* We may have freed already, just quietly ignore this... */
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Close received for channel %lu/%lu",
+ channelp->local.id,
+ channelp->remote.id);
+
+ channelp->remote.close = 1;
+ channelp->remote.eof = 1;
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_OPEN
+ string "session"
+ uint32 sender channel
+ uint32 initial window size
+ uint32 maximum packet size
+ */
+
+ case SSH_MSG_CHANNEL_OPEN:
+ if(datalen < 17)
+ ;
+ else if ((datalen >= (sizeof("forwarded-tcpip") + 4)) &&
+ ((sizeof("forwarded-tcpip") - 1) ==
+ _libssh2_ntohu32(data + 1))
+ &&
+ (memcmp(data + 5, "forwarded-tcpip",
+ sizeof("forwarded-tcpip") - 1) == 0)) {
+
+ /* init the state struct */
+ memset(&session->packAdd_Qlstn_state, 0,
+ sizeof(session->packAdd_Qlstn_state));
+
+ libssh2_packet_add_jump_point2:
+ session->packAdd_state = libssh2_NB_state_jump2;
+ rc = packet_queue_listener(session, data, datalen,
+ &session->packAdd_Qlstn_state);
+ }
+ else if ((datalen >= (sizeof("x11") + 4)) &&
+ ((sizeof("x11") - 1) == _libssh2_ntohu32(data + 1)) &&
+ (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) {
+
+ /* init the state struct */
+ memset(&session->packAdd_x11open_state, 0,
+ sizeof(session->packAdd_x11open_state));
+
+ libssh2_packet_add_jump_point3:
+ session->packAdd_state = libssh2_NB_state_jump3;
+ rc = packet_x11_open(session, data, datalen,
+ &session->packAdd_x11open_state);
+ }
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
+ byte SSH_MSG_CHANNEL_WINDOW_ADJUST
+ uint32 recipient channel
+ uint32 bytes to add
+ */
+ case SSH_MSG_CHANNEL_WINDOW_ADJUST:
+ if(datalen < 9)
+ ;
+ else {
+ uint32_t bytestoadd = _libssh2_ntohu32(data + 5);
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if(channelp) {
+ channelp->local.window_size += bytestoadd;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Window adjust for channel %lu/%lu, "
+ "adding %lu bytes, new window_size=%lu",
+ channelp->local.id,
+ channelp->remote.id,
+ bytestoadd,
+ channelp->local.window_size);
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ default:
+ break;
+ }
+
+ session->packAdd_state = libssh2_NB_state_sent;
+ }
+
+ if (session->packAdd_state == libssh2_NB_state_sent) {
+ LIBSSH2_PACKET *packetp =
+ LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
+ if (!packetp) {
+ _libssh2_debug(session, LIBSSH2_ERROR_ALLOC,
+ "memory for packet");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return LIBSSH2_ERROR_ALLOC;
+ }
+ packetp->data = data;
+ packetp->data_len = datalen;
+ packetp->data_head = data_head;
+
+ _libssh2_list_add(&session->packets, &packetp->node);
+
+ session->packAdd_state = libssh2_NB_state_sent1;
+ }
+
+ if ((msg == SSH_MSG_KEXINIT &&
+ !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) ||
+ (session->packAdd_state == libssh2_NB_state_sent2)) {
+ if (session->packAdd_state == libssh2_NB_state_sent1) {
+ /*
+ * Remote wants new keys
+ * Well, it's already in the brigade,
+ * let's just call back into ourselves
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Renegotiating Keys");
+
+ session->packAdd_state = libssh2_NB_state_sent2;
+ }
+
+ /*
+ * The KEXINIT message has been added to the queue. The packAdd and
+ * readPack states need to be reset because _libssh2_kex_exchange
+ * (eventually) calls upon _libssh2_transport_read to read the rest of
+ * the key exchange conversation.
+ */
+ session->readPack_state = libssh2_NB_state_idle;
+ session->packet.total_num = 0;
+ session->packAdd_state = libssh2_NB_state_idle;
+ session->fullpacket_state = libssh2_NB_state_idle;
+
+ memset(&session->startup_key_state, 0, sizeof(key_exchange_state_t));
+
+ /*
+ * If there was a key reexchange failure, let's just hope we didn't
+ * send NEWKEYS yet, otherwise remote will drop us like a rock
+ */
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * _libssh2_packet_ask
+ *
+ * Scan the brigade for a matching packet type, optionally poll the socket for
+ * a packet first
+ */
+int
+_libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs, const unsigned char *match_buf,
+ size_t match_len)
+{
+ LIBSSH2_PACKET *packet = _libssh2_list_first(&session->packets);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Looking for packet of type: %d", (int) packet_type);
+
+ while (packet) {
+ if (packet->data[0] == packet_type
+ && (packet->data_len >= (match_ofs + match_len))
+ && (!match_buf ||
+ (memcmp(packet->data + match_ofs, match_buf,
+ match_len) == 0))) {
+ *data = packet->data;
+ *data_len = packet->data_len;
+
+ /* unlink struct from session->packets */
+ _libssh2_list_remove(&packet->node);
+
+ LIBSSH2_FREE(session, packet);
+
+ return 0;
+ }
+ packet = _libssh2_list_next(&packet->node);
+ }
+ return -1;
+}
+
+/*
+ * libssh2_packet_askv
+ *
+ * Scan for any of a list of packet types in the brigade, optionally poll the
+ * socket for a packet first
+ */
+int
+_libssh2_packet_askv(LIBSSH2_SESSION * session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len)
+{
+ int i, packet_types_len = strlen((char *) packet_types);
+
+ for(i = 0; i < packet_types_len; i++) {
+ if (0 == _libssh2_packet_ask(session, packet_types[i], data,
+ data_len, match_ofs,
+ match_buf, match_len)) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * _libssh2_packet_require
+ *
+ * Loops _libssh2_transport_read() until the packet requested is available
+ * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
+ *
+ * Returns negative on error
+ * Returns 0 when it has taken care of the requested packet.
+ */
+int
+_libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_require_state_t *state)
+{
+ if (state->start == 0) {
+ if (_libssh2_packet_ask(session, packet_type, data, data_len,
+ match_ofs, match_buf,
+ match_len) == 0) {
+ /* A packet was available in the packet brigade */
+ return 0;
+ }
+
+ state->start = time(NULL);
+ }
+
+ while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ int ret = _libssh2_transport_read(session);
+ if (ret == LIBSSH2_ERROR_EAGAIN)
+ return ret;
+ else if (ret < 0) {
+ state->start = 0;
+ /* an error which is not just because of blocking */
+ return ret;
+ } else if (ret == packet_type) {
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ ret = _libssh2_packet_ask(session, packet_type, data, data_len,
+ match_ofs, match_buf, match_len);
+ state->start = 0;
+ return ret;
+ } else if (ret == 0) {
+ /* nothing available, wait until data arrives or we time out */
+ long left = LIBSSH2_READ_TIMEOUT - (long)(time(NULL) -
+ state->start);
+
+ if (left <= 0) {
+ state->start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ return -1; /* no packet available yet */
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/*
+ * _libssh2_packet_burn
+ *
+ * Loops _libssh2_transport_read() until any packet is available and promptly
+ * discards it.
+ * Used during KEX exchange to discard badly guessed KEX_INIT packets
+ */
+int
+_libssh2_packet_burn(LIBSSH2_SESSION * session,
+ libssh2_nonblocking_states * state)
+{
+ unsigned char *data;
+ size_t data_len;
+ unsigned char i, all_packets[255];
+ int ret;
+
+ if (*state == libssh2_NB_state_idle) {
+ for(i = 1; i < 255; i++) {
+ all_packets[i - 1] = i;
+ }
+ all_packets[254] = 0;
+
+ if (_libssh2_packet_askv(session, all_packets, &data, &data_len, 0,
+ NULL, 0) == 0) {
+ i = data[0];
+ /* A packet was available in the packet brigade, burn it */
+ LIBSSH2_FREE(session, data);
+ return i;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Blocking until packet becomes available to burn");
+ *state = libssh2_NB_state_created;
+ }
+
+ while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ ret = _libssh2_transport_read(session);
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ } else if (ret < 0) {
+ *state = libssh2_NB_state_idle;
+ return ret;
+ } else if (ret == 0) {
+ /* FIXME: this might busyloop */
+ continue;
+ }
+
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ if (0 ==
+ _libssh2_packet_ask(session, (unsigned char)ret,
+ &data, &data_len, 0, NULL, 0)) {
+ /* Smoke 'em if you got 'em */
+ LIBSSH2_FREE(session, data);
+ *state = libssh2_NB_state_idle;
+ return ret;
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/*
+ * _libssh2_packet_requirev
+ *
+ * Loops _libssh2_transport_read() until one of a list of packet types
+ * requested is available. SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause
+ * a bailout. packet_types is a null terminated list of packet_type numbers
+ */
+
+int
+_libssh2_packet_requirev(LIBSSH2_SESSION *session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf, size_t match_len,
+ packet_requirev_state_t * state)
+{
+ if (_libssh2_packet_askv(session, packet_types, data, data_len, match_ofs,
+ match_buf, match_len) == 0) {
+ /* One of the packets listed was available in the packet brigade */
+ state->start = 0;
+ return 0;
+ }
+
+ if (state->start == 0) {
+ state->start = time(NULL);
+ }
+
+ while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
+ int ret = _libssh2_transport_read(session);
+ if ((ret < 0) && (ret != LIBSSH2_ERROR_EAGAIN)) {
+ state->start = 0;
+ return ret;
+ }
+ if (ret <= 0) {
+ long left = LIBSSH2_READ_TIMEOUT -
+ (long)(time(NULL) - state->start);
+
+ if (left <= 0) {
+ state->start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ else if (ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+ }
+
+ if (strchr((char *) packet_types, ret)) {
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ return _libssh2_packet_askv(session, packet_types, data,
+ data_len, match_ofs, match_buf,
+ match_len);
+ }
+ }
+
+ /* Only reached if the socket died */
+ state->start = 0;
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
diff --git a/libs/libssh2/src/packet.h b/libs/libssh2/src/packet.h
new file mode 100644
index 0000000000..d66b15b50d
--- /dev/null
+++ b/libs/libssh2/src/packet.h
@@ -0,0 +1,76 @@
+#ifndef LIBSSH2_PACKET_H
+#define LIBSSH2_PACKET_H
+/*
+ * Copyright (C) 2010 by Daniel Stenberg
+ * Author: Daniel Stenberg <daniel@haxx.se>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+int _libssh2_packet_read(LIBSSH2_SESSION * session);
+
+int _libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len);
+
+int _libssh2_packet_askv(LIBSSH2_SESSION * session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len);
+int _libssh2_packet_require(LIBSSH2_SESSION * session,
+ unsigned char packet_type, unsigned char **data,
+ size_t *data_len, int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_require_state_t * state);
+int _libssh2_packet_requirev(LIBSSH2_SESSION *session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_requirev_state_t * state);
+int _libssh2_packet_burn(LIBSSH2_SESSION * session,
+ libssh2_nonblocking_states * state);
+int _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long data_len);
+int _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
+ size_t datalen, int macstate);
+
+#endif /* LIBSSH2_PACKET_H */
diff --git a/libs/libssh2/src/pem.c b/libs/libssh2/src/pem.c
new file mode 100644
index 0000000000..9f51bba3b5
--- /dev/null
+++ b/libs/libssh2/src/pem.c
@@ -0,0 +1,324 @@
+/* Copyright (C) 2007 The Written Word, Inc.
+ * Copyright (C) 2008, Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+static int
+readline(char *line, int line_size, FILE * fp)
+{
+ size_t len;
+
+ if (!line) {
+ return -1;
+ }
+ if (!fgets(line, line_size, fp)) {
+ return -1;
+ }
+
+ if (*line) {
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+ }
+
+ if (*line) {
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\r') {
+ line[len - 1] = '\0';
+ }
+ }
+
+ return 0;
+}
+
+static int
+readline_memory(char *line, size_t line_size,
+ const char *filedata, size_t filedata_len,
+ size_t *filedata_offset)
+{
+ size_t off, len;
+
+ off = *filedata_offset;
+
+ for (len = 0; off + len < filedata_len && len < line_size; len++) {
+ if (filedata[off + len] == '\n' ||
+ filedata[off + len] == '\r') {
+ break;
+ }
+ }
+
+ if (len) {
+ memcpy(line, filedata + off, len);
+ *filedata_offset += len;
+ }
+
+ line[len] = '\0';
+ *filedata_offset += 1;
+
+ return 0;
+}
+
+#define LINE_SIZE 128
+
+int
+_libssh2_pem_parse(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ FILE * fp, unsigned char **data, unsigned int *datalen)
+{
+ char line[LINE_SIZE];
+ char *b64data = NULL;
+ unsigned int b64datalen = 0;
+ int ret;
+
+ do {
+ *line = '\0';
+
+ if (readline(line, LINE_SIZE, fp)) {
+ return -1;
+ }
+ }
+ while (strcmp(line, headerbegin) != 0);
+
+ *line = '\0';
+
+ do {
+ if (*line) {
+ char *tmp;
+ size_t linelen;
+
+ linelen = strlen(line);
+ tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
+ if (!tmp) {
+ ret = -1;
+ goto out;
+ }
+ memcpy(tmp + b64datalen, line, linelen);
+ b64data = tmp;
+ b64datalen += linelen;
+ }
+
+ *line = '\0';
+
+ if (readline(line, LINE_SIZE, fp)) {
+ ret = -1;
+ goto out;
+ }
+ } while (strcmp(line, headerend) != 0);
+
+ if (!b64data) {
+ return -1;
+ }
+
+ if (libssh2_base64_decode(session, (char**) data, datalen,
+ b64data, b64datalen)) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = 0;
+ out:
+ if (b64data) {
+ LIBSSH2_FREE(session, b64data);
+ }
+ return ret;
+}
+
+int
+_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ const char *filedata, size_t filedata_len,
+ unsigned char **data, unsigned int *datalen)
+{
+ char line[LINE_SIZE];
+ char *b64data = NULL;
+ unsigned int b64datalen = 0;
+ size_t off = 0;
+ int ret;
+
+ do {
+ *line = '\0';
+
+ if (readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
+ return -1;
+ }
+ }
+ while (strcmp(line, headerbegin) != 0);
+
+ *line = '\0';
+
+ do {
+ if (*line) {
+ char *tmp;
+ size_t linelen;
+
+ linelen = strlen(line);
+ tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
+ if (!tmp) {
+ ret = -1;
+ goto out;
+ }
+ memcpy(tmp + b64datalen, line, linelen);
+ b64data = tmp;
+ b64datalen += linelen;
+ }
+
+ *line = '\0';
+
+ if (readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
+ ret = -1;
+ goto out;
+ }
+ } while (strcmp(line, headerend) != 0);
+
+ if (!b64data) {
+ return -1;
+ }
+
+ if (libssh2_base64_decode(session, (char**) data, datalen,
+ b64data, b64datalen)) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = 0;
+ out:
+ if (b64data) {
+ LIBSSH2_FREE(session, b64data);
+ }
+ return ret;
+}
+
+static int
+read_asn1_length(const unsigned char *data,
+ unsigned int datalen, unsigned int *len)
+{
+ unsigned int lenlen;
+ int nextpos;
+
+ if (datalen < 1) {
+ return -1;
+ }
+ *len = data[0];
+
+ if (*len >= 0x80) {
+ lenlen = *len & 0x7F;
+ *len = data[1];
+ if (1 + lenlen > datalen) {
+ return -1;
+ }
+ if (lenlen > 1) {
+ *len <<= 8;
+ *len |= data[2];
+ }
+ } else {
+ lenlen = 0;
+ }
+
+ nextpos = 1 + lenlen;
+ if (lenlen > 2 || 1 + lenlen + *len > datalen) {
+ return -1;
+ }
+
+ return nextpos;
+}
+
+int
+_libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen)
+{
+ unsigned int len;
+ int lenlen;
+
+ if (*datalen < 1) {
+ return -1;
+ }
+
+ if ((*data)[0] != '\x30') {
+ return -1;
+ }
+
+ (*data)++;
+ (*datalen)--;
+
+ lenlen = read_asn1_length(*data, *datalen, &len);
+ if (lenlen < 0 || lenlen + len != *datalen) {
+ return -1;
+ }
+
+ *data += lenlen;
+ *datalen -= lenlen;
+
+ return 0;
+}
+
+int
+_libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
+ unsigned char **i, unsigned int *ilen)
+{
+ unsigned int len;
+ int lenlen;
+
+ if (*datalen < 1) {
+ return -1;
+ }
+
+ if ((*data)[0] != '\x02') {
+ return -1;
+ }
+
+ (*data)++;
+ (*datalen)--;
+
+ lenlen = read_asn1_length(*data, *datalen, &len);
+ if (lenlen < 0 || lenlen + len > *datalen) {
+ return -1;
+ }
+
+ *data += lenlen;
+ *datalen -= lenlen;
+
+ *i = *data;
+ *ilen = len;
+
+ *data += len;
+ *datalen -= len;
+
+ return 0;
+}
diff --git a/libs/libssh2/src/publickey.c b/libs/libssh2/src/publickey.c
new file mode 100644
index 0000000000..bfee0a8420
--- /dev/null
+++ b/libs/libssh2/src/publickey.c
@@ -0,0 +1,1059 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2010-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include "libssh2_publickey.h"
+#include "channel.h"
+#include "session.h"
+
+#define LIBSSH2_PUBLICKEY_VERSION 2
+
+/* Numericised response codes -- Not IETF, just local representation */
+#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0
+#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1
+#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2
+
+typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST
+{
+ int code;
+ const char *name;
+ int name_len;
+} LIBSSH2_PUBLICKEY_CODE_LIST;
+
+static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_response_codes[] =
+{
+ {LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1},
+ {LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1},
+ {LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey",
+ sizeof("publickey") - 1} ,
+ {0, NULL, 0}
+};
+
+/* PUBLICKEY status codes -- IETF defined */
+#define LIBSSH2_PUBLICKEY_SUCCESS 0
+#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1
+#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2
+#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3
+#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4
+#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5
+#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6
+#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7
+#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8
+
+#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8
+
+static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_status_codes[] = {
+ {LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1} ,
+ {LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied",
+ sizeof("access denied") - 1},
+ {LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded",
+ sizeof("storage exceeded") - 1} ,
+ {LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported",
+ sizeof("version not supported") - 1} ,
+ {LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found",
+ sizeof("key not found") - 1},
+ {LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported",
+ sizeof("key not supported") - 1},
+ {LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present",
+ sizeof("key already present") - 1},
+ {LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure",
+ sizeof("general failure") - 1},
+ {LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported",
+ sizeof("request not supported") - 1},
+ {0, NULL, 0}
+};
+
+/*
+ * publickey_status_error
+ *
+ * Format an error message from a status code
+ */
+static void
+publickey_status_error(const LIBSSH2_PUBLICKEY *pkey,
+ LIBSSH2_SESSION *session, int status)
+{
+ const char *msg;
+
+ /* GENERAL_FAILURE got remapped between version 1 and 2 */
+ if (status == 6 && pkey && pkey->version == 1) {
+ status = 7;
+ }
+
+ if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) {
+ msg = "unknown";
+ } else {
+ msg = publickey_status_codes[status].name;
+ }
+
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, msg);
+}
+
+/*
+ * publickey_packet_receive
+ *
+ * Read a packet from the subsystem
+ */
+static int
+publickey_packet_receive(LIBSSH2_PUBLICKEY * pkey,
+ unsigned char **data, size_t *data_len)
+{
+ LIBSSH2_CHANNEL *channel = pkey->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char buffer[4];
+ int rc;
+ *data = NULL; /* default to nothing returned */
+ *data_len = 0;
+
+ if (pkey->receive_state == libssh2_NB_state_idle) {
+ rc = _libssh2_channel_read(channel, 0, (char *) buffer, 4);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc != 4) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid response from publickey subsystem");
+ }
+
+ pkey->receive_packet_len = _libssh2_ntohu32(buffer);
+ pkey->receive_packet =
+ LIBSSH2_ALLOC(session, pkey->receive_packet_len);
+ if (!pkey->receive_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate publickey response "
+ "buffer");
+ }
+
+ pkey->receive_state = libssh2_NB_state_sent;
+ }
+
+ if (pkey->receive_state == libssh2_NB_state_sent) {
+ rc = _libssh2_channel_read(channel, 0, (char *) pkey->receive_packet,
+ pkey->receive_packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc != (int)pkey->receive_packet_len) {
+ LIBSSH2_FREE(session, pkey->receive_packet);
+ pkey->receive_packet = NULL;
+ pkey->receive_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for publickey subsystem "
+ "response packet");
+ }
+
+ *data = pkey->receive_packet;
+ *data_len = pkey->receive_packet_len;
+ }
+
+ pkey->receive_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/* publickey_response_id
+ *
+ * Translate a string response name to a numeric code
+ * Data will be incremented by 4 + response_len on success only
+ */
+static int
+publickey_response_id(unsigned char **pdata, size_t data_len)
+{
+ size_t response_len;
+ unsigned char *data = *pdata;
+ const LIBSSH2_PUBLICKEY_CODE_LIST *codes = publickey_response_codes;
+
+ if (data_len < 4) {
+ /* Malformed response */
+ return -1;
+ }
+ response_len = _libssh2_ntohu32(data);
+ data += 4;
+ data_len -= 4;
+ if (data_len < response_len) {
+ /* Malformed response */
+ return -1;
+ }
+
+ while (codes->name) {
+ if ((unsigned long)codes->name_len == response_len &&
+ strncmp(codes->name, (char *) data, response_len) == 0) {
+ *pdata = data + response_len;
+ return codes->code;
+ }
+ codes++;
+ }
+
+ return -1;
+}
+
+/* publickey_response_success
+ *
+ * Generic helper routine to wait for success response and nothing else
+ */
+static int
+publickey_response_success(LIBSSH2_PUBLICKEY * pkey)
+{
+ LIBSSH2_SESSION *session = pkey->channel->session;
+ unsigned char *data, *s;
+ size_t data_len;
+ int response;
+
+ while (1) {
+ int rc = publickey_packet_receive(pkey, &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for response from "
+ "publickey subsystem");
+ }
+
+ s = data;
+ response = publickey_response_id(&s, data_len);
+
+ switch (response) {
+ case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
+ /* Error, or processing complete */
+ {
+ unsigned long status = _libssh2_ntohu32(s);
+
+ LIBSSH2_FREE(session, data);
+
+ if (status == LIBSSH2_PUBLICKEY_SUCCESS)
+ return 0;
+
+ publickey_status_error(pkey, session, status);
+ return -1;
+ }
+ default:
+ LIBSSH2_FREE(session, data);
+ if (response < 0) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid publickey subsystem response");
+ }
+ /* Unknown/Unexpected */
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Unexpected publickey subsystem response");
+ data = NULL;
+ }
+ }
+ /* never reached, but include `return` to silence compiler warnings */
+ return -1;
+}
+
+/* *****************
+ * Publickey API *
+ ***************** */
+
+/*
+ * publickey_init
+ *
+ * Startup the publickey subsystem
+ */
+static LIBSSH2_PUBLICKEY *publickey_init(LIBSSH2_SESSION *session)
+{
+ int response;
+ int rc;
+
+ if (session->pkeyInit_state == libssh2_NB_state_idle) {
+ session->pkeyInit_data = NULL;
+ session->pkeyInit_pkey = NULL;
+ session->pkeyInit_channel = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Initializing publickey subsystem");
+
+ session->pkeyInit_state = libssh2_NB_state_allocated;
+ }
+
+ if (session->pkeyInit_state == libssh2_NB_state_allocated) {
+
+ session->pkeyInit_channel =
+ _libssh2_channel_open(session, "session",
+ sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
+ 0);
+ if (!session->pkeyInit_channel) {
+ if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
+ /* The error state is already set, so leave it */
+ return NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to startup channel");
+ goto err_exit;
+ }
+
+ session->pkeyInit_state = libssh2_NB_state_sent;
+ }
+
+ if (session->pkeyInit_state == libssh2_NB_state_sent) {
+ rc = _libssh2_channel_process_startup(session->pkeyInit_channel,
+ "subsystem",
+ sizeof("subsystem") - 1,
+ "publickey",
+ sizeof("publickey") - 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting publickey subsystem");
+ return NULL;
+ } else if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to request publickey subsystem");
+ goto err_exit;
+ }
+
+ session->pkeyInit_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->pkeyInit_state == libssh2_NB_state_sent1) {
+ unsigned char *s;
+ rc = _libssh2_channel_extended_data(session->pkeyInit_channel,
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting publickey subsystem");
+ return NULL;
+ }
+
+ session->pkeyInit_pkey =
+ LIBSSH2_CALLOC(session, sizeof(LIBSSH2_PUBLICKEY));
+ if (!session->pkeyInit_pkey) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a new publickey structure");
+ goto err_exit;
+ }
+ session->pkeyInit_pkey->channel = session->pkeyInit_channel;
+ session->pkeyInit_pkey->version = 0;
+
+ s = session->pkeyInit_buffer;
+ _libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4);
+ s += 4;
+ _libssh2_htonu32(s, sizeof("version") - 1);
+ s += 4;
+ memcpy(s, "version", sizeof("version") - 1);
+ s += sizeof("version") - 1;
+ _libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION);
+
+ session->pkeyInit_buffer_sent = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey advertising version %d support",
+ (int) LIBSSH2_PUBLICKEY_VERSION);
+
+ session->pkeyInit_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->pkeyInit_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_channel_write(session->pkeyInit_channel, 0,
+ session->pkeyInit_buffer,
+ 19 - session->pkeyInit_buffer_sent);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending publickey version packet");
+ return NULL;
+ } else if (rc < 0) {
+ _libssh2_error(session, rc,
+ "Unable to send publickey version packet");
+ goto err_exit;
+ }
+ session->pkeyInit_buffer_sent += rc;
+ if(session->pkeyInit_buffer_sent < 19) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Need to be called again to complete this");
+ return NULL;
+ }
+
+ session->pkeyInit_state = libssh2_NB_state_sent3;
+ }
+
+ if (session->pkeyInit_state == libssh2_NB_state_sent3) {
+ while (1) {
+ unsigned char *s;
+ rc = publickey_packet_receive(session->pkeyInit_pkey,
+ &session->pkeyInit_data,
+ &session->pkeyInit_data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response from "
+ "publickey subsystem");
+ return NULL;
+ } else if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for response from "
+ "publickey subsystem");
+ goto err_exit;
+ }
+
+ s = session->pkeyInit_data;
+ if ((response =
+ publickey_response_id(&s, session->pkeyInit_data_len)) < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid publickey subsystem response code");
+ goto err_exit;
+ }
+
+ switch (response) {
+ case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
+ /* Error */
+ {
+ unsigned long status, descr_len, lang_len;
+
+ status = _libssh2_ntohu32(s);
+ s += 4;
+ descr_len = _libssh2_ntohu32(s);
+ s += 4;
+ /* description starts here */
+ s += descr_len;
+ lang_len = _libssh2_ntohu32(s);
+ s += 4;
+ /* lang starts here */
+ s += lang_len;
+
+ if (s >
+ session->pkeyInit_data + session->pkeyInit_data_len) {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Malformed publickey subsystem packet");
+ goto err_exit;
+ }
+
+ publickey_status_error(NULL, session, status);
+
+ goto err_exit;
+ }
+
+ case LIBSSH2_PUBLICKEY_RESPONSE_VERSION:
+ /* What we want */
+ session->pkeyInit_pkey->version = _libssh2_ntohu32(s);
+ if (session->pkeyInit_pkey->version >
+ LIBSSH2_PUBLICKEY_VERSION) {
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Truncate remote publickey version from %lu",
+ session->pkeyInit_pkey->version);
+ session->pkeyInit_pkey->version =
+ LIBSSH2_PUBLICKEY_VERSION;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Enabling publickey subsystem version %lu",
+ session->pkeyInit_pkey->version);
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ session->pkeyInit_data = NULL;
+ session->pkeyInit_state = libssh2_NB_state_idle;
+ return session->pkeyInit_pkey;
+
+ default:
+ /* Unknown/Unexpected */
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Unexpected publickey subsystem response, "
+ "ignoring");
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ session->pkeyInit_data = NULL;
+ }
+ }
+ }
+
+ /* Never reached except by direct goto */
+ err_exit:
+ session->pkeyInit_state = libssh2_NB_state_sent4;
+ if (session->pkeyInit_channel) {
+ rc = _libssh2_channel_close(session->pkeyInit_channel);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block closing channel");
+ return NULL;
+ }
+ }
+ if (session->pkeyInit_pkey) {
+ LIBSSH2_FREE(session, session->pkeyInit_pkey);
+ session->pkeyInit_pkey = NULL;
+ }
+ if (session->pkeyInit_data) {
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ session->pkeyInit_data = NULL;
+ }
+ session->pkeyInit_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_publickey_init
+ *
+ * Startup the publickey subsystem
+ */
+LIBSSH2_API LIBSSH2_PUBLICKEY *
+libssh2_publickey_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_PUBLICKEY *ptr;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ publickey_init(session));
+ return ptr;
+}
+
+
+
+/*
+ * libssh2_publickey_add_ex
+ *
+ * Add a new public key entry
+ */
+LIBSSH2_API int
+libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name,
+ unsigned long name_len, const unsigned char *blob,
+ unsigned long blob_len, char overwrite,
+ unsigned long num_attrs,
+ const libssh2_publickey_attribute attrs[])
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_SESSION *session;
+ /* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name}
+ blob_len(4) + {blob} */
+ unsigned long i, packet_len = 19 + name_len + blob_len;
+ unsigned char *comment = NULL;
+ unsigned long comment_len = 0;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ channel = pkey->channel;
+ session = channel->session;
+
+ if (pkey->add_state == libssh2_NB_state_idle) {
+ pkey->add_packet = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, "Adding %s publickey",
+ name);
+
+ if (pkey->version == 1) {
+ for(i = 0; i < num_attrs; i++) {
+ /* Search for a comment attribute */
+ if (attrs[i].name_len == (sizeof("comment") - 1) &&
+ strncmp(attrs[i].name, "comment",
+ sizeof("comment") - 1) == 0) {
+ comment = (unsigned char *) attrs[i].value;
+ comment_len = attrs[i].value_len;
+ break;
+ }
+ }
+ packet_len += 4 + comment_len;
+ } else {
+ packet_len += 5; /* overwrite(1) + attribute_count(4) */
+ for(i = 0; i < num_attrs; i++) {
+ packet_len += 9 + attrs[i].name_len + attrs[i].value_len;
+ /* name_len(4) + value_len(4) + mandatory(1) */
+ }
+ }
+
+ pkey->add_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!pkey->add_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey \"add\" packet");
+ }
+
+ pkey->add_s = pkey->add_packet;
+ _libssh2_htonu32(pkey->add_s, packet_len - 4);
+ pkey->add_s += 4;
+ _libssh2_htonu32(pkey->add_s, sizeof("add") - 1);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, "add", sizeof("add") - 1);
+ pkey->add_s += sizeof("add") - 1;
+ if (pkey->version == 1) {
+ _libssh2_htonu32(pkey->add_s, comment_len);
+ pkey->add_s += 4;
+ if (comment) {
+ memcpy(pkey->add_s, comment, comment_len);
+ pkey->add_s += comment_len;
+ }
+
+ _libssh2_htonu32(pkey->add_s, name_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, name, name_len);
+ pkey->add_s += name_len;
+ _libssh2_htonu32(pkey->add_s, blob_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, blob, blob_len);
+ pkey->add_s += blob_len;
+ } else {
+ /* Version == 2 */
+
+ _libssh2_htonu32(pkey->add_s, name_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, name, name_len);
+ pkey->add_s += name_len;
+ _libssh2_htonu32(pkey->add_s, blob_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, blob, blob_len);
+ pkey->add_s += blob_len;
+ *(pkey->add_s++) = overwrite ? 0x01 : 0;
+ _libssh2_htonu32(pkey->add_s, num_attrs);
+ pkey->add_s += 4;
+ for(i = 0; i < num_attrs; i++) {
+ _libssh2_htonu32(pkey->add_s, attrs[i].name_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len);
+ pkey->add_s += attrs[i].name_len;
+ _libssh2_htonu32(pkey->add_s, attrs[i].value_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len);
+ pkey->add_s += attrs[i].value_len;
+ *(pkey->add_s++) = attrs[i].mandatory ? 0x01 : 0;
+ }
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey \"add\" packet: "
+ "type=%s blob_len=%ld num_attrs=%ld",
+ name, blob_len, num_attrs);
+
+ pkey->add_state = libssh2_NB_state_created;
+ }
+
+ if (pkey->add_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, pkey->add_packet,
+ (pkey->add_s - pkey->add_packet));
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if ((pkey->add_s - pkey->add_packet) != rc) {
+ LIBSSH2_FREE(session, pkey->add_packet);
+ pkey->add_packet = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send publickey add packet");
+ }
+ LIBSSH2_FREE(session, pkey->add_packet);
+ pkey->add_packet = NULL;
+
+ pkey->add_state = libssh2_NB_state_sent;
+ }
+
+ rc = publickey_response_success(pkey);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+
+ pkey->add_state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+/* libssh2_publickey_remove_ex
+ * Remove an existing publickey so that authentication can no longer be
+ * performed using it
+ */
+LIBSSH2_API int
+libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY * pkey,
+ const unsigned char *name, unsigned long name_len,
+ const unsigned char *blob, unsigned long blob_len)
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_SESSION *session;
+ /* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name}
+ + blob_len(4) + {blob} */
+ unsigned long packet_len = 22 + name_len + blob_len;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ channel = pkey->channel;
+ session = channel->session;
+
+ if (pkey->remove_state == libssh2_NB_state_idle) {
+ pkey->remove_packet = NULL;
+
+ pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!pkey->remove_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey \"remove\" packet");
+ }
+
+ pkey->remove_s = pkey->remove_packet;
+ _libssh2_htonu32(pkey->remove_s, packet_len - 4);
+ pkey->remove_s += 4;
+ _libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1);
+ pkey->remove_s += 4;
+ memcpy(pkey->remove_s, "remove", sizeof("remove") - 1);
+ pkey->remove_s += sizeof("remove") - 1;
+ _libssh2_htonu32(pkey->remove_s, name_len);
+ pkey->remove_s += 4;
+ memcpy(pkey->remove_s, name, name_len);
+ pkey->remove_s += name_len;
+ _libssh2_htonu32(pkey->remove_s, blob_len);
+ pkey->remove_s += 4;
+ memcpy(pkey->remove_s, blob, blob_len);
+ pkey->remove_s += blob_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey \"remove\" packet: "
+ "type=%s blob_len=%ld",
+ name, blob_len);
+
+ pkey->remove_state = libssh2_NB_state_created;
+ }
+
+ if (pkey->remove_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, pkey->remove_packet,
+ (pkey->remove_s - pkey->remove_packet));
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if ((pkey->remove_s - pkey->remove_packet) != rc) {
+ LIBSSH2_FREE(session, pkey->remove_packet);
+ pkey->remove_packet = NULL;
+ pkey->remove_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send publickey remove packet");
+ }
+ LIBSSH2_FREE(session, pkey->remove_packet);
+ pkey->remove_packet = NULL;
+
+ pkey->remove_state = libssh2_NB_state_sent;
+ }
+
+ rc = publickey_response_success(pkey);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+
+ pkey->remove_state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+/* libssh2_publickey_list_fetch
+ * Fetch a list of supported public key from a server
+ */
+LIBSSH2_API int
+libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY * pkey, unsigned long *num_keys,
+ libssh2_publickey_list ** pkey_list)
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_SESSION *session;
+ libssh2_publickey_list *list = NULL;
+ unsigned long buffer_len = 12, keys = 0, max_keys = 0, i;
+ /* 12 = packet_len(4) + list_len(4) + "list"(4) */
+ int response;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ channel = pkey->channel;
+ session = channel->session;
+
+ if (pkey->listFetch_state == libssh2_NB_state_idle) {
+ pkey->listFetch_data = NULL;
+
+ pkey->listFetch_s = pkey->listFetch_buffer;
+ _libssh2_htonu32(pkey->listFetch_s, buffer_len - 4);
+ pkey->listFetch_s += 4;
+ _libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1);
+ pkey->listFetch_s += 4;
+ memcpy(pkey->listFetch_s, "list", sizeof("list") - 1);
+ pkey->listFetch_s += sizeof("list") - 1;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey \"list\" packet");
+
+ pkey->listFetch_state = libssh2_NB_state_created;
+ }
+
+ if (pkey->listFetch_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0,
+ pkey->listFetch_buffer,
+ (pkey->listFetch_s -
+ pkey->listFetch_buffer));
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if ((pkey->listFetch_s - pkey->listFetch_buffer) != rc) {
+ pkey->listFetch_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send publickey list packet");
+ }
+
+ pkey->listFetch_state = libssh2_NB_state_sent;
+ }
+
+ while (1) {
+ rc = publickey_packet_receive(pkey, &pkey->listFetch_data,
+ &pkey->listFetch_data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for response from "
+ "publickey subsystem");
+ goto err_exit;
+ }
+
+ pkey->listFetch_s = pkey->listFetch_data;
+ if ((response =
+ publickey_response_id(&pkey->listFetch_s,
+ pkey->listFetch_data_len)) < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid publickey subsystem response code");
+ goto err_exit;
+ }
+
+ switch (response) {
+ case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
+ /* Error, or processing complete */
+ {
+ unsigned long status, descr_len, lang_len;
+
+ status = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ descr_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ /* description starts at pkey->listFetch_s */
+ pkey->listFetch_s += descr_len;
+ lang_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ /* lang starts at pkey->listFetch_s */
+ pkey->listFetch_s += lang_len;
+
+ if (pkey->listFetch_s >
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Malformed publickey subsystem packet");
+ goto err_exit;
+ }
+
+ if (status == LIBSSH2_PUBLICKEY_SUCCESS) {
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ *pkey_list = list;
+ *num_keys = keys;
+ pkey->listFetch_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ publickey_status_error(pkey, session, status);
+ goto err_exit;
+ }
+ case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY:
+ /* What we want */
+ if (keys >= max_keys) {
+ libssh2_publickey_list *newlist;
+ /* Grow the key list if necessary */
+ max_keys += 8;
+ newlist =
+ LIBSSH2_REALLOC(session, list,
+ (max_keys +
+ 1) * sizeof(libssh2_publickey_list));
+ if (!newlist) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey list");
+ goto err_exit;
+ }
+ list = newlist;
+ }
+ if (pkey->version == 1) {
+ unsigned long comment_len;
+
+ comment_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ if (comment_len) {
+ list[keys].num_attrs = 1;
+ list[keys].attrs =
+ LIBSSH2_ALLOC(session,
+ sizeof(libssh2_publickey_attribute));
+ if (!list[keys].attrs) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey attributes");
+ goto err_exit;
+ }
+ list[keys].attrs[0].name = "comment";
+ list[keys].attrs[0].name_len = sizeof("comment") - 1;
+ list[keys].attrs[0].value = (char *) pkey->listFetch_s;
+ list[keys].attrs[0].value_len = comment_len;
+ list[keys].attrs[0].mandatory = 0;
+
+ pkey->listFetch_s += comment_len;
+ } else {
+ list[keys].num_attrs = 0;
+ list[keys].attrs = NULL;
+ }
+ list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ list[keys].name = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].name_len;
+ list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ list[keys].blob = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].blob_len;
+ } else {
+ /* Version == 2 */
+ list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ list[keys].name = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].name_len;
+ list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ list[keys].blob = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].blob_len;
+ list[keys].num_attrs = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ if (list[keys].num_attrs) {
+ list[keys].attrs =
+ LIBSSH2_ALLOC(session,
+ list[keys].num_attrs *
+ sizeof(libssh2_publickey_attribute));
+ if (!list[keys].attrs) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey attributes");
+ goto err_exit;
+ }
+ for(i = 0; i < list[keys].num_attrs; i++) {
+ list[keys].attrs[i].name_len =
+ _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ list[keys].attrs[i].name = (char *) pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].attrs[i].name_len;
+ list[keys].attrs[i].value_len =
+ _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ list[keys].attrs[i].value = (char *) pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].attrs[i].value_len;
+
+ /* actually an ignored value */
+ list[keys].attrs[i].mandatory = 0;
+ }
+ } else {
+ list[keys].attrs = NULL;
+ }
+ }
+ /* To be FREEd in libssh2_publickey_list_free() */
+ list[keys].packet = pkey->listFetch_data;
+ keys++;
+
+ list[keys].packet = NULL; /* Terminate the list */
+ pkey->listFetch_data = NULL;
+ break;
+ default:
+ /* Unknown/Unexpected */
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Unexpected publickey subsystem response");
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ }
+ }
+
+ /* Only reached via explicit goto */
+ err_exit:
+ if (pkey->listFetch_data) {
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ }
+ if (list) {
+ libssh2_publickey_list_free(pkey, list);
+ }
+ pkey->listFetch_state = libssh2_NB_state_idle;
+ return -1;
+}
+
+/* libssh2_publickey_list_free
+ * Free a previously fetched list of public keys
+ */
+LIBSSH2_API void
+libssh2_publickey_list_free(LIBSSH2_PUBLICKEY * pkey,
+ libssh2_publickey_list * pkey_list)
+{
+ LIBSSH2_SESSION *session;
+ libssh2_publickey_list *p = pkey_list;
+
+ if(!pkey || !p)
+ return;
+
+ session = pkey->channel->session;
+
+ while (p->packet) {
+ if (p->attrs) {
+ LIBSSH2_FREE(session, p->attrs);
+ }
+ LIBSSH2_FREE(session, p->packet);
+ p++;
+ }
+
+ LIBSSH2_FREE(session, pkey_list);
+}
+
+/* libssh2_publickey_shutdown
+ * Shutdown the publickey subsystem
+ */
+LIBSSH2_API int
+libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
+{
+ LIBSSH2_SESSION *session;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = pkey->channel->session;
+
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if (pkey->receive_packet) {
+ LIBSSH2_FREE(session, pkey->receive_packet);
+ pkey->receive_packet = NULL;
+ }
+ if (pkey->add_packet) {
+ LIBSSH2_FREE(session, pkey->add_packet);
+ pkey->add_packet = NULL;
+ }
+ if (pkey->remove_packet) {
+ LIBSSH2_FREE(session, pkey->remove_packet);
+ pkey->remove_packet = NULL;
+ }
+ if (pkey->listFetch_data) {
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ }
+
+ rc = _libssh2_channel_free(pkey->channel);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ LIBSSH2_FREE(session, pkey);
+ return 0;
+}
diff --git a/libs/libssh2/src/scp.c b/libs/libssh2/src/scp.c
new file mode 100644
index 0000000000..22778dd38a
--- /dev/null
+++ b/libs/libssh2/src/scp.c
@@ -0,0 +1,1126 @@
+/* Copyright (c) 2009-2010 by Daniel Stenberg
+ * Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include <errno.h>
+#include <stdlib.h>
+
+#include "channel.h"
+#include "session.h"
+
+
+/* Max. length of a quoted string after libssh2_shell_quotearg() processing */
+#define _libssh2_shell_quotedsize(s) (3 * strlen(s) + 2)
+
+/*
+ This function quotes a string in a way suitable to be used with a
+ shell, e.g. the file name
+ one two
+ becomes
+ 'one two'
+
+ The resulting output string is crafted in a way that makes it usable
+ with the two most common shell types: Bourne Shell derived shells
+ (sh, ksh, ksh93, bash, zsh) and C-Shell derivates (csh, tcsh).
+
+ The following special cases are handled:
+ o If the string contains an apostrophy itself, the apostrophy
+ character is written in quotation marks, e.g. "'".
+ The shell cannot handle the syntax 'doesn\'t', so we close the
+ current argument word, add the apostrophe in quotation marks "",
+ and open a new argument word instead (_ indicate the input
+ string characters):
+ _____ _ _
+ 'doesn' "'" 't'
+
+ Sequences of apostrophes are combined in one pair of quotation marks:
+ a'''b
+ becomes
+ _ ___ _
+ 'a'"'''"'b'
+
+ o If the string contains an exclamation mark (!), the C-Shell
+ interprets it as an event number. Using \! (not within quotation
+ marks or single quotation marks) is a mechanism understood by
+ both Bourne Shell and C-Shell.
+
+ If a quotation was already started, the argument word is closed
+ first:
+ a!b
+
+ become
+ _ _ _
+ 'a'\!'b'
+
+ The result buffer must be large enough for the expanded result. A
+ bad case regarding expansion is alternating characters and
+ apostrophes:
+
+ a'b'c'd' (length 8) gets converted to
+ 'a'"'"'b'"'"'c'"'"'d'"'" (length 24)
+
+ This is the worst case.
+
+ Maximum length of the result:
+ 1 + 6 * (length(input) + 1) / 2) + 1
+
+ => 3 * length(input) + 2
+
+ Explanation:
+ o leading apostrophe
+ o one character / apostrophe pair (two characters) can get
+ represented as 6 characters: a' -> a'"'"'
+ o String terminator (+1)
+
+ A result buffer three times the size of the input buffer + 2
+ characters should be safe.
+
+ References:
+ o csh-compatible quotation (special handling for '!' etc.), see
+ http://www.grymoire.com/Unix/Csh.html#toc-uh-10
+
+ Return value:
+ Length of the resulting string (not counting the terminating '\0'),
+ or 0 in case of errors, e.g. result buffer too small
+
+ Note: this function could possible be used elsewhere within libssh2, but
+ until then it is kept static and in this source file.
+*/
+
+static unsigned
+shell_quotearg(const char *path, unsigned char *buf,
+ unsigned bufsize)
+{
+ const char *src;
+ unsigned char *dst, *endp;
+
+ /*
+ * Processing States:
+ * UQSTRING: unquoted string: ... -- used for quoting exclamation
+ * marks. This is the initial state
+ * SQSTRING: single-quoted-string: '... -- any character may follow
+ * QSTRING: quoted string: "... -- only apostrophes may follow
+ */
+ enum { UQSTRING, SQSTRING, QSTRING } state = UQSTRING;
+
+ endp = &buf[bufsize];
+ src = path;
+ dst = buf;
+ while (*src && dst < endp - 1) {
+
+ switch (*src) {
+ /*
+ * Special handling for apostrophe.
+ * An apostrophe is always written in quotation marks, e.g.
+ * ' -> "'".
+ */
+
+ case '\'':
+ switch (state) {
+ case UQSTRING: /* Unquoted string */
+ if (dst+1 >= endp)
+ return 0;
+ *dst++ = '"';
+ break;
+ case QSTRING: /* Continue quoted string */
+ break;
+ case SQSTRING: /* Close single quoted string */
+ if (dst+2 >= endp)
+ return 0;
+ *dst++ = '\'';
+ *dst++ = '"';
+ break;
+ default:
+ break;
+ }
+ state = QSTRING;
+ break;
+
+ /*
+ * Special handling for exclamation marks. CSH interprets
+ * exclamation marks even when quoted with apostrophes. We convert
+ * it to the plain string \!, because both Bourne Shell and CSH
+ * interpret that as a verbatim exclamation mark.
+ */
+
+ case '!':
+ switch (state) {
+ case UQSTRING:
+ if (dst+1 >= endp)
+ return 0;
+ *dst++ = '\\';
+ break;
+ case QSTRING:
+ if (dst+2 >= endp)
+ return 0;
+ *dst++ = '"'; /* Closing quotation mark */
+ *dst++ = '\\';
+ break;
+ case SQSTRING: /* Close single quoted string */
+ if (dst+2 >= endp)
+ return 0;
+ *dst++ = '\'';
+ *dst++ = '\\';
+ break;
+ default:
+ break;
+ }
+ state = UQSTRING;
+ break;
+
+ /*
+ * Ordinary character: prefer single-quoted string
+ */
+
+ default:
+ switch (state) {
+ case UQSTRING:
+ if (dst+1 >= endp)
+ return 0;
+ *dst++ = '\'';
+ break;
+ case QSTRING:
+ if (dst+2 >= endp)
+ return 0;
+ *dst++ = '"'; /* Closing quotation mark */
+ *dst++ = '\'';
+ break;
+ case SQSTRING: /* Continue single quoted string */
+ break;
+ default:
+ break;
+ }
+ state = SQSTRING; /* Start single-quoted string */
+ break;
+ }
+
+ if (dst+1 >= endp)
+ return 0;
+ *dst++ = *src++;
+ }
+
+ switch (state) {
+ case UQSTRING:
+ break;
+ case QSTRING: /* Close quoted string */
+ if (dst+1 >= endp)
+ return 0;
+ *dst++ = '"';
+ break;
+ case SQSTRING: /* Close single quoted string */
+ if (dst+1 >= endp)
+ return 0;
+ *dst++ = '\'';
+ break;
+ default:
+ break;
+ }
+
+ if (dst+1 >= endp)
+ return 0;
+ *dst = '\0';
+
+ /* The result cannot be larger than 3 * strlen(path) + 2 */
+ /* assert((dst - buf) <= (3 * (src - path) + 2)); */
+
+ return dst - buf;
+}
+
+/*
+ * scp_recv
+ *
+ * Open a channel and request a remote file via SCP
+ *
+ */
+static LIBSSH2_CHANNEL *
+scp_recv(LIBSSH2_SESSION * session, const char *path, libssh2_struct_stat * sb)
+{
+ int cmd_len;
+ int rc;
+ int tmp_err_code;
+ const char *tmp_err_msg;
+
+ if (session->scpRecv_state == libssh2_NB_state_idle) {
+ session->scpRecv_mode = 0;
+ session->scpRecv_size = 0;
+ session->scpRecv_mtime = 0;
+ session->scpRecv_atime = 0;
+
+ session->scpRecv_command_len =
+ _libssh2_shell_quotedsize(path) + sizeof("scp -f ") + (sb?1:0);
+
+ session->scpRecv_command =
+ LIBSSH2_ALLOC(session, session->scpRecv_command_len);
+
+ if (!session->scpRecv_command) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a command buffer for "
+ "SCP session");
+ return NULL;
+ }
+
+ snprintf((char *)session->scpRecv_command,
+ session->scpRecv_command_len,
+ "scp -%sf ", sb?"p":"");
+
+ cmd_len = strlen((char *)session->scpRecv_command);
+ cmd_len += shell_quotearg(path,
+ &session->scpRecv_command[cmd_len],
+ session->scpRecv_command_len - cmd_len);
+
+ session->scpRecv_command[cmd_len] = '\0';
+ session->scpRecv_command_len = cmd_len + 1;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "Opening channel for SCP receive");
+
+ session->scpRecv_state = libssh2_NB_state_created;
+ }
+
+ if (session->scpRecv_state == libssh2_NB_state_created) {
+ /* Allocate a channel */
+ session->scpRecv_channel =
+ _libssh2_channel_open(session, "session",
+ sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
+ 0);
+ if (!session->scpRecv_channel) {
+ if (libssh2_session_last_errno(session) !=
+ LIBSSH2_ERROR_EAGAIN) {
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ session->scpRecv_command = NULL;
+ session->scpRecv_state = libssh2_NB_state_idle;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting up channel");
+ }
+ return NULL;
+ }
+
+ session->scpRecv_state = libssh2_NB_state_sent;
+ }
+
+ if (session->scpRecv_state == libssh2_NB_state_sent) {
+ /* Request SCP for the desired file */
+ rc = _libssh2_channel_process_startup(session->scpRecv_channel, "exec",
+ sizeof("exec") - 1,
+ (char *) session->scpRecv_command,
+ session->scpRecv_command_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting SCP startup");
+ return NULL;
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ session->scpRecv_command = NULL;
+ goto scp_recv_error;
+ }
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ session->scpRecv_command = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sending initial wakeup");
+ /* SCP ACK */
+ session->scpRecv_response[0] = '\0';
+
+ session->scpRecv_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->scpRecv_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_channel_write(session->scpRecv_channel, 0,
+ session->scpRecv_response, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending initial wakeup");
+ return NULL;
+ } else if (rc != 1) {
+ goto scp_recv_error;
+ }
+
+ /* Parse SCP response */
+ session->scpRecv_response_len = 0;
+
+ session->scpRecv_state = libssh2_NB_state_sent2;
+ }
+
+ if ((session->scpRecv_state == libssh2_NB_state_sent2)
+ || (session->scpRecv_state == libssh2_NB_state_sent3)) {
+ while (sb && (session->scpRecv_response_len <
+ LIBSSH2_SCP_RESPONSE_BUFLEN)) {
+ unsigned char *s, *p;
+
+ if (session->scpRecv_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_channel_read(session->scpRecv_channel, 0,
+ (char *) session->
+ scpRecv_response +
+ session->scpRecv_response_len, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for SCP response");
+ return NULL;
+ }
+ else if (rc < 0) {
+ /* error, give up */
+ _libssh2_error(session, rc, "Failed reading SCP response");
+ goto scp_recv_error;
+ }
+ else if(rc == 0)
+ goto scp_recv_empty_channel;
+
+ session->scpRecv_response_len++;
+
+ if (session->scpRecv_response[0] != 'T') {
+ size_t err_len;
+ char *err_msg;
+
+ /* there can be
+ 01 for warnings
+ 02 for errors
+
+ The following string MUST be newline terminated
+ */
+ err_len =
+ _libssh2_channel_packet_data_len(session->
+ scpRecv_channel, 0);
+ err_msg = LIBSSH2_ALLOC(session, err_len + 1);
+ if (!err_msg) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to get memory ");
+ goto scp_recv_error;
+ }
+
+ /* Read the remote error message */
+ (void)_libssh2_channel_read(session->scpRecv_channel, 0,
+ err_msg, err_len);
+ /* If it failed for any reason, we ignore it anyway. */
+
+ /* zero terminate the error */
+ err_msg[err_len]=0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "got %02x %s", session->scpRecv_response[0],
+ err_msg);
+
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Failed to recv file");
+
+ LIBSSH2_FREE(session, err_msg);
+ goto scp_recv_error;
+ }
+
+ if ((session->scpRecv_response_len > 1) &&
+ ((session->
+ scpRecv_response[session->scpRecv_response_len - 1] <
+ '0')
+ || (session->
+ scpRecv_response[session->scpRecv_response_len - 1] >
+ '9'))
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ ' ')
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\r')
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid data in SCP response");
+ goto scp_recv_error;
+ }
+
+ if ((session->scpRecv_response_len < 9)
+ || (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')) {
+ if (session->scpRecv_response_len ==
+ LIBSSH2_SCP_RESPONSE_BUFLEN) {
+ /* You had your chance */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unterminated response from SCP server");
+ goto scp_recv_error;
+ }
+ /* Way too short to be an SCP response, or not done yet,
+ short circuit */
+ continue;
+ }
+
+ /* We're guaranteed not to go under response_len == 0 by the
+ logic above */
+ while ((session->
+ scpRecv_response[session->scpRecv_response_len - 1] ==
+ '\r')
+ || (session->
+ scpRecv_response[session->scpRecv_response_len -
+ 1] == '\n'))
+ session->scpRecv_response_len--;
+ session->scpRecv_response[session->scpRecv_response_len] =
+ '\0';
+
+ if (session->scpRecv_response_len < 8) {
+ /* EOL came too soon */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "too short" );
+ goto scp_recv_error;
+ }
+
+ s = session->scpRecv_response + 1;
+
+ p = (unsigned char *) strchr((char *) s, ' ');
+ if (!p || ((p - s) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "malformed mtime");
+ goto scp_recv_error;
+ }
+
+ *(p++) = '\0';
+ /* Make sure we don't get fooled by leftover values */
+ session->scpRecv_mtime = strtol((char *) s, NULL, 10);
+
+ s = (unsigned char *) strchr((char *) p, ' ');
+ if (!s || ((s - p) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, malformed mtime.usec");
+ goto scp_recv_error;
+ }
+
+ /* Ignore mtime.usec */
+ s++;
+ p = (unsigned char *) strchr((char *) s, ' ');
+ if (!p || ((p - s) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, too short or malformed");
+ goto scp_recv_error;
+ }
+
+ *p = '\0';
+ /* Make sure we don't get fooled by leftover values */
+ session->scpRecv_atime = strtol((char *) s, NULL, 10);
+
+ /* SCP ACK */
+ session->scpRecv_response[0] = '\0';
+
+ session->scpRecv_state = libssh2_NB_state_sent3;
+ }
+
+ if (session->scpRecv_state == libssh2_NB_state_sent3) {
+ rc = _libssh2_channel_write(session->scpRecv_channel, 0,
+ session->scpRecv_response, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting to send SCP ACK");
+ return NULL;
+ } else if (rc != 1) {
+ goto scp_recv_error;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "mtime = %ld, atime = %ld",
+ session->scpRecv_mtime, session->scpRecv_atime);
+
+ /* We *should* check that atime.usec is valid, but why let
+ that stop use? */
+ break;
+ }
+ }
+
+ session->scpRecv_state = libssh2_NB_state_sent4;
+ }
+
+ if (session->scpRecv_state == libssh2_NB_state_sent4) {
+ session->scpRecv_response_len = 0;
+
+ session->scpRecv_state = libssh2_NB_state_sent5;
+ }
+
+ if ((session->scpRecv_state == libssh2_NB_state_sent5)
+ || (session->scpRecv_state == libssh2_NB_state_sent6)) {
+ while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
+ char *s, *p, *e = NULL;
+
+ if (session->scpRecv_state == libssh2_NB_state_sent5) {
+ rc = _libssh2_channel_read(session->scpRecv_channel, 0,
+ (char *) session->
+ scpRecv_response +
+ session->scpRecv_response_len, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for SCP response");
+ return NULL;
+ }
+ else if (rc < 0) {
+ /* error, bail out*/
+ _libssh2_error(session, rc, "Failed reading SCP response");
+ goto scp_recv_error;
+ }
+ else if(rc == 0)
+ goto scp_recv_empty_channel;
+
+ session->scpRecv_response_len++;
+
+ if (session->scpRecv_response[0] != 'C') {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server");
+ goto scp_recv_error;
+ }
+
+ if ((session->scpRecv_response_len > 1) &&
+ (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\r')
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')
+ &&
+ (session->
+ scpRecv_response[session->scpRecv_response_len - 1]
+ < 32)) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid data in SCP response");
+ goto scp_recv_error;
+ }
+
+ if ((session->scpRecv_response_len < 7)
+ || (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')) {
+ if (session->scpRecv_response_len ==
+ LIBSSH2_SCP_RESPONSE_BUFLEN) {
+ /* You had your chance */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unterminated response from SCP server");
+ goto scp_recv_error;
+ }
+ /* Way too short to be an SCP response, or not done yet,
+ short circuit */
+ continue;
+ }
+
+ /* We're guaranteed not to go under response_len == 0 by the
+ logic above */
+ while ((session->
+ scpRecv_response[session->scpRecv_response_len - 1] ==
+ '\r')
+ || (session->
+ scpRecv_response[session->scpRecv_response_len -
+ 1] == '\n')) {
+ session->scpRecv_response_len--;
+ }
+ session->scpRecv_response[session->scpRecv_response_len] =
+ '\0';
+
+ if (session->scpRecv_response_len < 6) {
+ /* EOL came too soon */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, too short");
+ goto scp_recv_error;
+ }
+
+ s = (char *) session->scpRecv_response + 1;
+
+ p = strchr(s, ' ');
+ if (!p || ((p - s) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, malformed mode");
+ goto scp_recv_error;
+ }
+
+ *(p++) = '\0';
+ /* Make sure we don't get fooled by leftover values */
+
+ session->scpRecv_mode = strtol(s, &e, 8);
+ if (e && *e) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, invalid mode");
+ goto scp_recv_error;
+ }
+
+ s = strchr(p, ' ');
+ if (!s || ((s - p) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, too short or malformed");
+ goto scp_recv_error;
+ }
+
+ *s = '\0';
+ /* Make sure we don't get fooled by leftover values */
+ session->scpRecv_size = scpsize_strtol(p, &e, 10);
+ if (e && *e) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, invalid size");
+ goto scp_recv_error;
+ }
+
+ /* SCP ACK */
+ session->scpRecv_response[0] = '\0';
+
+ session->scpRecv_state = libssh2_NB_state_sent6;
+ }
+
+ if (session->scpRecv_state == libssh2_NB_state_sent6) {
+ rc = _libssh2_channel_write(session->scpRecv_channel, 0,
+ session->scpRecv_response, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending SCP ACK");
+ return NULL;
+ } else if (rc != 1) {
+ goto scp_recv_error;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "mode = 0%lo size = %ld", session->scpRecv_mode,
+ session->scpRecv_size);
+
+ /* We *should* check that basename is valid, but why let that
+ stop us? */
+ break;
+ }
+ }
+
+ session->scpRecv_state = libssh2_NB_state_sent7;
+ }
+
+ if (sb) {
+ memset(sb, 0, sizeof(libssh2_struct_stat));
+
+ sb->st_mtime = session->scpRecv_mtime;
+ sb->st_atime = session->scpRecv_atime;
+ sb->st_size = session->scpRecv_size;
+ sb->st_mode = (unsigned short)session->scpRecv_mode;
+ }
+
+ session->scpRecv_state = libssh2_NB_state_idle;
+ return session->scpRecv_channel;
+
+ scp_recv_empty_channel:
+ /* the code only jumps here as a result of a zero read from channel_read()
+ so we check EOF status to avoid getting stuck in a loop */
+ if(libssh2_channel_eof(session->scpRecv_channel))
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unexpected channel close");
+ else
+ return session->scpRecv_channel;
+ /* fall-through */
+ scp_recv_error:
+ tmp_err_code = session->err_code;
+ tmp_err_msg = session->err_msg;
+ while (libssh2_channel_free(session->scpRecv_channel) ==
+ LIBSSH2_ERROR_EAGAIN);
+ session->err_code = tmp_err_code;
+ session->err_msg = tmp_err_msg;
+ session->scpRecv_channel = NULL;
+ session->scpRecv_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_scp_recv
+ *
+ * DEPRECATED
+ *
+ * Open a channel and request a remote file via SCP. This receives files larger
+ * than 2 GB, but is unable to report the proper size on platforms where the
+ * st_size member of struct stat is limited to 2 GB (e.g. windows).
+ *
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat * sb)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ /* scp_recv uses libssh2_struct_stat, so pass one if the caller gave us a struct to populate... */
+ libssh2_struct_stat sb_intl;
+ libssh2_struct_stat *sb_ptr;
+ sb_ptr = sb ? &sb_intl : NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb_ptr));
+
+ /* ...and populate the caller's with as much info as fits. */
+ if (sb) {
+ memset(sb, 0, sizeof(struct stat));
+
+ sb->st_mtime = sb_intl.st_mtime;
+ sb->st_atime = sb_intl.st_atime;
+ sb->st_size = (off_t)sb_intl.st_size;
+ sb->st_mode = sb_intl.st_mode;
+ }
+
+ return ptr;
+}
+
+/*
+ * libssh2_scp_recv2
+ *
+ * Open a channel and request a remote file via SCP. This supports files > 2GB
+ * on platforms that support it.
+ *
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_recv2(LIBSSH2_SESSION *session, const char *path, libssh2_struct_stat * sb)
+{
+ LIBSSH2_CHANNEL *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb));
+ return ptr;
+}
+
+/*
+ * scp_send()
+ *
+ * Send a file using SCP
+ *
+ */
+static LIBSSH2_CHANNEL *
+scp_send(LIBSSH2_SESSION * session, const char *path, int mode,
+ libssh2_int64_t size, time_t mtime, time_t atime)
+{
+ int cmd_len;
+ int rc;
+ int tmp_err_code;
+ const char *tmp_err_msg;
+
+ if (session->scpSend_state == libssh2_NB_state_idle) {
+ session->scpSend_command_len =
+ _libssh2_shell_quotedsize(path) + sizeof("scp -t ") +
+ ((mtime || atime)?1:0);
+
+ session->scpSend_command =
+ LIBSSH2_ALLOC(session, session->scpSend_command_len);
+
+ if (!session->scpSend_command) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a command buffer for "
+ "SCP session");
+ return NULL;
+ }
+
+ snprintf((char *)session->scpSend_command,
+ session->scpSend_command_len,
+ "scp -%st ", (mtime || atime)?"p":"");
+
+ cmd_len = strlen((char *)session->scpSend_command);
+ cmd_len += shell_quotearg(path,
+ &session->scpSend_command[cmd_len],
+ session->scpSend_command_len - cmd_len);
+
+ session->scpSend_command[cmd_len] = '\0';
+ session->scpSend_command_len = cmd_len + 1;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "Opening channel for SCP send");
+ /* Allocate a channel */
+
+ session->scpSend_state = libssh2_NB_state_created;
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_created) {
+ session->scpSend_channel =
+ _libssh2_channel_open(session, "session", sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
+ if (!session->scpSend_channel) {
+ if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) {
+ /* previous call set libssh2_session_last_error(), pass it
+ through */
+ LIBSSH2_FREE(session, session->scpSend_command);
+ session->scpSend_command = NULL;
+ session->scpSend_state = libssh2_NB_state_idle;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting up channel");
+ }
+ return NULL;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent;
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_sent) {
+ /* Request SCP for the desired file */
+ rc = _libssh2_channel_process_startup(session->scpSend_channel, "exec",
+ sizeof("exec") - 1,
+ (char *) session->scpSend_command,
+ session->scpSend_command_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting SCP startup");
+ return NULL;
+ }
+ else if (rc) {
+ /* previous call set libssh2_session_last_error(), pass it
+ through */
+ LIBSSH2_FREE(session, session->scpSend_command);
+ session->scpSend_command = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unknown error while getting error string");
+ goto scp_send_error;
+ }
+ LIBSSH2_FREE(session, session->scpSend_command);
+ session->scpSend_command = NULL;
+
+ session->scpSend_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_sent1) {
+ /* Wait for ACK */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ (char *) session->scpSend_response, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response from remote");
+ return NULL;
+ }
+ else if (rc < 0) {
+ _libssh2_error(session, rc, "SCP failure");
+ goto scp_send_error;
+ }
+ else if(!rc)
+ /* remain in the same state */
+ goto scp_send_empty_channel;
+ else if (session->scpSend_response[0] != 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid ACK response from remote");
+ goto scp_send_error;
+ }
+ if (mtime || atime) {
+ /* Send mtime and atime to be used for file */
+ session->scpSend_response_len =
+ snprintf((char *) session->scpSend_response,
+ LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n",
+ (long)mtime, (long)atime);
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s",
+ session->scpSend_response);
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent2;
+ }
+
+ /* Send mtime and atime to be used for file */
+ if (mtime || atime) {
+ if (session->scpSend_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_channel_write(session->scpSend_channel, 0,
+ session->scpSend_response,
+ session->scpSend_response_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending time data for SCP file");
+ return NULL;
+ } else if (rc != (int)session->scpSend_response_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send time data for SCP file");
+ goto scp_send_error;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent3;
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_sent3) {
+ /* Wait for ACK */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ (char *) session->scpSend_response, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response");
+ return NULL;
+ }
+ else if (rc < 0) {
+ _libssh2_error(session, rc, "SCP failure");
+ goto scp_send_error;
+ }
+ else if(!rc)
+ /* remain in the same state */
+ goto scp_send_empty_channel;
+ else if (session->scpSend_response[0] != 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid SCP ACK response");
+ goto scp_send_error;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent4;
+ }
+ } else {
+ if (session->scpSend_state == libssh2_NB_state_sent2) {
+ session->scpSend_state = libssh2_NB_state_sent4;
+ }
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_sent4) {
+ /* Send mode, size, and basename */
+ const char *base = strrchr(path, '/');
+ if (base)
+ base++;
+ else
+ base = path;
+
+ session->scpSend_response_len =
+ snprintf((char *) session->scpSend_response,
+ LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %"
+ LIBSSH2_INT64_T_FORMAT " %s\n", mode,
+ size, base);
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s",
+ session->scpSend_response);
+
+ session->scpSend_state = libssh2_NB_state_sent5;
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_sent5) {
+ rc = _libssh2_channel_write(session->scpSend_channel, 0,
+ session->scpSend_response,
+ session->scpSend_response_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block send core file data for SCP file");
+ return NULL;
+ } else if (rc != (int)session->scpSend_response_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send core file data for SCP file");
+ goto scp_send_error;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent6;
+ }
+
+ if (session->scpSend_state == libssh2_NB_state_sent6) {
+ /* Wait for ACK */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ (char *) session->scpSend_response, 1);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response");
+ return NULL;
+ }
+ else if (rc < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid ACK response from remote");
+ goto scp_send_error;
+ }
+ else if (rc == 0)
+ goto scp_send_empty_channel;
+
+ else if (session->scpSend_response[0] != 0) {
+ size_t err_len;
+ char *err_msg;
+
+ err_len =
+ _libssh2_channel_packet_data_len(session->scpSend_channel, 0);
+ err_msg = LIBSSH2_ALLOC(session, err_len + 1);
+ if (!err_msg) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "failed to get memory");
+ goto scp_send_error;
+ }
+
+ /* Read the remote error message */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ err_msg, err_len);
+ if (rc > 0) {
+ err_msg[err_len]=0;
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "got %02x %s", session->scpSend_response[0],
+ err_msg);
+ }
+ LIBSSH2_FREE(session, err_msg);
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "failed to send file");
+ goto scp_send_error;
+ }
+ }
+
+ session->scpSend_state = libssh2_NB_state_idle;
+ return session->scpSend_channel;
+
+ scp_send_empty_channel:
+ /* the code only jumps here as a result of a zero read from channel_read()
+ so we check EOF status to avoid getting stuck in a loop */
+ if(libssh2_channel_eof(session->scpSend_channel)) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unexpected channel close");
+ }
+ else
+ return session->scpSend_channel;
+ /* fall-through */
+ scp_send_error:
+ tmp_err_code = session->err_code;
+ tmp_err_msg = session->err_msg;
+ while (libssh2_channel_free(session->scpSend_channel) ==
+ LIBSSH2_ERROR_EAGAIN);
+ session->err_code = tmp_err_code;
+ session->err_msg = tmp_err_msg;
+ session->scpSend_channel = NULL;
+ session->scpSend_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_scp_send_ex
+ *
+ * Send a file using SCP. Old API.
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode,
+ size_t size, long mtime, long atime)
+{
+ LIBSSH2_CHANNEL *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ scp_send(session, path, mode, size,
+ (time_t)mtime, (time_t)atime));
+ return ptr;
+}
+
+/*
+ * libssh2_scp_send64
+ *
+ * Send a file using SCP
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_send64(LIBSSH2_SESSION *session, const char *path, int mode,
+ libssh2_int64_t size, time_t mtime, time_t atime)
+{
+ LIBSSH2_CHANNEL *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ scp_send(session, path, mode, size, mtime, atime));
+ return ptr;
+}
diff --git a/libs/libssh2/src/session.c b/libs/libssh2/src/session.c
new file mode 100644
index 0000000000..6352d12ee7
--- /dev/null
+++ b/libs/libssh2/src/session.c
@@ -0,0 +1,1794 @@
+/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2015 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson <simon@josefsson.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifdef HAVE_GETTIMEOFDAY
+#include <sys/time.h>
+#endif
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "transport.h"
+#include "session.h"
+#include "channel.h"
+#include "mac.h"
+#include "misc.h"
+
+/* libssh2_default_alloc
+ */
+static
+LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
+{
+ (void) abstract;
+ return malloc(count);
+}
+
+/* libssh2_default_free
+ */
+static
+LIBSSH2_FREE_FUNC(libssh2_default_free)
+{
+ (void) abstract;
+ free(ptr);
+}
+
+/* libssh2_default_realloc
+ */
+static
+LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
+{
+ (void) abstract;
+ return realloc(ptr, count);
+}
+
+/*
+ * banner_receive
+ *
+ * Wait for a hello from the remote host
+ * Allocate a buffer and store the banner in session->remote.banner
+ * Returns: 0 on success, LIBSSH2_ERROR_EAGAIN if read would block, negative
+ * on failure
+ */
+static int
+banner_receive(LIBSSH2_SESSION * session)
+{
+ int ret;
+ int banner_len;
+
+ if (session->banner_TxRx_state == libssh2_NB_state_idle) {
+ banner_len = 0;
+
+ session->banner_TxRx_state = libssh2_NB_state_created;
+ } else {
+ banner_len = session->banner_TxRx_total_send;
+ }
+
+ while ((banner_len < (int) sizeof(session->banner_TxRx_banner)) &&
+ ((banner_len == 0)
+ || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) {
+ char c = '\0';
+
+ /* no incoming block yet! */
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
+
+ ret = LIBSSH2_RECV(session, &c, 1,
+ LIBSSH2_SOCKET_RECV_FLAGS(session));
+ if (ret < 0) {
+ if(session->api_block_mode || (ret != -EAGAIN))
+ /* ignore EAGAIN when non-blocking */
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error recving %d bytes: %d", 1, -ret);
+ }
+ else
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Recved %d bytes banner", ret);
+
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ session->socket_block_directions =
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ session->banner_TxRx_total_send = banner_len;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ /* Some kinda error */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+
+ if (ret == 0) {
+ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+ }
+
+ if (c == '\0') {
+ /* NULLs are not allowed in SSH banners */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_BANNER_RECV;
+ }
+
+ session->banner_TxRx_banner[banner_len++] = c;
+ }
+
+ while (banner_len &&
+ ((session->banner_TxRx_banner[banner_len - 1] == '\n') ||
+ (session->banner_TxRx_banner[banner_len - 1] == '\r'))) {
+ banner_len--;
+ }
+
+ /* From this point on, we are done here */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+
+ if (!banner_len)
+ return LIBSSH2_ERROR_BANNER_RECV;
+
+ session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
+ if (!session->remote.banner) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Error allocating space for remote banner");
+ }
+ memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len);
+ session->remote.banner[banner_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Received Banner: %s",
+ session->remote.banner);
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * banner_send
+ *
+ * Send the default banner, or the one set via libssh2_setopt_string
+ *
+ * Returns LIBSSH2_ERROR_EAGAIN if it would block - and if it does so, you
+ * should call this function again as soon as it is likely that more data can
+ * be sent, and this function should then be called with the same argument set
+ * (same data pointer and same data_len) until zero or failure is returned.
+ */
+static int
+banner_send(LIBSSH2_SESSION * session)
+{
+ char *banner = (char *) LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF;
+ int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1;
+ ssize_t ret;
+#ifdef LIBSSH2DEBUG
+ char banner_dup[256];
+#endif
+
+ if (session->banner_TxRx_state == libssh2_NB_state_idle) {
+ if (session->local.banner) {
+ /* setopt_string will have given us our \r\n characters */
+ banner_len = strlen((char *) session->local.banner);
+ banner = (char *) session->local.banner;
+ }
+#ifdef LIBSSH2DEBUG
+ /* Hack and slash to avoid sending CRLF in debug output */
+ if (banner_len < 256) {
+ memcpy(banner_dup, banner, banner_len - 2);
+ banner_dup[banner_len - 2] = '\0';
+ } else {
+ memcpy(banner_dup, banner, 255);
+ banner[255] = '\0';
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Sending Banner: %s",
+ banner_dup);
+#endif
+
+ session->banner_TxRx_state = libssh2_NB_state_created;
+ }
+
+ /* no outgoing block yet! */
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND;
+
+ ret = LIBSSH2_SEND(session,
+ banner + session->banner_TxRx_total_send,
+ banner_len - session->banner_TxRx_total_send,
+ LIBSSH2_SOCKET_SEND_FLAGS(session));
+ if (ret < 0)
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error sending %d bytes: %d",
+ banner_len - session->banner_TxRx_total_send, -ret);
+ else
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Sent %d/%d bytes at %p+%d", ret,
+ banner_len - session->banner_TxRx_total_send,
+ banner, session->banner_TxRx_total_send);
+
+ if (ret != (banner_len - session->banner_TxRx_total_send)) {
+ if (ret >= 0 || ret == -EAGAIN) {
+ /* the whole packet could not be sent, save the what was */
+ session->socket_block_directions =
+ LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ if (ret > 0)
+ session->banner_TxRx_total_send += ret;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+
+ /* Set the state back to idle */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+
+ return 0;
+}
+
+/*
+ * session_nonblock() sets the given socket to either blocking or
+ * non-blocking mode based on the 'nonblock' boolean argument. This function
+ * is copied from the libcurl sources with permission.
+ */
+static int
+session_nonblock(libssh2_socket_t sockfd, /* operate on this */
+ int nonblock /* TRUE or FALSE */ )
+{
+#undef SETBLOCK
+#define SETBLOCK 0
+#ifdef HAVE_O_NONBLOCK
+ /* most recent unix versions */
+ int flags;
+
+ flags = fcntl(sockfd, F_GETFL, 0);
+ if (nonblock)
+ return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+ else
+ return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
+#undef SETBLOCK
+#define SETBLOCK 1
+#endif
+
+#if defined(HAVE_FIONBIO) && (SETBLOCK == 0)
+ /* older unix versions and VMS*/
+ int flags;
+
+ flags = nonblock;
+ return ioctl(sockfd, FIONBIO, &flags);
+#undef SETBLOCK
+#define SETBLOCK 2
+#endif
+
+#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0)
+ /* Windows? */
+ unsigned long flags;
+ flags = nonblock;
+
+ return ioctlsocket(sockfd, FIONBIO, &flags);
+#undef SETBLOCK
+#define SETBLOCK 3
+#endif
+
+#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0)
+ /* presumably for Amiga */
+ return IoctlSocket(sockfd, FIONBIO, (long) nonblock);
+#undef SETBLOCK
+#define SETBLOCK 4
+#endif
+
+#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0)
+ /* BeOS */
+ long b = nonblock ? 1 : 0;
+ return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
+#undef SETBLOCK
+#define SETBLOCK 5
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+ return 0; /* returns success */
+#undef SETBLOCK
+#define SETBLOCK 6
+#endif
+
+#if (SETBLOCK == 0)
+#error "no non-blocking method was found/used/set"
+#endif
+}
+
+/*
+ * get_socket_nonblocking()
+ *
+ * gets the given blocking or non-blocking state of the socket.
+ */
+static int
+get_socket_nonblocking(int sockfd)
+{ /* operate on this */
+#undef GETBLOCK
+#define GETBLOCK 0
+#ifdef HAVE_O_NONBLOCK
+ /* most recent unix versions */
+ int flags;
+
+ if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
+ /* Assume blocking on error */
+ return 1;
+ }
+ return (flags & O_NONBLOCK);
+#undef GETBLOCK
+#define GETBLOCK 1
+#endif
+
+#if defined(WSAEWOULDBLOCK) && (GETBLOCK == 0)
+ /* Windows? */
+ unsigned int option_value;
+ socklen_t option_len = sizeof(option_value);
+
+ if (getsockopt
+ (sockfd, SOL_SOCKET, SO_ERROR, (void *) &option_value, &option_len)) {
+ /* Assume blocking on error */
+ return 1;
+ }
+ return (int) option_value;
+#undef GETBLOCK
+#define GETBLOCK 2
+#endif
+
+#if defined(HAVE_SO_NONBLOCK) && (GETBLOCK == 0)
+ /* BeOS */
+ long b;
+ if (getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) {
+ /* Assume blocking on error */
+ return 1;
+ }
+ return (int) b;
+#undef GETBLOCK
+#define GETBLOCK 5
+#endif
+
+#if defined(SO_STATE) && defined( __VMS ) && (GETBLOCK == 0)
+
+ /* VMS TCP/IP Services */
+
+ size_t sockstat = 0;
+ int callstat = 0;
+ size_t size = sizeof( int );
+
+ callstat = getsockopt(sockfd, SOL_SOCKET, SO_STATE,
+ (char *)&sockstat, &size);
+ if ( callstat == -1 ) return(0);
+ if ( (sockstat&SS_NBIO) )return(1);
+ return(0);
+
+#undef GETBLOCK
+#define GETBLOCK 6
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+ return 1; /* returns blocking */
+#undef GETBLOCK
+#define GETBLOCK 7
+#endif
+
+#if (GETBLOCK == 0)
+#error "no non-blocking method was found/used/get"
+#endif
+}
+
+/* libssh2_session_banner_set
+ * Set the local banner to use in the server handshake.
+ */
+LIBSSH2_API int
+libssh2_session_banner_set(LIBSSH2_SESSION * session, const char *banner)
+{
+ size_t banner_len = banner ? strlen(banner) : 0;
+
+ if (session->local.banner) {
+ LIBSSH2_FREE(session, session->local.banner);
+ session->local.banner = NULL;
+ }
+
+ if (!banner_len)
+ return 0;
+
+ session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3);
+ if (!session->local.banner) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for local banner");
+ }
+
+ memcpy(session->local.banner, banner, banner_len);
+
+ /* first zero terminate like this so that the debug output is nice */
+ session->local.banner[banner_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting local Banner: %s",
+ session->local.banner);
+ session->local.banner[banner_len++] = '\r';
+ session->local.banner[banner_len++] = '\n';
+ session->local.banner[banner_len] = '\0';
+
+ return 0;
+}
+
+/* libssh2_banner_set
+ * Set the local banner. DEPRECATED VERSION
+ */
+LIBSSH2_API int
+libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner)
+{
+ return libssh2_session_banner_set(session, banner);
+}
+
+/*
+ * libssh2_session_init_ex
+ *
+ * Allocate and initialize a libssh2 session structure. Allows for malloc
+ * callbacks in case the calling program has its own memory manager It's
+ * allowable (but unadvisable) to define some but not all of the malloc
+ * callbacks An additional pointer value may be optionally passed to be sent
+ * to the callbacks (so they know who's asking)
+ */
+LIBSSH2_API LIBSSH2_SESSION *
+libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
+ LIBSSH2_FREE_FUNC((*my_free)),
+ LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract)
+{
+ LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
+ LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
+ LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
+ LIBSSH2_SESSION *session;
+
+ if (my_alloc) {
+ local_alloc = my_alloc;
+ }
+ if (my_free) {
+ local_free = my_free;
+ }
+ if (my_realloc) {
+ local_realloc = my_realloc;
+ }
+
+ session = local_alloc(sizeof(LIBSSH2_SESSION), &abstract);
+ if (session) {
+ memset(session, 0, sizeof(LIBSSH2_SESSION));
+ session->alloc = local_alloc;
+ session->free = local_free;
+ session->realloc = local_realloc;
+ session->send = _libssh2_send;
+ session->recv = _libssh2_recv;
+ session->abstract = abstract;
+ session->api_timeout = 0; /* timeout-free API by default */
+ session->api_block_mode = 1; /* blocking API by default */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "New session resource allocated");
+ _libssh2_init_if_needed ();
+ }
+ return session;
+}
+
+/*
+ * libssh2_session_callback_set
+ *
+ * Set (or reset) a callback function
+ * Returns the prior address
+ *
+ * FIXME: this function relies on that we can typecast function pointers
+ * to void pointers, which isn't allowed in ISO C!
+ */
+LIBSSH2_API void *
+libssh2_session_callback_set(LIBSSH2_SESSION * session,
+ int cbtype, void *callback)
+{
+ void *oldcb;
+
+ switch (cbtype) {
+ case LIBSSH2_CALLBACK_IGNORE:
+ oldcb = session->ssh_msg_ignore;
+ session->ssh_msg_ignore = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_DEBUG:
+ oldcb = session->ssh_msg_debug;
+ session->ssh_msg_debug = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_DISCONNECT:
+ oldcb = session->ssh_msg_disconnect;
+ session->ssh_msg_disconnect = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_MACERROR:
+ oldcb = session->macerror;
+ session->macerror = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_X11:
+ oldcb = session->x11;
+ session->x11 = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_SEND:
+ oldcb = session->send;
+ session->send = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_RECV:
+ oldcb = session->recv;
+ session->recv = callback;
+ return oldcb;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting Callback %d", cbtype);
+
+ return NULL;
+}
+
+/*
+ * _libssh2_wait_socket()
+ *
+ * Utility function that waits for action on the socket. Returns 0 when ready
+ * to run again or error on timeout.
+ */
+int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time)
+{
+ int rc;
+ int seconds_to_next;
+ int dir;
+ int has_timeout;
+ long ms_to_next = 0;
+ long elapsed_ms;
+
+ /* since libssh2 often sets EAGAIN internally before this function is
+ called, we can decrease some amount of confusion in user programs by
+ resetting the error code in this function to reduce the risk of EAGAIN
+ being stored as error when a blocking function has returned */
+ session->err_code = LIBSSH2_ERROR_NONE;
+
+ rc = libssh2_keepalive_send (session, &seconds_to_next);
+ if (rc < 0)
+ return rc;
+
+ ms_to_next = seconds_to_next * 1000;
+
+ /* figure out what to wait for */
+ dir = libssh2_session_block_directions(session);
+
+ if(!dir) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Nothing to wait for in wait_socket");
+ /* To avoid that we hang below just because there's nothing set to
+ wait for, we timeout on 1 second to also avoid busy-looping
+ during this condition */
+ ms_to_next = 1000;
+ }
+
+ if (session->api_timeout > 0 &&
+ (seconds_to_next == 0 ||
+ ms_to_next > session->api_timeout)) {
+ time_t now = time (NULL);
+ elapsed_ms = (long)(1000*difftime(now, start_time));
+ if (elapsed_ms > session->api_timeout) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "API timeout expired");
+ }
+ ms_to_next = (session->api_timeout - elapsed_ms);
+ has_timeout = 1;
+ }
+ else if (ms_to_next > 0) {
+ has_timeout = 1;
+ }
+ else
+ has_timeout = 0;
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd sockets[1];
+
+ sockets[0].fd = session->socket_fd;
+ sockets[0].events = 0;
+ sockets[0].revents = 0;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
+ sockets[0].events |= POLLIN;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
+ sockets[0].events |= POLLOUT;
+
+ rc = poll(sockets, 1, has_timeout?ms_to_next: -1);
+ }
+#else
+ {
+ fd_set rfd;
+ fd_set wfd;
+ fd_set *writefd = NULL;
+ fd_set *readfd = NULL;
+ struct timeval tv;
+
+ tv.tv_sec = ms_to_next / 1000;
+ tv.tv_usec = (ms_to_next - tv.tv_sec*1000) * 1000;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) {
+ FD_ZERO(&rfd);
+ FD_SET(session->socket_fd, &rfd);
+ readfd = &rfd;
+ }
+
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
+ FD_ZERO(&wfd);
+ FD_SET(session->socket_fd, &wfd);
+ writefd = &wfd;
+ }
+
+ rc = select(session->socket_fd + 1, readfd, writefd, NULL,
+ has_timeout ? &tv : NULL);
+ }
+#endif
+ if(rc == 0) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Timed out waiting on socket");
+ }
+ if(rc < 0) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Error waiting on socket");
+ }
+
+ return 0; /* ready to try again */
+}
+
+static int
+session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock)
+{
+ int rc;
+
+ if (session->startup_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "session_startup for socket %d", sock);
+ if (LIBSSH2_INVALID_SOCKET == sock) {
+ /* Did we forget something? */
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_SOCKET,
+ "Bad socket provided");
+ }
+ session->socket_fd = sock;
+
+ session->socket_prev_blockstate =
+ !get_socket_nonblocking(session->socket_fd);
+
+ if (session->socket_prev_blockstate) {
+ /* If in blocking state change to non-blocking */
+ rc = session_nonblock(session->socket_fd, 1);
+ if (rc) {
+ return _libssh2_error(session, rc,
+ "Failed changing socket's "
+ "blocking state to non-blocking");
+ }
+ }
+
+ session->startup_state = libssh2_NB_state_created;
+ }
+
+ if (session->startup_state == libssh2_NB_state_created) {
+ rc = banner_send(session);
+ if (rc) {
+ return _libssh2_error(session, rc,
+ "Failed sending banner");
+ }
+ session->startup_state = libssh2_NB_state_sent;
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ }
+
+ if (session->startup_state == libssh2_NB_state_sent) {
+ do {
+ rc = banner_receive(session);
+ if (rc)
+ return _libssh2_error(session, rc,
+ "Failed getting banner");
+ } while(strncmp("SSH-", (char *)session->remote.banner, 4));
+
+ session->startup_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->startup_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_kex_exchange(session, 0, &session->startup_key_state);
+ if (rc)
+ return _libssh2_error(session, rc,
+ "Unable to exchange encryption keys");
+
+ session->startup_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->startup_state == libssh2_NB_state_sent2) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Requesting userauth service");
+
+ /* Request the userauth service */
+ session->startup_service[0] = SSH_MSG_SERVICE_REQUEST;
+ _libssh2_htonu32(session->startup_service + 1,
+ sizeof("ssh-userauth") - 1);
+ memcpy(session->startup_service + 5, "ssh-userauth",
+ sizeof("ssh-userauth") - 1);
+
+ session->startup_state = libssh2_NB_state_sent3;
+ }
+
+ if (session->startup_state == libssh2_NB_state_sent3) {
+ rc = _libssh2_transport_send(session, session->startup_service,
+ sizeof("ssh-userauth") + 5 - 1,
+ NULL, 0);
+ if (rc) {
+ return _libssh2_error(session, rc,
+ "Unable to ask for ssh-userauth service");
+ }
+
+ session->startup_state = libssh2_NB_state_sent4;
+ }
+
+ if (session->startup_state == libssh2_NB_state_sent4) {
+ rc = _libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT,
+ &session->startup_data,
+ &session->startup_data_len, 0, NULL, 0,
+ &session->startup_req_state);
+ if (rc)
+ return rc;
+
+ session->startup_service_length =
+ _libssh2_ntohu32(session->startup_data + 1);
+
+ if ((session->startup_service_length != (sizeof("ssh-userauth") - 1))
+ || strncmp("ssh-userauth", (char *) session->startup_data + 5,
+ session->startup_service_length)) {
+ LIBSSH2_FREE(session, session->startup_data);
+ session->startup_data = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid response received from server");
+ }
+ LIBSSH2_FREE(session, session->startup_data);
+ session->startup_data = NULL;
+
+ session->startup_state = libssh2_NB_state_idle;
+
+ return 0;
+ }
+
+ /* just for safety return some error */
+ return LIBSSH2_ERROR_INVAL;
+}
+
+/*
+ * libssh2_session_handshake()
+ *
+ * session: LIBSSH2_SESSION struct allocated and owned by the calling program
+ * sock: *must* be populated with an opened and connected socket.
+ *
+ * Returns: 0 on success, or non-zero on failure
+ */
+LIBSSH2_API int
+libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock)
+{
+ int rc;
+
+ BLOCK_ADJUST(rc, session, session_startup(session, sock) );
+
+ return rc;
+}
+
+/*
+ * libssh2_session_startup()
+ *
+ * DEPRECATED. Use libssh2_session_handshake() instead! This function is not
+ * portable enough.
+ *
+ * session: LIBSSH2_SESSION struct allocated and owned by the calling program
+ * sock: *must* be populated with an opened and connected socket.
+ *
+ * Returns: 0 on success, or non-zero on failure
+ */
+LIBSSH2_API int
+libssh2_session_startup(LIBSSH2_SESSION *session, int sock)
+{
+ return libssh2_session_handshake(session, (libssh2_socket_t) sock);
+}
+
+/*
+ * libssh2_session_free
+ *
+ * Frees the memory allocated to the session
+ * Also closes and frees any channels attached to this session
+ */
+static int
+session_free(LIBSSH2_SESSION *session)
+{
+ int rc;
+ LIBSSH2_PACKET *pkg;
+ LIBSSH2_CHANNEL *ch;
+ LIBSSH2_LISTENER *l;
+ int packets_left = 0;
+
+ if (session->free_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Freeing session resource",
+ session->remote.banner);
+
+ session->free_state = libssh2_NB_state_created;
+ }
+
+ if (session->free_state == libssh2_NB_state_created) {
+ while ((ch = _libssh2_list_first(&session->channels))) {
+
+ rc = _libssh2_channel_free(ch);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->free_state = libssh2_NB_state_sent;
+ }
+
+ if (session->free_state == libssh2_NB_state_sent) {
+ while ((l = _libssh2_list_first(&session->listeners))) {
+ rc = _libssh2_channel_forward_cancel(l);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->free_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->state & LIBSSH2_STATE_NEWKEYS) {
+ /* hostkey */
+ if (session->hostkey && session->hostkey->dtor) {
+ session->hostkey->dtor(session, &session->server_hostkey_abstract);
+ }
+
+ /* Client to Server */
+ /* crypt */
+ if (session->local.crypt && session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+ /* comp */
+ if (session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+ /* mac */
+ if (session->local.mac && session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ /* Server to Client */
+ /* crypt */
+ if (session->remote.crypt && session->remote.crypt->dtor) {
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+ /* comp */
+ if (session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+ /* mac */
+ if (session->remote.mac && session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ /* session_id */
+ if (session->session_id) {
+ LIBSSH2_FREE(session, session->session_id);
+ }
+ }
+
+ /* Free banner(s) */
+ if (session->remote.banner) {
+ LIBSSH2_FREE(session, session->remote.banner);
+ }
+ if (session->local.banner) {
+ LIBSSH2_FREE(session, session->local.banner);
+ }
+
+ /* Free preference(s) */
+ if (session->kex_prefs) {
+ LIBSSH2_FREE(session, session->kex_prefs);
+ }
+ if (session->hostkey_prefs) {
+ LIBSSH2_FREE(session, session->hostkey_prefs);
+ }
+
+ if (session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+ if (session->local.crypt_prefs) {
+ LIBSSH2_FREE(session, session->local.crypt_prefs);
+ }
+ if (session->local.mac_prefs) {
+ LIBSSH2_FREE(session, session->local.mac_prefs);
+ }
+ if (session->local.comp_prefs) {
+ LIBSSH2_FREE(session, session->local.comp_prefs);
+ }
+ if (session->local.lang_prefs) {
+ LIBSSH2_FREE(session, session->local.lang_prefs);
+ }
+
+ if (session->remote.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ }
+ if (session->remote.crypt_prefs) {
+ LIBSSH2_FREE(session, session->remote.crypt_prefs);
+ }
+ if (session->remote.mac_prefs) {
+ LIBSSH2_FREE(session, session->remote.mac_prefs);
+ }
+ if (session->remote.comp_prefs) {
+ LIBSSH2_FREE(session, session->remote.comp_prefs);
+ }
+ if (session->remote.lang_prefs) {
+ LIBSSH2_FREE(session, session->remote.lang_prefs);
+ }
+
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if (session->kexinit_data) {
+ LIBSSH2_FREE(session, session->kexinit_data);
+ }
+ if (session->startup_data) {
+ LIBSSH2_FREE(session, session->startup_data);
+ }
+ if (session->userauth_list_data) {
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ }
+ if (session->userauth_pswd_data) {
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ }
+ if (session->userauth_pswd_newpw) {
+ LIBSSH2_FREE(session, session->userauth_pswd_newpw);
+ }
+ if (session->userauth_host_packet) {
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ }
+ if (session->userauth_host_method) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ }
+ if (session->userauth_host_data) {
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ }
+ if (session->userauth_pblc_data) {
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ }
+ if (session->userauth_pblc_packet) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ }
+ if (session->userauth_pblc_method) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ }
+ if (session->userauth_kybd_data) {
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ }
+ if (session->userauth_kybd_packet) {
+ LIBSSH2_FREE(session, session->userauth_kybd_packet);
+ }
+ if (session->userauth_kybd_auth_instruction) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction);
+ }
+ if (session->open_packet) {
+ LIBSSH2_FREE(session, session->open_packet);
+ }
+ if (session->open_data) {
+ LIBSSH2_FREE(session, session->open_data);
+ }
+ if (session->direct_message) {
+ LIBSSH2_FREE(session, session->direct_message);
+ }
+ if (session->fwdLstn_packet) {
+ LIBSSH2_FREE(session, session->fwdLstn_packet);
+ }
+ if (session->pkeyInit_data) {
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ }
+ if (session->scpRecv_command) {
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ }
+ if (session->scpSend_command) {
+ LIBSSH2_FREE(session, session->scpSend_command);
+ }
+ if (session->sftpInit_sftp) {
+ LIBSSH2_FREE(session, session->sftpInit_sftp);
+ }
+
+ /* Free payload buffer */
+ if (session->packet.total_num) {
+ LIBSSH2_FREE(session, session->packet.payload);
+ }
+
+ /* Cleanup all remaining packets */
+ while ((pkg = _libssh2_list_first(&session->packets))) {
+ packets_left++;
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "packet left with id %d", pkg->data[0]);
+ /* unlink the node */
+ _libssh2_list_remove(&pkg->node);
+
+ /* free */
+ LIBSSH2_FREE(session, pkg->data);
+ LIBSSH2_FREE(session, pkg);
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Extra packets left %d", packets_left);
+
+ if(session->socket_prev_blockstate) {
+ /* if the socket was previously blocking, put it back so */
+ rc = session_nonblock(session->socket_fd, 0);
+ if (rc) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unable to reset socket's blocking state");
+ }
+ }
+
+ if (session->server_hostkey) {
+ LIBSSH2_FREE(session, session->server_hostkey);
+ }
+
+ /* error string */
+ if (session->err_msg && ((session->err_flags & LIBSSH2_ERR_FLAG_DUP) != 0)) {
+ LIBSSH2_FREE(session, (char *)session->err_msg);
+ }
+
+ LIBSSH2_FREE(session, session);
+
+ return 0;
+}
+
+/*
+ * libssh2_session_free
+ *
+ * Frees the memory allocated to the session
+ * Also closes and frees any channels attached to this session
+ */
+LIBSSH2_API int
+libssh2_session_free(LIBSSH2_SESSION * session)
+{
+ int rc;
+
+ BLOCK_ADJUST(rc, session, session_free(session) );
+
+ return rc;
+}
+
+/*
+ * libssh2_session_disconnect_ex
+ */
+static int
+session_disconnect(LIBSSH2_SESSION *session, int reason,
+ const char *description,
+ const char *lang)
+{
+ unsigned char *s;
+ unsigned long descr_len = 0, lang_len = 0;
+ int rc;
+
+ if (session->disconnect_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Disconnecting: reason=%d, desc=%s, lang=%s", reason,
+ description, lang);
+ if (description)
+ descr_len = strlen(description);
+
+ if (lang)
+ lang_len = strlen(lang);
+
+ if(descr_len > 256)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "too long description");
+
+ /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */
+ session->disconnect_data_len = descr_len + lang_len + 13;
+
+ s = session->disconnect_data;
+
+ *(s++) = SSH_MSG_DISCONNECT;
+ _libssh2_store_u32(&s, reason);
+ _libssh2_store_str(&s, description, descr_len);
+ /* store length only, lang is sent separately */
+ _libssh2_store_u32(&s, lang_len);
+
+ session->disconnect_state = libssh2_NB_state_created;
+ }
+
+ rc = _libssh2_transport_send(session, session->disconnect_data,
+ session->disconnect_data_len,
+ (unsigned char *)lang, lang_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ session->disconnect_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_session_disconnect_ex
+ */
+LIBSSH2_API int
+libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason,
+ const char *desc, const char *lang)
+{
+ int rc;
+
+ BLOCK_ADJUST(rc, session,
+ session_disconnect(session, reason, desc, lang));
+
+ return rc;
+}
+
+/* libssh2_session_methods
+ *
+ * Return the currently active methods for method_type
+ *
+ * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string
+ * regardless of actual negotiation Strings should NOT be freed
+ */
+LIBSSH2_API const char *
+libssh2_session_methods(LIBSSH2_SESSION * session, int method_type)
+{
+ /* All methods have char *name as their first element */
+ const LIBSSH2_KEX_METHOD *method = NULL;
+
+ switch (method_type) {
+ case LIBSSH2_METHOD_KEX:
+ method = session->kex;
+ break;
+
+ case LIBSSH2_METHOD_HOSTKEY:
+ method = (LIBSSH2_KEX_METHOD *) session->hostkey;
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_CS:
+ method = (LIBSSH2_KEX_METHOD *) session->local.crypt;
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_SC:
+ method = (LIBSSH2_KEX_METHOD *) session->remote.crypt;
+ break;
+
+ case LIBSSH2_METHOD_MAC_CS:
+ method = (LIBSSH2_KEX_METHOD *) session->local.mac;
+ break;
+
+ case LIBSSH2_METHOD_MAC_SC:
+ method = (LIBSSH2_KEX_METHOD *) session->remote.mac;
+ break;
+
+ case LIBSSH2_METHOD_COMP_CS:
+ method = (LIBSSH2_KEX_METHOD *) session->local.comp;
+ break;
+
+ case LIBSSH2_METHOD_COMP_SC:
+ method = (LIBSSH2_KEX_METHOD *) session->remote.comp;
+ break;
+
+ case LIBSSH2_METHOD_LANG_CS:
+ return "";
+
+ case LIBSSH2_METHOD_LANG_SC:
+ return "";
+
+ default:
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "Invalid parameter specified for method_type");
+ return NULL;
+ }
+
+ if (!method) {
+ _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No method negotiated");
+ return NULL;
+ }
+
+ return method->name;
+}
+
+/* libssh2_session_abstract
+ * Retrieve a pointer to the abstract property
+ */
+LIBSSH2_API void **
+libssh2_session_abstract(LIBSSH2_SESSION * session)
+{
+ return &session->abstract;
+}
+
+/* libssh2_session_last_error
+ *
+ * Returns error code and populates an error string into errmsg If want_buf is
+ * non-zero then the string placed into errmsg must be freed by the calling
+ * program. Otherwise it is assumed to be owned by libssh2
+ */
+LIBSSH2_API int
+libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg,
+ int *errmsg_len, int want_buf)
+{
+ size_t msglen = 0;
+
+ /* No error to report */
+ if (!session->err_code) {
+ if (errmsg) {
+ if (want_buf) {
+ *errmsg = LIBSSH2_ALLOC(session, 1);
+ if (*errmsg) {
+ **errmsg = 0;
+ }
+ } else {
+ *errmsg = (char *) "";
+ }
+ }
+ if (errmsg_len) {
+ *errmsg_len = 0;
+ }
+ return 0;
+ }
+
+ if (errmsg) {
+ const char *error = session->err_msg ? session->err_msg : "";
+
+ msglen = strlen(error);
+
+ if (want_buf) {
+ /* Make a copy so the calling program can own it */
+ *errmsg = LIBSSH2_ALLOC(session, msglen + 1);
+ if (*errmsg) {
+ memcpy(*errmsg, error, msglen);
+ (*errmsg)[msglen] = 0;
+ }
+ }
+ else
+ *errmsg = (char *)error;
+ }
+
+ if (errmsg_len) {
+ *errmsg_len = msglen;
+ }
+
+ return session->err_code;
+}
+
+/* libssh2_session_last_errno
+ *
+ * Returns error code
+ */
+LIBSSH2_API int
+libssh2_session_last_errno(LIBSSH2_SESSION * session)
+{
+ return session->err_code;
+}
+
+/* libssh2_session_set_last_error
+ *
+ * Sets the internal error code for the session.
+ *
+ * This function is available specifically to be used by high level
+ * language wrappers (i.e. Python or Perl) that may extend the library
+ * features while still relying on its error reporting mechanism.
+ */
+LIBSSH2_API int
+libssh2_session_set_last_error(LIBSSH2_SESSION* session,
+ int errcode,
+ const char* errmsg)
+{
+ return _libssh2_error_flags(session, errcode, errmsg,
+ LIBSSH2_ERR_FLAG_DUP);
+}
+
+/* Libssh2_session_flag
+ *
+ * Set/Get session flags
+ *
+ * Return error code.
+ */
+LIBSSH2_API int
+libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value)
+{
+ switch(flag) {
+ case LIBSSH2_FLAG_SIGPIPE:
+ session->flag.sigpipe = value;
+ break;
+ case LIBSSH2_FLAG_COMPRESS:
+ session->flag.compress = value;
+ break;
+ default:
+ /* unknown flag */
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/* _libssh2_session_set_blocking
+ *
+ * Set a session's blocking mode on or off, return the previous status when
+ * this function is called. Note this function does not alter the state of the
+ * actual socket involved.
+ */
+int
+_libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking)
+{
+ int bl = session->api_block_mode;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Setting blocking mode %s", blocking?"ON":"OFF");
+ session->api_block_mode = blocking;
+
+ return bl;
+}
+
+/* libssh2_session_set_blocking
+ *
+ * Set a channel's blocking mode on or off, similar to a socket's
+ * fcntl(fd, F_SETFL, O_NONBLOCK); type command
+ */
+LIBSSH2_API void
+libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking)
+{
+ (void) _libssh2_session_set_blocking(session, blocking);
+}
+
+/* libssh2_session_get_blocking
+ *
+ * Returns a session's blocking mode on or off
+ */
+LIBSSH2_API int
+libssh2_session_get_blocking(LIBSSH2_SESSION * session)
+{
+ return session->api_block_mode;
+}
+
+
+/* libssh2_session_set_timeout
+ *
+ * Set a session's timeout (in msec) for blocking mode,
+ * or 0 to disable timeouts.
+ */
+LIBSSH2_API void
+libssh2_session_set_timeout(LIBSSH2_SESSION * session, long timeout)
+{
+ session->api_timeout = timeout;
+}
+
+/* libssh2_session_get_timeout
+ *
+ * Returns a session's timeout, or 0 if disabled
+ */
+LIBSSH2_API long
+libssh2_session_get_timeout(LIBSSH2_SESSION * session)
+{
+ return session->api_timeout;
+}
+
+/*
+ * libssh2_poll_channel_read
+ *
+ * Returns 0 if no data is waiting on channel,
+ * non-0 if data is available
+ */
+LIBSSH2_API int
+libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended)
+{
+ LIBSSH2_SESSION *session;
+ LIBSSH2_PACKET *packet;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = channel->session;
+ packet = _libssh2_list_first(&session->packets);
+
+ while (packet) {
+ if ( channel->local.id == _libssh2_ntohu32(packet->data + 1)) {
+ if ( extended == 1 &&
+ (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA
+ || packet->data[0] == SSH_MSG_CHANNEL_DATA )) {
+ return 1;
+ } else if ( extended == 0 &&
+ packet->data[0] == SSH_MSG_CHANNEL_DATA) {
+ return 1;
+ }
+ /* else - no data of any type is ready to be read */
+ }
+ packet = _libssh2_list_next(&packet->node);
+ }
+
+ return 0;
+}
+
+/*
+ * poll_channel_write
+ *
+ * Returns 0 if writing to channel would block,
+ * non-0 if data can be written without blocking
+ */
+static inline int
+poll_channel_write(LIBSSH2_CHANNEL * channel)
+{
+ return channel->local.window_size ? 1 : 0;
+}
+
+/* poll_listener_queued
+ *
+ * Returns 0 if no connections are waiting to be accepted
+ * non-0 if one or more connections are available
+ */
+static inline int
+poll_listener_queued(LIBSSH2_LISTENER * listener)
+{
+ return _libssh2_list_first(&listener->queue) ? 1 : 0;
+}
+
+/*
+ * libssh2_poll
+ *
+ * Poll sockets, channels, and listeners for activity
+ */
+LIBSSH2_API int
+libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
+{
+ long timeout_remaining;
+ unsigned int i, active_fds;
+#ifdef HAVE_POLL
+ LIBSSH2_SESSION *session = NULL;
+#ifdef HAVE_ALLOCA
+ struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds);
+#else
+ struct pollfd sockets[256];
+
+ if (nfds > 256)
+ /* systems without alloca use a fixed-size array, this can be fixed if
+ we really want to, at least if the compiler is a C99 capable one */
+ return -1;
+#endif
+ /* Setup sockets for polling */
+ for(i = 0; i < nfds; i++) {
+ fds[i].revents = 0;
+ switch (fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ sockets[i].fd = fds[i].fd.socket;
+ sockets[i].events = fds[i].events;
+ sockets[i].revents = 0;
+ break;
+
+ case LIBSSH2_POLLFD_CHANNEL:
+ sockets[i].fd = fds[i].fd.channel->session->socket_fd;
+ sockets[i].events = POLLIN;
+ sockets[i].revents = 0;
+ if (!session)
+ session = fds[i].fd.channel->session;
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ sockets[i].fd = fds[i].fd.listener->session->socket_fd;
+ sockets[i].events = POLLIN;
+ sockets[i].revents = 0;
+ if (!session)
+ session = fds[i].fd.listener->session;
+ break;
+
+ default:
+ if (session)
+ _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE,
+ "Invalid descriptor passed to libssh2_poll()");
+ return -1;
+ }
+ }
+#elif defined(HAVE_SELECT)
+ LIBSSH2_SESSION *session = NULL;
+ libssh2_socket_t maxfd = 0;
+ fd_set rfds, wfds;
+ struct timeval tv;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ for(i = 0; i < nfds; i++) {
+ fds[i].revents = 0;
+ switch (fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ if (fds[i].events & LIBSSH2_POLLFD_POLLIN) {
+ FD_SET(fds[i].fd.socket, &rfds);
+ if (fds[i].fd.socket > maxfd)
+ maxfd = fds[i].fd.socket;
+ }
+ if (fds[i].events & LIBSSH2_POLLFD_POLLOUT) {
+ FD_SET(fds[i].fd.socket, &wfds);
+ if (fds[i].fd.socket > maxfd)
+ maxfd = fds[i].fd.socket;
+ }
+ break;
+
+ case LIBSSH2_POLLFD_CHANNEL:
+ FD_SET(fds[i].fd.channel->session->socket_fd, &rfds);
+ if (fds[i].fd.channel->session->socket_fd > maxfd)
+ maxfd = fds[i].fd.channel->session->socket_fd;
+ if (!session)
+ session = fds[i].fd.channel->session;
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ FD_SET(fds[i].fd.listener->session->socket_fd, &rfds);
+ if (fds[i].fd.listener->session->socket_fd > maxfd)
+ maxfd = fds[i].fd.listener->session->socket_fd;
+ if (!session)
+ session = fds[i].fd.listener->session;
+ break;
+
+ default:
+ if (session)
+ _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE,
+ "Invalid descriptor passed to libssh2_poll()");
+ return -1;
+ }
+ }
+#else
+ /* No select() or poll()
+ * no sockets structure to setup
+ */
+
+ timeout = 0;
+#endif /* HAVE_POLL or HAVE_SELECT */
+
+ timeout_remaining = timeout;
+ do {
+#if defined(HAVE_POLL) || defined(HAVE_SELECT)
+ int sysret;
+#endif
+
+ active_fds = 0;
+
+ for(i = 0; i < nfds; i++) {
+ if (fds[i].events != fds[i].revents) {
+ switch (fds[i].type) {
+ case LIBSSH2_POLLFD_CHANNEL:
+ if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) &&
+ /* Want to be ready for read */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) {
+ /* Not yet known to be ready for read */
+ fds[i].revents |=
+ libssh2_poll_channel_read(fds[i].fd.channel,
+ 0) ?
+ LIBSSH2_POLLFD_POLLIN : 0;
+ }
+ if ((fds[i].events & LIBSSH2_POLLFD_POLLEXT) &&
+ /* Want to be ready for extended read */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) {
+ /* Not yet known to be ready for extended read */
+ fds[i].revents |=
+ libssh2_poll_channel_read(fds[i].fd.channel,
+ 1) ?
+ LIBSSH2_POLLFD_POLLEXT : 0;
+ }
+ if ((fds[i].events & LIBSSH2_POLLFD_POLLOUT) &&
+ /* Want to be ready for write */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) {
+ /* Not yet known to be ready for write */
+ fds[i].revents |=
+ poll_channel_write(fds[i].fd. channel) ?
+ LIBSSH2_POLLFD_POLLOUT : 0;
+ }
+ if (fds[i].fd.channel->remote.close
+ || fds[i].fd.channel->local.close) {
+ fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED;
+ }
+ if (fds[i].fd.channel->session->socket_state ==
+ LIBSSH2_SOCKET_DISCONNECTED) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_CHANNEL_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) &&
+ /* Want a connection */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) {
+ /* No connections known of yet */
+ fds[i].revents |=
+ poll_listener_queued(fds[i].fd. listener) ?
+ LIBSSH2_POLLFD_POLLIN : 0;
+ }
+ if (fds[i].fd.listener->session->socket_state ==
+ LIBSSH2_SOCKET_DISCONNECTED) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_LISTENER_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ break;
+ }
+ }
+ if (fds[i].revents) {
+ active_fds++;
+ }
+ }
+
+ if (active_fds) {
+ /* Don't block on the sockets if we have channels/listeners which
+ are ready */
+ timeout_remaining = 0;
+ }
+#ifdef HAVE_POLL
+
+#ifdef HAVE_LIBSSH2_GETTIMEOFDAY
+ {
+ struct timeval tv_begin, tv_end;
+
+ _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL);
+ sysret = poll(sockets, nfds, timeout_remaining);
+ _libssh2_gettimeofday((struct timeval *) &tv_end, NULL);
+ timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000;
+ timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
+ }
+#else
+ /* If the platform doesn't support gettimeofday,
+ * then just make the call non-blocking and walk away
+ */
+ sysret = poll(sockets, nfds, timeout_remaining);
+ timeout_remaining = 0;
+#endif /* HAVE_GETTIMEOFDAY */
+
+ if (sysret > 0) {
+ for(i = 0; i < nfds; i++) {
+ switch (fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ fds[i].revents = sockets[i].revents;
+ sockets[i].revents = 0; /* In case we loop again, be nice */
+ if (fds[i].revents) {
+ active_fds++;
+ }
+ break;
+ case LIBSSH2_POLLFD_CHANNEL:
+ if (sockets[i].events & POLLIN) {
+ /* Spin session until no data available */
+ while (_libssh2_transport_read(fds[i].fd.channel->session)
+ > 0);
+ }
+ if (sockets[i].revents & POLLHUP) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_CHANNEL_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ sockets[i].revents = 0;
+ break;
+ case LIBSSH2_POLLFD_LISTENER:
+ if (sockets[i].events & POLLIN) {
+ /* Spin session until no data available */
+ while (_libssh2_transport_read(fds[i].fd.listener->session)
+ > 0);
+ }
+ if (sockets[i].revents & POLLHUP) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_LISTENER_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ sockets[i].revents = 0;
+ break;
+ }
+ }
+ }
+#elif defined(HAVE_SELECT)
+ tv.tv_sec = timeout_remaining / 1000;
+ tv.tv_usec = (timeout_remaining % 1000) * 1000;
+#ifdef HAVE_LIBSSH2_GETTIMEOFDAY
+ {
+ struct timeval tv_begin, tv_end;
+
+ _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL);
+ sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ _libssh2_gettimeofday((struct timeval *) &tv_end, NULL);
+
+ timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000;
+ timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
+ }
+#else
+ /* If the platform doesn't support gettimeofday,
+ * then just make the call non-blocking and walk away
+ */
+ sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ timeout_remaining = 0;
+#endif
+
+ if (sysret > 0) {
+ for(i = 0; i < nfds; i++) {
+ switch (fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ if (FD_ISSET(fds[i].fd.socket, &rfds)) {
+ fds[i].revents |= LIBSSH2_POLLFD_POLLIN;
+ }
+ if (FD_ISSET(fds[i].fd.socket, &wfds)) {
+ fds[i].revents |= LIBSSH2_POLLFD_POLLOUT;
+ }
+ if (fds[i].revents) {
+ active_fds++;
+ }
+ break;
+
+ case LIBSSH2_POLLFD_CHANNEL:
+ if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) {
+ /* Spin session until no data available */
+ while (_libssh2_transport_read(fds[i].fd.channel->session)
+ > 0);
+ }
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ if (FD_ISSET
+ (fds[i].fd.listener->session->socket_fd, &rfds)) {
+ /* Spin session until no data available */
+ while (_libssh2_transport_read(fds[i].fd.listener->session)
+ > 0);
+ }
+ break;
+ }
+ }
+ }
+#endif /* else no select() or poll() -- timeout (and by extension
+ * timeout_remaining) will be equal to 0 */
+ } while ((timeout_remaining > 0) && !active_fds);
+
+ return active_fds;
+}
+
+/*
+ * libssh2_session_block_directions
+ *
+ * Get blocked direction when a function returns LIBSSH2_ERROR_EAGAIN
+ * Returns LIBSSH2_SOCKET_BLOCK_INBOUND if recv() blocked
+ * or LIBSSH2_SOCKET_BLOCK_OUTBOUND if send() blocked
+ */
+LIBSSH2_API int
+libssh2_session_block_directions(LIBSSH2_SESSION *session)
+{
+ return session->socket_block_directions;
+}
+
+/* libssh2_session_banner_get
+ * Get the remote banner (server ID string)
+ */
+
+LIBSSH2_API const char *
+libssh2_session_banner_get(LIBSSH2_SESSION *session)
+{
+ /* to avoid a coredump when session is NULL */
+ if (NULL == session)
+ return NULL;
+
+ if (NULL==session->remote.banner)
+ return NULL;
+
+ return (const char *) session->remote.banner;
+}
diff --git a/libs/libssh2/src/session.h b/libs/libssh2/src/session.h
new file mode 100644
index 0000000000..aff4f2c5c8
--- /dev/null
+++ b/libs/libssh2/src/session.h
@@ -0,0 +1,93 @@
+#ifndef LIBSSH2_SESSION_H
+#define LIBSSH2_SESSION_H
+/* Copyright (c) 2004-2007 Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2010 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson <simon@josefsson.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/* Conveniance-macros to allow code like this;
+
+ int rc = BLOCK_ADJUST(rc, session, session_startup(session, sock) );
+
+ int rc = BLOCK_ADJUST_ERRNO(ptr, session, session_startup(session, sock) );
+
+ The point of course being to make sure that while in non-blocking mode
+ these always return no matter what the return code is, but in blocking mode
+ it blocks if EAGAIN is the reason for the return from the underlying
+ function.
+
+*/
+#define BLOCK_ADJUST(rc,sess,x) \
+ do { \
+ time_t entry_time = time (NULL); \
+ do { \
+ rc = x; \
+ /* the order of the check below is important to properly deal with \
+ the case when the 'sess' is freed */ \
+ if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \
+ break; \
+ rc = _libssh2_wait_socket(sess, entry_time); \
+ } while(!rc); \
+ } while(0)
+
+/*
+ * For functions that returns a pointer, we need to check if the API is
+ * non-blocking and return immediately. If the pointer is non-NULL we return
+ * immediately. If the API is blocking and we get a NULL we check the errno
+ * and *only* if that is EAGAIN we loop and wait for socket action.
+ */
+#define BLOCK_ADJUST_ERRNO(ptr,sess,x) \
+ do { \
+ time_t entry_time = time (NULL); \
+ int rc; \
+ do { \
+ ptr = x; \
+ if(!sess->api_block_mode || \
+ (ptr != NULL) || \
+ (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \
+ break; \
+ rc = _libssh2_wait_socket(sess, entry_time); \
+ } while(!rc); \
+ } while(0)
+
+
+int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t entry_time);
+
+/* this is the lib-internal set blocking function */
+int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking);
+
+#endif /* LIBSSH2_SESSION_H */
diff --git a/libs/libssh2/src/sftp.c b/libs/libssh2/src/sftp.c
new file mode 100644
index 0000000000..7c44116401
--- /dev/null
+++ b/libs/libssh2/src/sftp.c
@@ -0,0 +1,3466 @@
+/* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2007 Eli Fant <elifantu@mail.ru>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+
+#include "libssh2_priv.h"
+#include "libssh2_sftp.h"
+#include "channel.h"
+#include "session.h"
+#include "sftp.h"
+
+/* Note: Version 6 was documented at the time of writing
+ * However it was marked as "DO NOT IMPLEMENT" due to pending changes
+ *
+ * This release of libssh2 implements Version 5 with automatic downgrade
+ * based on server's declaration
+ */
+
+/* SFTP packet types */
+#define SSH_FXP_INIT 1
+#define SSH_FXP_VERSION 2
+#define SSH_FXP_OPEN 3
+#define SSH_FXP_CLOSE 4
+#define SSH_FXP_READ 5
+#define SSH_FXP_WRITE 6
+#define SSH_FXP_LSTAT 7
+#define SSH_FXP_FSTAT 8
+#define SSH_FXP_SETSTAT 9
+#define SSH_FXP_FSETSTAT 10
+#define SSH_FXP_OPENDIR 11
+#define SSH_FXP_READDIR 12
+#define SSH_FXP_REMOVE 13
+#define SSH_FXP_MKDIR 14
+#define SSH_FXP_RMDIR 15
+#define SSH_FXP_REALPATH 16
+#define SSH_FXP_STAT 17
+#define SSH_FXP_RENAME 18
+#define SSH_FXP_READLINK 19
+#define SSH_FXP_SYMLINK 20
+#define SSH_FXP_STATUS 101
+#define SSH_FXP_HANDLE 102
+#define SSH_FXP_DATA 103
+#define SSH_FXP_NAME 104
+#define SSH_FXP_ATTRS 105
+#define SSH_FXP_EXTENDED 200
+#define SSH_FXP_EXTENDED_REPLY 201
+
+/* S_IFREG */
+#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000
+/* S_IFDIR */
+#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000
+
+#define SSH_FXE_STATVFS_ST_RDONLY 0x00000001
+#define SSH_FXE_STATVFS_ST_NOSUID 0x00000002
+
+/* This is the maximum packet length to accept, as larger than this indicate
+ some kind of server problem. */
+#define LIBSSH2_SFTP_PACKET_MAXLEN 80000
+
+static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len);
+static void sftp_packet_flush(LIBSSH2_SFTP *sftp);
+
+/* sftp_attrsize
+ * Size that attr with this flagset will occupy when turned into a bin struct
+ */
+static int sftp_attrsize(unsigned long flags)
+{
+ return (4 + /* flags(4) */
+ ((flags & LIBSSH2_SFTP_ATTR_SIZE) ? 8 : 0) +
+ ((flags & LIBSSH2_SFTP_ATTR_UIDGID) ? 8 : 0) +
+ ((flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) ? 4 : 0) +
+ ((flags & LIBSSH2_SFTP_ATTR_ACMODTIME) ? 8 : 0));
+ /* atime + mtime as u32 */
+}
+
+/* _libssh2_store_u64
+ */
+static void _libssh2_store_u64(unsigned char **ptr, libssh2_uint64_t value)
+{
+ uint32_t msl = (uint32_t)(value >> 32);
+ unsigned char *buf = *ptr;
+
+ buf[0] = (unsigned char)((msl >> 24) & 0xFF);
+ buf[1] = (unsigned char)((msl >> 16) & 0xFF);
+ buf[2] = (unsigned char)((msl >> 8) & 0xFF);
+ buf[3] = (unsigned char)( msl & 0xFF);
+
+ buf[4] = (unsigned char)((value >> 24) & 0xFF);
+ buf[5] = (unsigned char)((value >> 16) & 0xFF);
+ buf[6] = (unsigned char)((value >> 8) & 0xFF);
+ buf[7] = (unsigned char)( value & 0xFF);
+
+ *ptr += 8;
+}
+
+/*
+ * Search list of zombied FXP_READ request IDs.
+ *
+ * Returns NULL if ID not in list.
+ */
+static struct sftp_zombie_requests *
+find_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
+{
+ struct sftp_zombie_requests *zombie =
+ _libssh2_list_first(&sftp->zombie_requests);
+
+ while(zombie) {
+ if(zombie->request_id == request_id)
+ break;
+ else
+ zombie = _libssh2_list_next(&zombie->node);
+ }
+
+ return zombie;
+}
+
+static void
+remove_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+
+ struct sftp_zombie_requests *zombie = find_zombie_request(sftp,
+ request_id);
+ if(zombie) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Removing request ID %ld from the list of zombie requests",
+ request_id);
+
+ _libssh2_list_remove(&zombie->node);
+ LIBSSH2_FREE(session, zombie);
+ }
+}
+
+static int
+add_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+
+ struct sftp_zombie_requests *zombie;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Marking request ID %ld as a zombie request", request_id);
+
+ zombie = LIBSSH2_ALLOC(sftp->channel->session,
+ sizeof(struct sftp_zombie_requests));
+ if (!zombie)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "malloc fail for zombie request ID");
+ else {
+ zombie->request_id = request_id;
+ _libssh2_list_add(&sftp->zombie_requests, &zombie->node);
+ return LIBSSH2_ERROR_NONE;
+ }
+}
+
+/*
+ * sftp_packet_add
+ *
+ * Add a packet to the SFTP packet brigade
+ */
+static int
+sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data,
+ size_t data_len)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ LIBSSH2_SFTP_PACKET *packet;
+ uint32_t request_id;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Received packet type %d (len %d)",
+ (int) data[0], data_len);
+
+ /*
+ * Experience shows that if we mess up EAGAIN handling somewhere or
+ * otherwise get out of sync with the channel, this is where we first get
+ * a wrong byte and if so we need to bail out at once to aid tracking the
+ * problem better.
+ */
+
+ switch(data[0]) {
+ case SSH_FXP_INIT:
+ case SSH_FXP_VERSION:
+ case SSH_FXP_OPEN:
+ case SSH_FXP_CLOSE:
+ case SSH_FXP_READ:
+ case SSH_FXP_WRITE:
+ case SSH_FXP_LSTAT:
+ case SSH_FXP_FSTAT:
+ case SSH_FXP_SETSTAT:
+ case SSH_FXP_FSETSTAT:
+ case SSH_FXP_OPENDIR:
+ case SSH_FXP_READDIR:
+ case SSH_FXP_REMOVE:
+ case SSH_FXP_MKDIR:
+ case SSH_FXP_RMDIR:
+ case SSH_FXP_REALPATH:
+ case SSH_FXP_STAT:
+ case SSH_FXP_RENAME:
+ case SSH_FXP_READLINK:
+ case SSH_FXP_SYMLINK:
+ case SSH_FXP_STATUS:
+ case SSH_FXP_HANDLE:
+ case SSH_FXP_DATA:
+ case SSH_FXP_NAME:
+ case SSH_FXP_ATTRS:
+ case SSH_FXP_EXTENDED:
+ case SSH_FXP_EXTENDED_REPLY:
+ break;
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Out of sync with the world");
+ }
+
+ request_id = _libssh2_ntohu32(&data[1]);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Received packet id %d",
+ request_id);
+
+ /* Don't add the packet if it answers a request we've given up on. */
+ if((data[0] == SSH_FXP_STATUS || data[0] == SSH_FXP_DATA)
+ && find_zombie_request(sftp, request_id)) {
+
+ /* If we get here, the file ended before the response arrived. We
+ are no longer interested in the request so we discard it */
+
+ LIBSSH2_FREE(session, data);
+
+ remove_zombie_request(sftp, request_id);
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_PACKET));
+ if (!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate datablock for SFTP packet");
+ }
+
+ packet->data = data;
+ packet->data_len = data_len;
+ packet->request_id = request_id;
+
+ _libssh2_list_add(&sftp->packets, &packet->node);
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * sftp_packet_read
+ *
+ * Frame an SFTP packet off the channel
+ */
+static int
+sftp_packet_read(LIBSSH2_SFTP *sftp)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *packet = NULL;
+ ssize_t rc;
+ unsigned long recv_window;
+ int packet_type;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
+
+ switch(sftp->packet_state) {
+ case libssh2_NB_state_sent: /* EAGAIN from window adjusting */
+ sftp->packet_state = libssh2_NB_state_idle;
+
+ packet = sftp->partial_packet;
+ goto window_adjust;
+
+ case libssh2_NB_state_sent1: /* EAGAIN from channel read */
+ sftp->packet_state = libssh2_NB_state_idle;
+
+ packet = sftp->partial_packet;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "partial read cont, len: %lu", sftp->partial_len);
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "partial read cont, already recvd: %lu",
+ sftp->partial_received);
+ /* fall-through */
+ default:
+ if(!packet) {
+ /* only do this if there's not already a packet buffer allocated
+ to use */
+
+ /* each packet starts with a 32 bit length field */
+ rc = _libssh2_channel_read(channel, 0,
+ (char *)&sftp->partial_size[
+ sftp->partial_size_len],
+ 4 - sftp->partial_size_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if (rc < 0)
+ return _libssh2_error(session, rc, "channel read");
+
+ sftp->partial_size_len += rc;
+
+ if(4 != sftp->partial_size_len)
+ /* we got a short read for the length part */
+ return LIBSSH2_ERROR_EAGAIN;
+
+ sftp->partial_len = _libssh2_ntohu32(sftp->partial_size);
+ /* make sure we don't proceed if the packet size is unreasonably
+ large */
+ if (sftp->partial_len > LIBSSH2_SFTP_PACKET_MAXLEN)
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
+ "SFTP packet too large");
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Data begin - Packet Length: %lu",
+ sftp->partial_len);
+ packet = LIBSSH2_ALLOC(session, sftp->partial_len);
+ if (!packet)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate SFTP packet");
+ sftp->partial_size_len = 0;
+ sftp->partial_received = 0; /* how much of the packet already
+ received */
+ sftp->partial_packet = packet;
+
+ window_adjust:
+ recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
+
+ if(sftp->partial_len > recv_window) {
+ /* ask for twice the data amount we need at once */
+ rc = _libssh2_channel_receive_window_adjust(channel,
+ sftp->partial_len*2,
+ 1, NULL);
+ /* store the state so that we continue with the correct
+ operation at next invoke */
+ sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN)?
+ libssh2_NB_state_sent:
+ libssh2_NB_state_idle;
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+
+ /* Read as much of the packet as we can */
+ while (sftp->partial_len > sftp->partial_received) {
+ rc = _libssh2_channel_read(channel, 0,
+ (char *)&packet[sftp->partial_received],
+ sftp->partial_len -
+ sftp->partial_received);
+
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ /*
+ * We received EAGAIN, save what we have and return EAGAIN to
+ * the caller. Set 'partial_packet' so that this function
+ * knows how to continue on the next invoke.
+ */
+ sftp->packet_state = libssh2_NB_state_sent1;
+ return rc;
+ }
+ else if (rc < 0) {
+ LIBSSH2_FREE(session, packet);
+ sftp->partial_packet = NULL;
+ return _libssh2_error(session, rc,
+ "Error waiting for SFTP packet");
+ }
+ sftp->partial_received += rc;
+ }
+
+ sftp->partial_packet = NULL;
+
+ /* sftp_packet_add takes ownership of the packet and might free it
+ so we take a copy of the packet type before we call it. */
+ packet_type = packet[0];
+ rc = sftp_packet_add(sftp, packet, sftp->partial_len);
+ if (rc) {
+ LIBSSH2_FREE(session, packet);
+ return rc;
+ }
+ else {
+ return packet_type;
+ }
+ }
+ /* WON'T REACH */
+}
+/*
+ * sftp_packetlist_flush
+ *
+ * Remove all pending packets in the packet_list and the corresponding one(s)
+ * in the SFTP packet brigade.
+ */
+static void sftp_packetlist_flush(LIBSSH2_SFTP_HANDLE *handle)
+{
+ struct sftp_pipeline_chunk *chunk;
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_SESSION *session = sftp->channel->session;
+
+ /* remove pending packets, if any */
+ chunk = _libssh2_list_first(&handle->packet_list);
+ while(chunk) {
+ unsigned char *data;
+ size_t data_len;
+ int rc;
+ struct sftp_pipeline_chunk *next = _libssh2_list_next(&chunk->node);
+
+ rc = sftp_packet_ask(sftp, SSH_FXP_STATUS,
+ chunk->request_id, &data, &data_len);
+ if(rc)
+ rc = sftp_packet_ask(sftp, SSH_FXP_DATA,
+ chunk->request_id, &data, &data_len);
+
+ if(!rc)
+ /* we found a packet, free it */
+ LIBSSH2_FREE(session, data);
+ else if(chunk->sent)
+ /* there was no incoming packet for this request, mark this
+ request as a zombie if it ever sent the request */
+ add_zombie_request(sftp, chunk->request_id);
+
+ _libssh2_list_remove(&chunk->node);
+ LIBSSH2_FREE(session, chunk);
+ chunk = next;
+ }
+}
+
+
+/*
+ * sftp_packet_ask()
+ *
+ * Checks if there's a matching SFTP packet available.
+ */
+static int
+sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
+
+ if(!packet)
+ return -1;
+
+ /* Special consideration when getting VERSION packet */
+
+ while (packet) {
+ if((packet->data[0] == packet_type) &&
+ ((packet_type == SSH_FXP_VERSION) ||
+ (packet->request_id == request_id))) {
+
+ /* Match! Fetch the data */
+ *data = packet->data;
+ *data_len = packet->data_len;
+
+ /* unlink and free this struct */
+ _libssh2_list_remove(&packet->node);
+ LIBSSH2_FREE(session, packet);
+
+ return 0;
+ }
+ /* check next struct in the list */
+ packet = _libssh2_list_next(&packet->node);
+ }
+ return -1;
+}
+
+/* sftp_packet_require
+ * A la libssh2_packet_require
+ */
+static int
+sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ int rc;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Requiring packet %d id %ld",
+ (int) packet_type, request_id);
+
+ if (sftp_packet_ask(sftp, packet_type, request_id, data, data_len) == 0) {
+ /* The right packet was available in the packet brigade */
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
+ (int) packet_type);
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ rc = sftp_packet_read(sftp);
+ if (rc < 0)
+ return rc;
+
+ /* data was read, check the queue again */
+ if (!sftp_packet_ask(sftp, packet_type, request_id, data, data_len)) {
+ /* The right packet was available in the packet brigade */
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
+ (int) packet_type);
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/* sftp_packet_requirev
+ * Require one of N possible responses
+ */
+static int
+sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses,
+ const unsigned char *valid_responses,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len)
+{
+ int i;
+ int rc;
+
+ /* If no timeout is active, start a new one */
+ if (sftp->requirev_start == 0)
+ sftp->requirev_start = time(NULL);
+
+ while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ for(i = 0; i < num_valid_responses; i++) {
+ if (sftp_packet_ask(sftp, valid_responses[i], request_id,
+ data, data_len) == 0) {
+ /*
+ * Set to zero before all returns to say
+ * the timeout is not active
+ */
+ sftp->requirev_start = 0;
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+
+ rc = sftp_packet_read(sftp);
+ if ((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) {
+ sftp->requirev_start = 0;
+ return rc;
+ } else if (rc <= 0) {
+ /* prevent busy-looping */
+ long left =
+ LIBSSH2_READ_TIMEOUT - (long)(time(NULL) - sftp->requirev_start);
+
+ if (left <= 0) {
+ sftp->requirev_start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ else if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ }
+ }
+
+ sftp->requirev_start = 0;
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/* sftp_attr2bin
+ * Populate attributes into an SFTP block
+ */
+static ssize_t
+sftp_attr2bin(unsigned char *p, const LIBSSH2_SFTP_ATTRIBUTES * attrs)
+{
+ unsigned char *s = p;
+ uint32_t flag_mask =
+ LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID |
+ LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME;
+
+ /* TODO: When we add SFTP4+ functionality flag_mask can get additional
+ bits */
+
+ if (!attrs) {
+ _libssh2_htonu32(s, 0);
+ return 4;
+ }
+
+ _libssh2_store_u32(&s, attrs->flags & flag_mask);
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
+ _libssh2_store_u64(&s, attrs->filesize);
+ }
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
+ _libssh2_store_u32(&s, attrs->uid);
+ _libssh2_store_u32(&s, attrs->gid);
+ }
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
+ _libssh2_store_u32(&s, attrs->permissions);
+ }
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
+ _libssh2_store_u32(&s, attrs->atime);
+ _libssh2_store_u32(&s, attrs->mtime);
+ }
+
+ return (s - p);
+}
+
+/* sftp_bin2attr
+ */
+static int
+sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES * attrs, const unsigned char *p)
+{
+ const unsigned char *s = p;
+
+ memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+ attrs->flags = _libssh2_ntohu32(s);
+ s += 4;
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
+ attrs->filesize = _libssh2_ntohu64(s);
+ s += 8;
+ }
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
+ attrs->uid = _libssh2_ntohu32(s);
+ s += 4;
+ attrs->gid = _libssh2_ntohu32(s);
+ s += 4;
+ }
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
+ attrs->permissions = _libssh2_ntohu32(s);
+ s += 4;
+ }
+
+ if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
+ attrs->atime = _libssh2_ntohu32(s);
+ s += 4;
+ attrs->mtime = _libssh2_ntohu32(s);
+ s += 4;
+ }
+
+ return (s - p);
+}
+
+/* ************
+ * SFTP API *
+ ************ */
+
+LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor);
+
+/* libssh2_sftp_dtor
+ * Shutdown an SFTP stream when the channel closes
+ */
+LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor)
+{
+ LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP *) (*channel_abstract);
+
+ (void) session_abstract;
+ (void) channel;
+
+ /* Free the partial packet storage for sftp_packet_read */
+ if (sftp->partial_packet) {
+ LIBSSH2_FREE(session, sftp->partial_packet);
+ }
+
+ /* Free the packet storage for _libssh2_sftp_packet_readdir */
+ if (sftp->readdir_packet) {
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ }
+
+ LIBSSH2_FREE(session, sftp);
+}
+
+/*
+ * sftp_init
+ *
+ * Startup an SFTP session
+ */
+static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
+{
+ unsigned char *data, *s;
+ size_t data_len;
+ ssize_t rc;
+ LIBSSH2_SFTP *sftp_handle;
+
+ if (session->sftpInit_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Initializing SFTP subsystem");
+
+ /*
+ * The 'sftpInit_sftp' and 'sftpInit_channel' struct fields within the
+ * session struct are only to be used during the setup phase. As soon
+ * as the SFTP session is created they are cleared and can thus be
+ * re-used again to allow any amount of SFTP handles per sessions.
+ *
+ * Note that you MUST NOT try to call libssh2_sftp_init() again to get
+ * another handle until the previous call has finished and either
+ * successfully made a handle or failed and returned error (not
+ * including *EAGAIN).
+ */
+
+ assert(session->sftpInit_sftp == NULL);
+ session->sftpInit_sftp = NULL;
+ session->sftpInit_state = libssh2_NB_state_created;
+ }
+
+ sftp_handle = session->sftpInit_sftp;
+
+ if (session->sftpInit_state == libssh2_NB_state_created) {
+ session->sftpInit_channel =
+ _libssh2_channel_open(session, "session", sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
+ if (!session->sftpInit_channel) {
+ if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting up channel");
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to startup channel");
+ session->sftpInit_state = libssh2_NB_state_idle;
+ }
+ return NULL;
+ }
+
+ session->sftpInit_state = libssh2_NB_state_sent;
+ }
+
+ if (session->sftpInit_state == libssh2_NB_state_sent) {
+ int ret = _libssh2_channel_process_startup(session->sftpInit_channel,
+ "subsystem",
+ sizeof("subsystem") - 1, "sftp",
+ strlen("sftp"));
+ if (ret == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block to request SFTP subsystem");
+ return NULL;
+ } else if (ret) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to request SFTP subsystem");
+ goto sftp_init_error;
+ }
+
+ session->sftpInit_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->sftpInit_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_channel_extended_data(session->sftpInit_channel,
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting handle extended data");
+ return NULL;
+ }
+
+ sftp_handle =
+ session->sftpInit_sftp =
+ LIBSSH2_CALLOC(session, sizeof(LIBSSH2_SFTP));
+ if (!sftp_handle) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a new SFTP structure");
+ goto sftp_init_error;
+ }
+ sftp_handle->channel = session->sftpInit_channel;
+ sftp_handle->request_id = 0;
+
+ _libssh2_htonu32(session->sftpInit_buffer, 5);
+ session->sftpInit_buffer[4] = SSH_FXP_INIT;
+ _libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION);
+ session->sftpInit_sent = 0; /* nothing's sent yet */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Sending FXP_INIT packet advertising version %d support",
+ (int) LIBSSH2_SFTP_VERSION);
+
+ session->sftpInit_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->sftpInit_state == libssh2_NB_state_sent2) {
+ /* sent off what's left of the init buffer to send */
+ rc = _libssh2_channel_write(session->sftpInit_channel, 0,
+ session->sftpInit_buffer +
+ session->sftpInit_sent,
+ 9 - session->sftpInit_sent);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending SSH_FXP_INIT");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send SSH_FXP_INIT");
+ goto sftp_init_error;
+ }
+ else {
+ /* add up the number of bytes sent */
+ session->sftpInit_sent += rc;
+
+ if(session->sftpInit_sent == 9)
+ /* move on */
+ session->sftpInit_state = libssh2_NB_state_sent3;
+
+ /* if less than 9, we remain in this state to send more later on */
+ }
+ }
+
+ rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION,
+ 0, &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return NULL;
+ else if (rc) {
+ _libssh2_error(session, rc,
+ "Timeout waiting for response from SFTP subsystem");
+ goto sftp_init_error;
+ }
+ if (data_len < 5) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Invalid SSH_FXP_VERSION response");
+ LIBSSH2_FREE(session, data);
+ goto sftp_init_error;
+ }
+
+ s = data + 1;
+ sftp_handle->version = _libssh2_ntohu32(s);
+ s += 4;
+ if (sftp_handle->version > LIBSSH2_SFTP_VERSION) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Truncating remote SFTP version from %lu",
+ sftp_handle->version);
+ sftp_handle->version = LIBSSH2_SFTP_VERSION;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Enabling SFTP version %lu compatibility",
+ sftp_handle->version);
+ while (s < (data + data_len)) {
+ size_t extname_len, extdata_len;
+
+ extname_len = _libssh2_ntohu32(s);
+ s += 4;
+ /* the extension name starts here */
+ s += extname_len;
+
+ extdata_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* TODO: Actually process extensions */
+ s += extdata_len;
+
+ }
+ LIBSSH2_FREE(session, data);
+
+ /* Make sure that when the channel gets closed, the SFTP service is shut
+ down too */
+ sftp_handle->channel->abstract = sftp_handle;
+ sftp_handle->channel->close_cb = libssh2_sftp_dtor;
+
+ session->sftpInit_state = libssh2_NB_state_idle;
+
+ /* clear the sftp and channel pointers in this session struct now */
+ session->sftpInit_sftp = NULL;
+ session->sftpInit_channel = NULL;
+
+ _libssh2_list_init(&sftp_handle->sftp_handles);
+
+ return sftp_handle;
+
+ sftp_init_error:
+ while (_libssh2_channel_free(session->sftpInit_channel) ==
+ LIBSSH2_ERROR_EAGAIN);
+ session->sftpInit_channel = NULL;
+ if (session->sftpInit_sftp) {
+ LIBSSH2_FREE(session, session->sftpInit_sftp);
+ session->sftpInit_sftp = NULL;
+ }
+ session->sftpInit_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_sftp_init
+ *
+ * Startup an SFTP session
+ */
+LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_SFTP *ptr;
+
+ if(!session)
+ return NULL;
+
+ if(!(session->state & LIBSSH2_STATE_AUTHENTICATED)) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "session not authenticated yet");
+ return NULL;
+ }
+
+ BLOCK_ADJUST_ERRNO(ptr, session, sftp_init(session));
+ return ptr;
+}
+
+/*
+ * sftp_shutdown
+ *
+ * Shuts down the SFTP subsystem
+ */
+static int
+sftp_shutdown(LIBSSH2_SFTP *sftp)
+{
+ int rc;
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if (sftp->partial_packet) {
+ LIBSSH2_FREE(session, sftp->partial_packet);
+ sftp->partial_packet = NULL;
+ }
+ if (sftp->open_packet) {
+ LIBSSH2_FREE(session, sftp->open_packet);
+ sftp->open_packet = NULL;
+ }
+ if (sftp->readdir_packet) {
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ sftp->readdir_packet = NULL;
+ }
+ if (sftp->fstat_packet) {
+ LIBSSH2_FREE(session, sftp->fstat_packet);
+ sftp->fstat_packet = NULL;
+ }
+ if (sftp->unlink_packet) {
+ LIBSSH2_FREE(session, sftp->unlink_packet);
+ sftp->unlink_packet = NULL;
+ }
+ if (sftp->rename_packet) {
+ LIBSSH2_FREE(session, sftp->rename_packet);
+ sftp->rename_packet = NULL;
+ }
+ if (sftp->fstatvfs_packet) {
+ LIBSSH2_FREE(session, sftp->fstatvfs_packet);
+ sftp->fstatvfs_packet = NULL;
+ }
+ if (sftp->statvfs_packet) {
+ LIBSSH2_FREE(session, sftp->statvfs_packet);
+ sftp->statvfs_packet = NULL;
+ }
+ if (sftp->mkdir_packet) {
+ LIBSSH2_FREE(session, sftp->mkdir_packet);
+ sftp->mkdir_packet = NULL;
+ }
+ if (sftp->rmdir_packet) {
+ LIBSSH2_FREE(session, sftp->rmdir_packet);
+ sftp->rmdir_packet = NULL;
+ }
+ if (sftp->stat_packet) {
+ LIBSSH2_FREE(session, sftp->stat_packet);
+ sftp->stat_packet = NULL;
+ }
+ if (sftp->symlink_packet) {
+ LIBSSH2_FREE(session, sftp->symlink_packet);
+ sftp->symlink_packet = NULL;
+ }
+ if (sftp->fsync_packet) {
+ LIBSSH2_FREE(session, sftp->fsync_packet);
+ sftp->fsync_packet = NULL;
+ }
+
+ sftp_packet_flush(sftp);
+
+ /* TODO: We should consider walking over the sftp_handles list and kill
+ * any remaining sftp handles ... */
+
+ rc = _libssh2_channel_free(sftp->channel);
+
+ return rc;
+}
+
+/* libssh2_sftp_shutdown
+ * Shutsdown the SFTP subsystem
+ */
+LIBSSH2_API int
+libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session, sftp_shutdown(sftp));
+ return rc;
+}
+
+/* *******************************
+ * SFTP File and Directory Ops *
+ ******************************* */
+
+/* sftp_open
+ */
+static LIBSSH2_SFTP_HANDLE *
+sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
+ size_t filename_len, uint32_t flags, long mode,
+ int open_type)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_SFTP_HANDLE *fp;
+ LIBSSH2_SFTP_ATTRIBUTES attrs = {
+ LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
+ };
+ unsigned char *s;
+ ssize_t rc;
+ int open_file = (open_type == LIBSSH2_SFTP_OPENFILE)?1:0;
+
+ if (sftp->open_state == libssh2_NB_state_idle) {
+ /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
+ flags(4) */
+ sftp->open_packet_len = filename_len + 13 +
+ (open_file? (4 + sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS)) : 0);
+
+ /* surprise! this starts out with nothing sent */
+ sftp->open_packet_sent = 0;
+ s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len);
+ if (!sftp->open_packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_OPEN or "
+ "FXP_OPENDIR packet");
+ return NULL;
+ }
+ /* Filetype in SFTP 3 and earlier */
+ attrs.permissions = mode |
+ (open_file ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE :
+ LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
+
+ _libssh2_store_u32(&s, sftp->open_packet_len - 4);
+ *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
+ sftp->open_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->open_request_id);
+ _libssh2_store_str(&s, filename, filename_len);
+
+ if (open_file) {
+ _libssh2_store_u32(&s, flags);
+ s += sftp_attr2bin(s, &attrs);
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
+ open_file? "file" : "directory");
+
+ sftp->open_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->open_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->open_packet+
+ sftp->open_packet_sent,
+ sftp->open_packet_len -
+ sftp->open_packet_sent);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending FXP_OPEN or FXP_OPENDIR command");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, rc, "Unable to send FXP_OPEN*");
+ LIBSSH2_FREE(session, sftp->open_packet);
+ sftp->open_packet = NULL;
+ sftp->open_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ /* bump the sent counter and remain in this state until the whole
+ data is off */
+ sftp->open_packet_sent += rc;
+
+ if(sftp->open_packet_len == sftp->open_packet_sent) {
+ LIBSSH2_FREE(session, sftp->open_packet);
+ sftp->open_packet = NULL;
+
+ sftp->open_state = libssh2_NB_state_sent;
+ }
+ }
+
+ if (sftp->open_state == libssh2_NB_state_sent) {
+ size_t data_len;
+ unsigned char *data;
+ static const unsigned char fopen_responses[2] =
+ { SSH_FXP_HANDLE, SSH_FXP_STATUS };
+ rc = sftp_packet_requirev(sftp, 2, fopen_responses,
+ sftp->open_request_id, &data,
+ &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for status message");
+ return NULL;
+ }
+ sftp->open_state = libssh2_NB_state_idle;
+ if (rc) {
+ _libssh2_error(session, rc, "Timeout waiting for status message");
+ return NULL;
+ }
+
+ /* OPEN can basically get STATUS or HANDLE back, where HANDLE implies
+ a fine response while STATUS means error. It seems though that at
+ times we get an SSH_FX_OK back in a STATUS, followed the "real"
+ HANDLE so we need to properly deal with that. */
+ if (data[0] == SSH_FXP_STATUS) {
+ int badness = 1;
+
+ if(data_len < 9) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Too small FXP_STATUS");
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+
+ sftp->last_errno = _libssh2_ntohu32(data + 5);
+
+ if(LIBSSH2_FX_OK == sftp->last_errno) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got HANDLE FXOK!");
+
+ LIBSSH2_FREE(session, data);
+
+ /* silly situation, but check for a HANDLE */
+ rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
+ sftp->open_request_id, &data,
+ &data_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ /* go back to sent state and wait for something else */
+ sftp->open_state = libssh2_NB_state_sent;
+ return NULL;
+ }
+ else if(!rc)
+ /* we got the handle so this is not a bad situation */
+ badness = 0;
+ }
+
+ if(badness) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Failed opening remote file");
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got FXP_STATUS %d",
+ sftp->last_errno);
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+ }
+
+ if(data_len < 10) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Too small FXP_HANDLE");
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+
+ fp = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE));
+ if (!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate new SFTP handle structure");
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+ fp->handle_type = open_file ? LIBSSH2_SFTP_HANDLE_FILE :
+ LIBSSH2_SFTP_HANDLE_DIR;
+
+ fp->handle_len = _libssh2_ntohu32(data + 5);
+ if (fp->handle_len > SFTP_HANDLE_MAXLEN)
+ /* SFTP doesn't allow handles longer than 256 characters */
+ fp->handle_len = SFTP_HANDLE_MAXLEN;
+
+ if(fp->handle_len > (data_len - 9))
+ /* do not reach beyond the end of the data we got */
+ fp->handle_len = data_len - 9;
+
+ memcpy(fp->handle, data + 9, fp->handle_len);
+
+ LIBSSH2_FREE(session, data);
+
+ /* add this file handle to the list kept in the sftp session */
+ _libssh2_list_add(&sftp->sftp_handles, &fp->node);
+
+ fp->sftp = sftp; /* point to the parent struct */
+
+ fp->u.file.offset = 0;
+ fp->u.file.offset_sent = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful");
+ return fp;
+ }
+ return NULL;
+}
+
+/* libssh2_sftp_open_ex
+ */
+LIBSSH2_API LIBSSH2_SFTP_HANDLE *
+libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename,
+ unsigned int filename_len, unsigned long flags, long mode,
+ int open_type)
+{
+ LIBSSH2_SFTP_HANDLE *hnd;
+
+ if(!sftp)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(hnd, sftp->channel->session,
+ sftp_open(sftp, filename, filename_len, flags, mode,
+ open_type));
+ return hnd;
+}
+
+/*
+ * sftp_read
+ *
+ * Read from an SFTP file handle
+ *
+ */
+static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
+ size_t buffer_size)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t count = 0;
+ struct sftp_pipeline_chunk *chunk;
+ struct sftp_pipeline_chunk *next;
+ ssize_t rc;
+ struct _libssh2_sftp_handle_file_data *filep =
+ &handle->u.file;
+ size_t bytes_in_buffer = 0;
+ char *sliding_bufferp = buffer;
+
+ /* This function can be interrupted in three different places where it
+ might need to wait for data from the network. It returns EAGAIN to
+ allow non-blocking clients to do other work but these client are
+ expected to call this function again (possibly many times) to finish
+ the operation.
+
+ The tricky part is that if we previously aborted a sftp_read due to
+ EAGAIN, we must continue at the same spot to continue the previously
+ interrupted operation. This is done using a state machine to record
+ what phase of execution we were at. The state is stored in
+ sftp->read_state.
+
+ libssh2_NB_state_idle: The first phase is where we prepare multiple
+ FXP_READ packets to do optimistic read-ahead. We send off as many as
+ possible in the second phase without waiting for a response to each
+ one; this is the key to fast reads. But we may have to adjust the
+ channel window size to do this which may interrupt this function while
+ waiting. The state machine saves the phase as libssh2_NB_state_idle so
+ it returns here on the next call.
+
+ libssh2_NB_state_sent: The second phase is where we send the FXP_READ
+ packets. Writing them to the channel can be interrupted with EAGAIN
+ but the state machine ensures we skip the first phase on the next call
+ and resume sending.
+
+ libssh2_NB_state_sent2: In the third phase (indicated by ) we read the
+ data from the responses that have arrived so far. Reading can be
+ interrupted with EAGAIN but the state machine ensures we skip the first
+ and second phases on the next call and resume sending.
+ */
+
+ switch (sftp->read_state) {
+ case libssh2_NB_state_idle:
+
+ /* Some data may already have been read from the server in the
+ previous call but didn't fit in the buffer at the time. If so, we
+ return that now as we can't risk being interrupted later with data
+ partially written to the buffer. */
+ if(filep->data_left) {
+ size_t copy = MIN(buffer_size, filep->data_left);
+
+ memcpy(buffer, &filep->data[ filep->data_len - filep->data_left],
+ copy);
+
+ filep->data_left -= copy;
+ filep->offset += copy;
+
+ if(!filep->data_left) {
+ LIBSSH2_FREE(session, filep->data);
+ filep->data = NULL;
+ }
+
+ return copy;
+ }
+
+ if (filep->eof) {
+ return 0;
+ } else {
+ /* We allow a number of bytes being requested at any given time
+ without having been acked - until we reach EOF. */
+
+ /* Number of bytes asked for that haven't been acked yet */
+ size_t already = (size_t)(filep->offset_sent - filep->offset);
+
+ size_t max_read_ahead = buffer_size*4;
+ unsigned long recv_window;
+
+ if(max_read_ahead > LIBSSH2_CHANNEL_WINDOW_DEFAULT*4)
+ max_read_ahead = LIBSSH2_CHANNEL_WINDOW_DEFAULT*4;
+
+ /* if the buffer_size passed in now is smaller than what has
+ already been sent, we risk getting count become a very large
+ number */
+ if(max_read_ahead > already)
+ count = max_read_ahead - already;
+
+ /* 'count' is how much more data to ask for, and 'already' is how
+ much data that already has been asked for but not yet returned.
+ Specificly, 'count' means how much data that have or will be
+ asked for by the nodes that are already added to the linked
+ list. Some of those read requests may not actually have been
+ sent off successfully yet.
+
+ If 'already' is very large it should be perfectly fine to have
+ count set to 0 as then we don't have to ask for more data
+ (right now).
+
+ buffer_size*4 is just picked more or less out of the air. The
+ idea is that when reading SFTP from a remote server, we send
+ away multiple read requests guessing that the client will read
+ more than only this 'buffer_size' amount of memory. So we ask
+ for maximum buffer_size*4 amount of data so that we can return
+ them very fast in subsequent calls.
+ */
+
+ recv_window = libssh2_channel_window_read_ex(sftp->channel,
+ NULL, NULL);
+ if(max_read_ahead > recv_window) {
+ /* more data will be asked for than what the window currently
+ allows, expand it! */
+
+ rc = _libssh2_channel_receive_window_adjust(sftp->channel,
+ max_read_ahead*8,
+ 1, NULL);
+ /* if this returns EAGAIN, we will get back to this function
+ at next call */
+ assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->data_left);
+ assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->eof);
+ if (rc)
+ return rc;
+ }
+ }
+
+ while(count > 0) {
+ unsigned char *s;
+
+ /* 25 = packet_len(4) + packet_type(1) + request_id(4) +
+ handle_len(4) + offset(8) + count(4) */
+ uint32_t packet_len = (uint32_t)handle->handle_len + 25;
+ uint32_t request_id;
+
+ uint32_t size = count;
+ if (size < buffer_size)
+ size = buffer_size;
+ if (size > MAX_SFTP_READ_SIZE)
+ size = MAX_SFTP_READ_SIZE;
+
+ chunk = LIBSSH2_ALLOC(session, packet_len +
+ sizeof(struct sftp_pipeline_chunk));
+ if (!chunk)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "malloc fail for FXP_WRITE");
+
+ chunk->offset = filep->offset_sent;
+ chunk->len = size;
+ chunk->lefttosend = packet_len;
+ chunk->sent = 0;
+
+ s = chunk->packet;
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *s++ = SSH_FXP_READ;
+ request_id = sftp->request_id++;
+ chunk->request_id = request_id;
+ _libssh2_store_u32(&s, request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+ _libssh2_store_u64(&s, filep->offset_sent);
+ filep->offset_sent += size; /* advance offset at once */
+ _libssh2_store_u32(&s, size);
+
+ /* add this new entry LAST in the list */
+ _libssh2_list_add(&handle->packet_list, &chunk->node);
+ count -= MIN(size,count); /* deduct the size we used, as we might
+ * have to create more packets */
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "read request id %d sent (offset: %d, size: %d)",
+ request_id, (int)chunk->offset, (int)chunk->len);
+ }
+
+ case libssh2_NB_state_sent:
+
+ sftp->read_state = libssh2_NB_state_idle;
+
+ /* move through the READ packets that haven't been sent and send as
+ many as possible - remember that we don't block */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ if(chunk->lefttosend) {
+
+ rc = _libssh2_channel_write(channel, 0,
+ &chunk->packet[chunk->sent],
+ chunk->lefttosend);
+ if(rc < 0) {
+ sftp->read_state = libssh2_NB_state_sent;
+ return rc;
+ }
+
+ /* remember where to continue sending the next time */
+ chunk->lefttosend -= rc;
+ chunk->sent += rc;
+
+ if(chunk->lefttosend) {
+ /* We still have data left to send for this chunk.
+ * If there is at least one completely sent chunk,
+ * we can get out of this loop and start reading. */
+ if (chunk != _libssh2_list_first(&handle->packet_list)) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ /* move on to the next chunk with data to send */
+ chunk = _libssh2_list_next(&chunk->node);
+ }
+
+ case libssh2_NB_state_sent2:
+
+ sftp->read_state = libssh2_NB_state_idle;
+
+ /*
+ * Count all ACKed packets and act on the contents of them.
+ */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ unsigned char *data;
+ size_t data_len;
+ uint32_t rc32;
+ static const unsigned char read_responses[2] = {
+ SSH_FXP_DATA, SSH_FXP_STATUS
+ };
+
+ if(chunk->lefttosend) {
+ /* if the chunk still has data left to send, we shouldn't wait
+ for an ACK for it just yet */
+ if (bytes_in_buffer > 0) {
+ return bytes_in_buffer;
+ } else {
+ /* we should never reach this point */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "sftp_read() internal error");
+ }
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, read_responses,
+ chunk->request_id, &data, &data_len);
+
+ if (rc==LIBSSH2_ERROR_EAGAIN && bytes_in_buffer != 0) {
+ /* do not return EAGAIN if we have already
+ * written data into the buffer */
+ return bytes_in_buffer;
+ }
+
+ if (rc < 0) {
+ sftp->read_state = libssh2_NB_state_sent2;
+ return rc;
+ }
+
+ /*
+ * We get DATA or STATUS back. STATUS can be error, or it is
+ * FX_EOF when we reach the end of the file.
+ */
+
+ switch (data[0]) {
+ case SSH_FXP_STATUS:
+ /* remove the chunk we just processed */
+
+ _libssh2_list_remove(&chunk->node);
+ LIBSSH2_FREE(session, chunk);
+
+ /* we must remove all outstanding READ requests, as either we
+ got an error or we're at end of file */
+ sftp_packetlist_flush(handle);
+
+ rc32 = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if (rc32 == LIBSSH2_FX_EOF) {
+ filep->eof = TRUE;
+ return bytes_in_buffer;
+ }
+ else {
+ sftp->last_errno = rc32;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP READ error");
+ }
+ break;
+
+ case SSH_FXP_DATA:
+ if (chunk->offset != filep->offset) {
+ /* This could happen if the server returns less bytes than
+ requested, which shouldn't happen for normal files. See:
+ https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
+ #section-6.4
+ */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Read Packet At Unexpected Offset");
+ }
+
+ rc32 = _libssh2_ntohu32(data + 5);
+ if (rc32 > (data_len - 9))
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol badness");
+
+ if(rc32 > chunk->len) {
+ /* A chunk larger than we requested was returned to us.
+ This is a protocol violation and we don't know how to
+ deal with it. Bail out! */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "FXP_READ response too big");
+ }
+
+ if(rc32 != chunk->len) {
+ /* a short read does not imply end of file, but we must
+ adjust the offset_sent since it was advanced with a
+ full chunk->len before */
+ filep->offset_sent -= (chunk->len - rc32);
+ }
+
+ if((bytes_in_buffer + rc32) > buffer_size) {
+ /* figure out the overlap amount */
+ filep->data_left = (bytes_in_buffer + rc32) - buffer_size;
+
+ /* getting the full packet would overflow the buffer, so
+ only get the correct amount and keep the remainder */
+ rc32 = (uint32_t)buffer_size - bytes_in_buffer;
+
+ /* store data to keep for next call */
+ filep->data = data;
+ filep->data_len = data_len;
+ }
+ else
+ filep->data_len = 0;
+
+ /* copy the received data from the received FXP_DATA packet to
+ the buffer at the correct index */
+ memcpy(sliding_bufferp, data + 9, rc32);
+ filep->offset += rc32;
+ bytes_in_buffer += rc32;
+ sliding_bufferp += rc32;
+
+ if(filep->data_len == 0)
+ /* free the allocated data if not stored to keep */
+ LIBSSH2_FREE(session, data);
+
+ /* remove the chunk we just processed keeping track of the
+ * next one in case we need it */
+ next = _libssh2_list_next(&chunk->node);
+ _libssh2_list_remove(&chunk->node);
+ LIBSSH2_FREE(session, chunk);
+
+ /* check if we have space left in the buffer
+ * and either continue to the next chunk or stop
+ */
+ if (bytes_in_buffer < buffer_size) {
+ chunk = next;
+ } else {
+ chunk = NULL;
+ }
+
+ break;
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol badness: unrecognised "
+ "read request response");
+ }
+ }
+
+ if (bytes_in_buffer > 0)
+ return bytes_in_buffer;
+
+ break;
+
+ default:
+ assert(!"State machine error; unrecognised read state");
+ }
+
+ /* we should never reach this point */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "sftp_read() internal error");
+}
+
+/* libssh2_sftp_read
+ * Read from an SFTP file handle
+ */
+LIBSSH2_API ssize_t
+libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *hnd, char *buffer,
+ size_t buffer_maxlen)
+{
+ ssize_t rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_read(hnd, buffer, buffer_maxlen));
+ return rc;
+}
+
+/* sftp_readdir
+ * Read from an SFTP directory handle
+ */
+static ssize_t sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
+ size_t buffer_maxlen, char *longentry,
+ size_t longentry_maxlen,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ uint32_t num_names;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
+ uint32_t packet_len = handle->handle_len + 13;
+ unsigned char *s, *data;
+ static const unsigned char read_responses[2] = {
+ SSH_FXP_NAME, SSH_FXP_STATUS };
+ ssize_t retcode;
+
+ if (sftp->readdir_state == libssh2_NB_state_idle) {
+ if (handle->u.dir.names_left) {
+ /*
+ * A prior request returned more than one directory entry,
+ * feed it back from the buffer
+ */
+ LIBSSH2_SFTP_ATTRIBUTES attrs_dummy;
+ size_t real_longentry_len;
+ size_t real_filename_len;
+ size_t filename_len;
+ size_t longentry_len;
+
+ s = (unsigned char *) handle->u.dir.next_name;
+ real_filename_len = _libssh2_ntohu32(s);
+
+ s += 4;
+
+ filename_len = real_filename_len;
+ if (filename_len >= buffer_maxlen) {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ memcpy(buffer, s, filename_len);
+ buffer[filename_len] = '\0'; /* zero terminate */
+ s += real_filename_len;
+
+ real_longentry_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ if (longentry && (longentry_maxlen>1)) {
+ longentry_len = real_longentry_len;
+
+ if (longentry_len >= longentry_maxlen) {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ memcpy(longentry, s, longentry_len);
+ longentry[longentry_len] = '\0'; /* zero terminate */
+ }
+ s += real_longentry_len;
+
+ if (attrs)
+ memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+
+ s += sftp_bin2attr(attrs ? attrs : &attrs_dummy, s);
+
+ handle->u.dir.next_name = (char *) s;
+ end:
+
+ if ((--handle->u.dir.names_left) == 0)
+ LIBSSH2_FREE(session, handle->u.dir.names_packet);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "libssh2_sftp_readdir_ex() return %d",
+ filename_len);
+ return (ssize_t)filename_len;
+ }
+
+ /* Request another entry(entries?) */
+
+ s = sftp->readdir_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->readdir_packet)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "FXP_READDIR packet");
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_READDIR;
+ sftp->readdir_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->readdir_request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ sftp->readdir_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->readdir_state == libssh2_NB_state_created) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Reading entries from directory handle");
+ retcode = _libssh2_channel_write(channel, 0, sftp->readdir_packet,
+ packet_len);
+ if (retcode == LIBSSH2_ERROR_EAGAIN) {
+ return retcode;
+ }
+ else if ((ssize_t)packet_len != retcode) {
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ sftp->readdir_packet = NULL;
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ sftp->readdir_packet = NULL;
+
+ sftp->readdir_state = libssh2_NB_state_sent;
+ }
+
+ retcode = sftp_packet_requirev(sftp, 2, read_responses,
+ sftp->readdir_request_id, &data,
+ &data_len);
+ if (retcode == LIBSSH2_ERROR_EAGAIN)
+ return retcode;
+ else if (retcode) {
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, retcode,
+ "Timeout waiting for status message");
+ }
+
+ if (data[0] == SSH_FXP_STATUS) {
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if (retcode == LIBSSH2_FX_EOF) {
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ sftp->readdir_state = libssh2_NB_state_idle;
+
+ num_names = _libssh2_ntohu32(data + 5);
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%lu entries returned",
+ num_names);
+ if (!num_names) {
+ LIBSSH2_FREE(session, data);
+ return 0;
+ }
+
+ handle->u.dir.names_left = num_names;
+ handle->u.dir.names_packet = data;
+ handle->u.dir.next_name = (char *) data + 9;
+
+ /* use the name popping mechanism from the start of the function */
+ return sftp_readdir(handle, buffer, buffer_maxlen, longentry,
+ longentry_maxlen, attrs);
+}
+
+/* libssh2_sftp_readdir_ex
+ * Read from an SFTP directory handle
+ */
+LIBSSH2_API int
+libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *hnd, char *buffer,
+ size_t buffer_maxlen, char *longentry,
+ size_t longentry_maxlen,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs)
+{
+ int rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_readdir(hnd, buffer, buffer_maxlen, longentry,
+ longentry_maxlen, attrs));
+ return rc;
+}
+
+/*
+ * sftp_write
+ *
+ * Write data to an SFTP handle. Returns the number of bytes written, or
+ * a negative error code.
+ *
+ * We recommend sending very large data buffers to this function!
+ *
+ * Concept:
+ *
+ * - Detect how much of the given buffer that was already sent in a previous
+ * call by inspecting the linked list of outgoing chunks. Make sure to skip
+ * passed the data that has already been taken care of.
+ *
+ * - Split all (new) outgoing data in chunks no larger than N.
+ *
+ * - Each N bytes chunk gets created as a separate SFTP packet.
+ *
+ * - Add all created outgoing packets to the linked list.
+ *
+ * - Walk through the list and send the chunks that haven't been sent,
+ * as many as possible until EAGAIN. Some of the chunks may have been put
+ * in the list in a previous invoke.
+ *
+ * - For all the chunks in the list that have been completely sent off, check
+ * for ACKs. If a chunk has been ACKed, it is removed from the linked
+ * list and the "acked" counter gets increased with that data amount.
+ *
+ * - Return TOTAL bytes acked so far.
+ *
+ * Caveats:
+ * - be careful: we must not return a higher number than what was given!
+ *
+ * TODO:
+ * Introduce an option that disables this sort of "speculative" ahead writing
+ * as there's a risk that it will do harm to some app.
+ */
+
+static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
+ size_t count)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ uint32_t retcode;
+ uint32_t packet_len;
+ unsigned char *s, *data;
+ ssize_t rc;
+ struct sftp_pipeline_chunk *chunk;
+ struct sftp_pipeline_chunk *next;
+ size_t acked = 0;
+ size_t org_count = count;
+ size_t already;
+
+ switch(sftp->write_state) {
+ default:
+ case libssh2_NB_state_idle:
+
+ /* Number of bytes sent off that haven't been acked and therefor we
+ will get passed in here again.
+
+ Also, add up the number of bytes that actually already have been
+ acked but we haven't been able to return as such yet, so we will
+ get that data as well passed in here again.
+ */
+ already = (size_t) (handle->u.file.offset_sent - handle->u.file.offset)+
+ handle->u.file.acked;
+
+ if(count >= already) {
+ /* skip the part already made into packets */
+ buffer += already;
+ count -= already;
+ }
+ else
+ /* there is more data already fine than what we got in this call */
+ count = 0;
+
+ sftp->write_state = libssh2_NB_state_idle;
+ while(count) {
+ /* TODO: Possibly this should have some logic to prevent a very
+ very small fraction to be left but lets ignore that for now */
+ uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
+ uint32_t request_id;
+
+ /* 25 = packet_len(4) + packet_type(1) + request_id(4) +
+ handle_len(4) + offset(8) + count(4) */
+ packet_len = handle->handle_len + size + 25;
+
+ chunk = LIBSSH2_ALLOC(session, packet_len +
+ sizeof(struct sftp_pipeline_chunk));
+ if (!chunk)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "malloc fail for FXP_WRITE");
+
+ chunk->len = size;
+ chunk->sent = 0;
+ chunk->lefttosend = packet_len;
+
+ s = chunk->packet;
+ _libssh2_store_u32(&s, packet_len - 4);
+
+ *(s++) = SSH_FXP_WRITE;
+ request_id = sftp->request_id++;
+ chunk->request_id = request_id;
+ _libssh2_store_u32(&s, request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+ _libssh2_store_u64(&s, handle->u.file.offset_sent);
+ handle->u.file.offset_sent += size; /* advance offset at once */
+ _libssh2_store_str(&s, buffer, size);
+
+ /* add this new entry LAST in the list */
+ _libssh2_list_add(&handle->packet_list, &chunk->node);
+
+ buffer += size;
+ count -= size; /* deduct the size we used, as we might have
+ to create more packets */
+ }
+
+ /* move through the WRITE packets that haven't been sent and send as many
+ as possible - remember that we don't block */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ if(chunk->lefttosend) {
+ rc = _libssh2_channel_write(channel, 0,
+ &chunk->packet[chunk->sent],
+ chunk->lefttosend);
+ if(rc < 0)
+ /* remain in idle state */
+ return rc;
+
+ /* remember where to continue sending the next time */
+ chunk->lefttosend -= rc;
+ chunk->sent += rc;
+
+ if(chunk->lefttosend)
+ /* data left to send, get out of loop */
+ break;
+ }
+
+ /* move on to the next chunk with data to send */
+ chunk = _libssh2_list_next(&chunk->node);
+ }
+
+ /* fall-through */
+ case libssh2_NB_state_sent:
+
+ sftp->write_state = libssh2_NB_state_idle;
+ /*
+ * Count all ACKed packets
+ */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ if(chunk->lefttosend)
+ /* if the chunk still has data left to send, we shouldn't wait
+ for an ACK for it just yet */
+ break;
+
+ else if(acked)
+ /* if we have sent data that is acked, we must return that
+ info before we call a function that might return EAGAIN */
+ break;
+
+ /* we check the packets in order */
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ chunk->request_id, &data, &data_len);
+ if (rc < 0) {
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ sftp->write_state = libssh2_NB_state_sent;
+ return rc;
+ }
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ sftp->last_errno = retcode;
+ if (retcode == LIBSSH2_FX_OK) {
+ acked += chunk->len; /* number of payload data that was acked
+ here */
+
+ /* we increase the offset value for all acks */
+ handle->u.file.offset += chunk->len;
+
+ next = _libssh2_list_next(&chunk->node);
+
+ _libssh2_list_remove(&chunk->node); /* remove from list */
+ LIBSSH2_FREE(session, chunk); /* free memory */
+
+ chunk = next;
+ }
+ else {
+ /* flush all pending packets from the outgoing list */
+ sftp_packetlist_flush(handle);
+
+ /* since we return error now, the application will not get any
+ outstanding data acked, so we need to rewind the offset to
+ where the application knows it has reached with acked data */
+ handle->u.file.offset -= handle->u.file.acked;
+
+ /* then reset the offset_sent to be the same as the offset */
+ handle->u.file.offset_sent = handle->u.file.offset;
+
+ /* clear the acked counter since we can have no pending data to
+ ack after an error */
+ handle->u.file.acked = 0;
+
+ /* the server returned an error for that written chunk, propagate
+ this back to our parent function */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "FXP write failed");
+ }
+ }
+ break;
+ }
+
+ /* if there were acked data in a previous call that wasn't returned then,
+ add that up and try to return it all now. This can happen if the app
+ first sends a huge buffer of data, and then in a second call it sends a
+ smaller one. */
+ acked += handle->u.file.acked;
+
+ if(acked) {
+ ssize_t ret = MIN(acked, org_count);
+ /* we got data acked so return that amount, but no more than what
+ was asked to get sent! */
+
+ /* store the remainder. 'ret' is always equal to or less than 'acked'
+ here */
+ handle->u.file.acked = acked - ret;
+
+ return ret;
+ }
+
+ else
+ return 0; /* nothing was acked, and no EAGAIN was received! */
+}
+
+/* libssh2_sftp_write
+ * Write data to a file handle
+ */
+LIBSSH2_API ssize_t
+libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *hnd, const char *buffer,
+ size_t count)
+{
+ ssize_t rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_write(hnd, buffer, count));
+ return rc;
+
+}
+
+static int sftp_fsync(LIBSSH2_SFTP_HANDLE *handle)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ /* 34 = packet_len(4) + packet_type(1) + request_id(4) +
+ string_len(4) + strlen("fsync@openssh.com")(17) + handle_len(4) */
+ uint32_t packet_len = handle->handle_len + 34;
+ size_t data_len;
+ unsigned char *packet, *s, *data;
+ ssize_t rc;
+ uint32_t retcode;
+
+ if (sftp->fsync_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Issuing fsync command");
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->fsync_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->fsync_request_id);
+ _libssh2_store_str(&s, "fsync@openssh.com", 17);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ sftp->fsync_state = libssh2_NB_state_created;
+ } else {
+ packet = sftp->fsync_packet;
+ }
+
+ if (sftp->fsync_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->fsync_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->fsync_packet = NULL;
+
+ if (rc < 0) {
+ sftp->fsync_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->fsync_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->fsync_request_id, &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ sftp->fsync_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ sftp->fsync_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if (retcode != LIBSSH2_FX_OK) {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "fsync failed");
+ }
+
+ return 0;
+}
+
+/* libssh2_sftp_fsync
+ * Commit data on the handle to disk.
+ */
+LIBSSH2_API int
+libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *hnd)
+{
+ int rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_fsync(hnd));
+ return rc;
+}
+
+
+/*
+ * sftp_fstat
+ *
+ * Get or Set stat on a file
+ */
+static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
+ uint32_t packet_len =
+ handle->handle_len + 13 + (setstat ? sftp_attrsize(attrs->flags) : 0);
+ unsigned char *s, *data;
+ static const unsigned char fstat_responses[2] =
+ { SSH_FXP_ATTRS, SSH_FXP_STATUS };
+ ssize_t rc;
+
+ if (sftp->fstat_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Issuing %s command",
+ setstat ? "set-stat" : "stat");
+ s = sftp->fstat_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->fstat_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "FSTAT/FSETSTAT packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT;
+ sftp->fstat_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->fstat_request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ if (setstat) {
+ s += sftp_attr2bin(s, attrs);
+ }
+
+ sftp->fstat_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->fstat_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->fstat_packet,
+ packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if ((ssize_t)packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->fstat_packet);
+ sftp->fstat_packet = NULL;
+ sftp->fstat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ (setstat ? "Unable to send FXP_FSETSTAT"
+ : "Unable to send FXP_FSTAT command"));
+ }
+ LIBSSH2_FREE(session, sftp->fstat_packet);
+ sftp->fstat_packet = NULL;
+
+ sftp->fstat_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, fstat_responses,
+ sftp->fstat_request_id, &data,
+ &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if (rc) {
+ sftp->fstat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Timeout waiting for status message");
+ }
+
+ sftp->fstat_state = libssh2_NB_state_idle;
+
+ if (data[0] == SSH_FXP_STATUS) {
+ uint32_t retcode;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if (retcode == LIBSSH2_FX_OK) {
+ return 0;
+ } else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ sftp_bin2attr(attrs, data + 5);
+ LIBSSH2_FREE(session, data);
+
+ return 0;
+}
+
+/* libssh2_sftp_fstat_ex
+ * Get or Set stat on a file
+ */
+LIBSSH2_API int
+libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *hnd,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
+{
+ int rc;
+ if(!hnd || !attrs)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_fstat(hnd, attrs, setstat));
+ return rc;
+}
+
+
+/* libssh2_sftp_seek64
+ * Set the read/write pointer to an arbitrary position within the file
+ */
+LIBSSH2_API void
+libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset)
+{
+ if(!handle)
+ return;
+ if(handle->u.file.offset == offset && handle->u.file.offset_sent == offset)
+ return;
+
+ handle->u.file.offset = handle->u.file.offset_sent = offset;
+ /* discard all pending requests and currently read data */
+ sftp_packetlist_flush(handle);
+
+ /* free the left received buffered data */
+ if (handle->u.file.data_left) {
+ LIBSSH2_FREE(handle->sftp->channel->session, handle->u.file.data);
+ handle->u.file.data_left = handle->u.file.data_len = 0;
+ handle->u.file.data = NULL;
+ }
+
+ /* reset EOF to False */
+ handle->u.file.eof = FALSE;
+}
+
+/* libssh2_sftp_seek
+ * Set the read/write pointer to an arbitrary position within the file
+ */
+LIBSSH2_API void
+libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset)
+{
+ libssh2_sftp_seek64(handle, (libssh2_uint64_t)offset);
+}
+
+/* libssh2_sftp_tell
+ * Return the current read/write pointer's offset
+ */
+LIBSSH2_API size_t
+libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle)
+{
+ if(!handle)
+ return 0; /* no handle, no size */
+
+ /* NOTE: this may very well truncate the size if it is larger than what
+ size_t can hold, so libssh2_sftp_tell64() is really the function you
+ should use */
+ return (size_t)(handle->u.file.offset);
+}
+
+/* libssh2_sftp_tell64
+ * Return the current read/write pointer's offset
+ */
+LIBSSH2_API libssh2_uint64_t
+libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle)
+{
+ if(!handle)
+ return 0; /* no handle, no size */
+
+ return handle->u.file.offset;
+}
+
+/*
+ * Flush all remaining incoming SFTP packets and zombies.
+ */
+static void sftp_packet_flush(LIBSSH2_SFTP *sftp)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
+ struct sftp_zombie_requests *zombie =
+ _libssh2_list_first(&sftp->zombie_requests);
+
+ while(packet) {
+ LIBSSH2_SFTP_PACKET *next;
+
+ /* check next struct in the list */
+ next = _libssh2_list_next(&packet->node);
+ _libssh2_list_remove(&packet->node);
+ LIBSSH2_FREE(session, packet->data);
+ LIBSSH2_FREE(session, packet);
+
+ packet = next;
+ }
+
+ while(zombie) {
+ /* figure out the next node */
+ struct sftp_zombie_requests *next = _libssh2_list_next(&zombie->node);
+ /* unlink the current one */
+ _libssh2_list_remove(&zombie->node);
+ /* free the memory */
+ LIBSSH2_FREE(session, zombie);
+ zombie = next;
+ }
+
+}
+
+/* sftp_close_handle
+ *
+ * Close a file or directory handle.
+ * Also frees handle resource and unlinks it from the SFTP structure.
+ * The handle is no longer usable after return of this function, unless
+ * the return value is LIBSSH2_ERROR_EAGAIN in which case this function
+ * should be called again.
+ */
+static int
+sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
+ uint32_t packet_len = handle->handle_len + 13;
+ unsigned char *s, *data = NULL;
+ int rc = 0;
+
+ if (handle->close_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Closing handle");
+ s = handle->close_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!handle->close_packet) {
+ handle->close_state = libssh2_NB_state_idle;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_CLOSE "
+ "packet");
+ } else {
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_CLOSE;
+ handle->close_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, handle->close_request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+ handle->close_state = libssh2_NB_state_created;
+ }
+ }
+
+ if (handle->close_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, handle->close_packet,
+ packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if ((ssize_t)packet_len != rc) {
+ handle->close_state = libssh2_NB_state_idle;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_CLOSE command");
+ } else
+ handle->close_state = libssh2_NB_state_sent;
+
+ LIBSSH2_FREE(session, handle->close_packet);
+ handle->close_packet = NULL;
+ }
+
+ if (handle->close_state == libssh2_NB_state_sent) {
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ handle->close_request_id, &data,
+ &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+
+ } else if (rc) {
+ _libssh2_error(session, rc,
+ "Error waiting for status message");
+ }
+
+ handle->close_state = libssh2_NB_state_sent1;
+ }
+
+ if(!data) {
+ /* if it reaches this point with data unset, something unwanted
+ happened for which we should have set an error code */
+ assert(rc);
+
+ } else {
+ int retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if (retcode != LIBSSH2_FX_OK) {
+ sftp->last_errno = retcode;
+ handle->close_state = libssh2_NB_state_idle;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ /* remove this handle from the parent's list */
+ _libssh2_list_remove(&handle->node);
+
+ if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR)
+ && handle->u.dir.names_left) {
+ LIBSSH2_FREE(session, handle->u.dir.names_packet);
+ }
+ else {
+ if(handle->u.file.data)
+ LIBSSH2_FREE(session, handle->u.file.data);
+ }
+
+ sftp_packetlist_flush(handle);
+ sftp->read_state = libssh2_NB_state_idle;
+
+ handle->close_state = libssh2_NB_state_idle;
+
+ LIBSSH2_FREE(session, handle);
+
+ return rc;
+}
+
+/* libssh2_sftp_close_handle
+ *
+ * Close a file or directory handle
+ * Also frees handle resource and unlinks it from the SFTP structure
+ */
+LIBSSH2_API int
+libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *hnd)
+{
+ int rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session, sftp_close_handle(hnd));
+ return rc;
+}
+
+/* sftp_unlink
+ * Delete a file from the remote server
+ */
+static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename,
+ size_t filename_len)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ int retcode;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
+ uint32_t packet_len = filename_len + 13;
+ unsigned char *s, *data;
+ int rc;
+
+ if (sftp->unlink_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Unlinking %s", filename);
+ s = sftp->unlink_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->unlink_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_REMOVE "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_REMOVE;
+ sftp->unlink_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->unlink_request_id);
+ _libssh2_store_str(&s, filename, filename_len);
+ sftp->unlink_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->unlink_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->unlink_packet,
+ packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if ((ssize_t)packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->unlink_packet);
+ sftp->unlink_packet = NULL;
+ sftp->unlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_REMOVE command");
+ }
+ LIBSSH2_FREE(session, sftp->unlink_packet);
+ sftp->unlink_packet = NULL;
+
+ sftp->unlink_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->unlink_request_id, &data,
+ &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if (rc) {
+ sftp->unlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->unlink_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if (retcode == LIBSSH2_FX_OK) {
+ return 0;
+ } else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+}
+
+/* libssh2_sftp_unlink_ex
+ * Delete a file from the remote server
+ */
+LIBSSH2_API int
+libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename,
+ unsigned int filename_len)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_unlink(sftp, filename, filename_len));
+ return rc;
+}
+
+/*
+ * sftp_rename
+ *
+ * Rename a file on the remote server
+ */
+static int sftp_rename(LIBSSH2_SFTP *sftp, const char *source_filename,
+ unsigned int source_filename_len,
+ const char *dest_filename,
+ unsigned int dest_filename_len, long flags)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ int retcode;
+ uint32_t packet_len =
+ source_filename_len + dest_filename_len + 17 + (sftp->version >=
+ 5 ? 4 : 0);
+ /* packet_len(4) + packet_type(1) + request_id(4) +
+ source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */
+ unsigned char *data;
+ ssize_t rc;
+
+ if (sftp->version < 2) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Server does not support RENAME");
+ }
+
+ if (sftp->rename_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Renaming %s to %s",
+ source_filename, dest_filename);
+ sftp->rename_s = sftp->rename_packet =
+ LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->rename_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_RENAME "
+ "packet");
+ }
+
+ _libssh2_store_u32(&sftp->rename_s, packet_len - 4);
+ *(sftp->rename_s++) = SSH_FXP_RENAME;
+ sftp->rename_request_id = sftp->request_id++;
+ _libssh2_store_u32(&sftp->rename_s, sftp->rename_request_id);
+ _libssh2_store_str(&sftp->rename_s, source_filename,
+ source_filename_len);
+ _libssh2_store_str(&sftp->rename_s, dest_filename, dest_filename_len);
+
+ if (sftp->version >= 5)
+ _libssh2_store_u32(&sftp->rename_s, flags);
+
+ sftp->rename_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->rename_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->rename_packet,
+ sftp->rename_s - sftp->rename_packet);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if ((ssize_t)packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->rename_packet);
+ sftp->rename_packet = NULL;
+ sftp->rename_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_RENAME command");
+ }
+ LIBSSH2_FREE(session, sftp->rename_packet);
+ sftp->rename_packet = NULL;
+
+ sftp->rename_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->rename_request_id, &data,
+ &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ sftp->rename_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->rename_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ sftp->last_errno = retcode;
+
+ /* now convert the SFTP error code to libssh2 return code or error
+ message */
+ switch (retcode) {
+ case LIBSSH2_FX_OK:
+ retcode = LIBSSH2_ERROR_NONE;
+ break;
+
+ case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+ retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "File already exists and "
+ "SSH_FXP_RENAME_OVERWRITE not specified");
+ break;
+
+ case LIBSSH2_FX_OP_UNSUPPORTED:
+ retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Operation Not Supported");
+ break;
+
+ default:
+ retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ break;
+ }
+
+ return retcode;
+}
+
+/* libssh2_sftp_rename_ex
+ * Rename a file on the remote server
+ */
+LIBSSH2_API int
+libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
+ unsigned int source_filename_len,
+ const char *dest_filename,
+ unsigned int dest_filename_len, long flags)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_rename(sftp, source_filename, source_filename_len,
+ dest_filename, dest_filename_len, flags));
+ return rc;
+}
+
+/*
+ * sftp_fstatvfs
+ *
+ * Get file system statistics
+ */
+static int sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4)
+ + handle_len (4) */
+ /* 20 = strlen ("fstatvfs@openssh.com") */
+ uint32_t packet_len = handle->handle_len + 20 + 17;
+ unsigned char *packet, *s, *data;
+ ssize_t rc;
+ unsigned int flag;
+ static const unsigned char responses[2] =
+ { SSH_FXP_EXTENDED_REPLY, SSH_FXP_STATUS };
+
+ if (sftp->fstatvfs_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Getting file system statistics");
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->fstatvfs_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->fstatvfs_request_id);
+ _libssh2_store_str(&s, "fstatvfs@openssh.com", 20);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ sftp->fstatvfs_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->fstatvfs_packet;
+ }
+
+ if (sftp->fstatvfs_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->fstatvfs_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->fstatvfs_packet = NULL;
+
+ if (rc < 0) {
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->fstatvfs_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, responses, sftp->fstatvfs_request_id,
+ &data, &data_len);
+
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ if (data[0] == SSH_FXP_STATUS) {
+ int retcode = _libssh2_ntohu32(data + 5);
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ LIBSSH2_FREE(session, data);
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+
+ if (data_len < 93) {
+ LIBSSH2_FREE(session, data);
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error: short response");
+ }
+
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+
+ st->f_bsize = _libssh2_ntohu64(data + 5);
+ st->f_frsize = _libssh2_ntohu64(data + 13);
+ st->f_blocks = _libssh2_ntohu64(data + 21);
+ st->f_bfree = _libssh2_ntohu64(data + 29);
+ st->f_bavail = _libssh2_ntohu64(data + 37);
+ st->f_files = _libssh2_ntohu64(data + 45);
+ st->f_ffree = _libssh2_ntohu64(data + 53);
+ st->f_favail = _libssh2_ntohu64(data + 61);
+ st->f_fsid = _libssh2_ntohu64(data + 69);
+ flag = (unsigned int)_libssh2_ntohu64(data + 77);
+ st->f_namemax = _libssh2_ntohu64(data + 85);
+
+ st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY)
+ ? LIBSSH2_SFTP_ST_RDONLY : 0;
+ st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID)
+ ? LIBSSH2_SFTP_ST_NOSUID : 0;
+
+ LIBSSH2_FREE(session, data);
+ return 0;
+}
+
+/* libssh2_sftp_fstatvfs
+ * Get filesystem space and inode utilization (requires fstatvfs@openssh.com
+ * support on the server)
+ */
+LIBSSH2_API int
+libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
+{
+ int rc;
+ if(!handle || !st)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, handle->sftp->channel->session, sftp_fstatvfs(handle, st));
+ return rc;
+}
+
+/*
+ * sftp_statvfs
+ *
+ * Get file system statistics
+ */
+static int sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, LIBSSH2_SFTP_STATVFS *st)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4)
+ + path_len (4) */
+ /* 19 = strlen ("statvfs@openssh.com") */
+ uint32_t packet_len = path_len + 19 + 17;
+ unsigned char *packet, *s, *data;
+ ssize_t rc;
+ unsigned int flag;
+ static const unsigned char responses[2] =
+ { SSH_FXP_EXTENDED_REPLY, SSH_FXP_STATUS };
+
+ if (sftp->statvfs_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Getting file system statistics of %s", path);
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->statvfs_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->statvfs_request_id);
+ _libssh2_store_str(&s, "statvfs@openssh.com", 19);
+ _libssh2_store_str(&s, path, path_len);
+
+ sftp->statvfs_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->statvfs_packet;
+ }
+
+ if (sftp->statvfs_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->statvfs_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->statvfs_packet = NULL;
+
+ if (rc < 0) {
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->statvfs_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, responses, sftp->statvfs_request_id,
+ &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ if (data[0] == SSH_FXP_STATUS) {
+ int retcode = _libssh2_ntohu32(data + 5);
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ LIBSSH2_FREE(session, data);
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+
+ if (data_len < 93) {
+ LIBSSH2_FREE(session, data);
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error: short response");
+ }
+
+ sftp->statvfs_state = libssh2_NB_state_idle;
+
+ st->f_bsize = _libssh2_ntohu64(data + 5);
+ st->f_frsize = _libssh2_ntohu64(data + 13);
+ st->f_blocks = _libssh2_ntohu64(data + 21);
+ st->f_bfree = _libssh2_ntohu64(data + 29);
+ st->f_bavail = _libssh2_ntohu64(data + 37);
+ st->f_files = _libssh2_ntohu64(data + 45);
+ st->f_ffree = _libssh2_ntohu64(data + 53);
+ st->f_favail = _libssh2_ntohu64(data + 61);
+ st->f_fsid = _libssh2_ntohu64(data + 69);
+ flag = (unsigned int)_libssh2_ntohu64(data + 77);
+ st->f_namemax = _libssh2_ntohu64(data + 85);
+
+ st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY)
+ ? LIBSSH2_SFTP_ST_RDONLY : 0;
+ st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID)
+ ? LIBSSH2_SFTP_ST_NOSUID : 0;
+
+ LIBSSH2_FREE(session, data);
+ return 0;
+}
+
+/* libssh2_sftp_statvfs_ex
+ * Get filesystem space and inode utilization (requires statvfs@openssh.com
+ * support on the server)
+ */
+LIBSSH2_API int
+libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
+ size_t path_len, LIBSSH2_SFTP_STATVFS *st)
+{
+ int rc;
+ if(!sftp || !st)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session, sftp_statvfs(sftp, path, path_len,
+ st));
+ return rc;
+}
+
+
+/*
+ * sftp_mkdir
+ *
+ * Create an SFTP directory
+ */
+static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, long mode)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_SFTP_ATTRIBUTES attrs = {
+ LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
+ };
+ size_t data_len;
+ int retcode;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len = path_len + 13 +
+ sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS);
+ unsigned char *packet, *s, *data;
+ int rc;
+
+ if (sftp->mkdir_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Creating directory %s with mode 0%lo", path, mode);
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_MKDIR "
+ "packet");
+ }
+ /* Filetype in SFTP 3 and earlier */
+ attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR;
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_MKDIR;
+ sftp->mkdir_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->mkdir_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ s += sftp_attr2bin(s, &attrs);
+
+ sftp->mkdir_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->mkdir_packet;
+ }
+
+ if (sftp->mkdir_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ sftp->mkdir_packet = packet;
+ return rc;
+ }
+ if (packet_len != rc) {
+ LIBSSH2_FREE(session, packet);
+ sftp->mkdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ LIBSSH2_FREE(session, packet);
+ sftp->mkdir_state = libssh2_NB_state_sent;
+ sftp->mkdir_packet = NULL;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS, sftp->mkdir_request_id,
+ &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ sftp->mkdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->mkdir_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if (retcode == LIBSSH2_FX_OK) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "OK!");
+ return 0;
+ } else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+}
+
+/*
+ * libssh2_sftp_mkdir_ex
+ *
+ * Create an SFTP directory
+ */
+LIBSSH2_API int
+libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, long mode)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_mkdir(sftp, path, path_len, mode));
+ return rc;
+}
+
+/* sftp_rmdir
+ * Remove a directory
+ */
+static int sftp_rmdir(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ int retcode;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len = path_len + 13;
+ unsigned char *s, *data;
+ int rc;
+
+ if (sftp->rmdir_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Removing directory: %s",
+ path);
+ s = sftp->rmdir_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->rmdir_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_RMDIR "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_RMDIR;
+ sftp->rmdir_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->rmdir_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ sftp->rmdir_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->rmdir_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->rmdir_packet,
+ packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->rmdir_packet);
+ sftp->rmdir_packet = NULL;
+ sftp->rmdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_RMDIR command");
+ }
+ LIBSSH2_FREE(session, sftp->rmdir_packet);
+ sftp->rmdir_packet = NULL;
+
+ sftp->rmdir_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->rmdir_request_id, &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (rc) {
+ sftp->rmdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->rmdir_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if (retcode == LIBSSH2_FX_OK) {
+ return 0;
+ } else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+}
+
+/* libssh2_sftp_rmdir_ex
+ * Remove a directory
+ */
+LIBSSH2_API int
+libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_rmdir(sftp, path, path_len));
+ return rc;
+}
+
+/* sftp_stat
+ * Stat a file or symbolic link
+ */
+static int sftp_stat(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, int stat_type,
+ LIBSSH2_SFTP_ATTRIBUTES * attrs)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len =
+ path_len + 13 +
+ ((stat_type ==
+ LIBSSH2_SFTP_SETSTAT) ? sftp_attrsize(attrs->flags) : 0);
+ unsigned char *s, *data;
+ static const unsigned char stat_responses[2] =
+ { SSH_FXP_ATTRS, SSH_FXP_STATUS };
+ int rc;
+
+ if (sftp->stat_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s",
+ (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" :
+ (stat_type ==
+ LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path);
+ s = sftp->stat_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->stat_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_*STAT "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+
+ switch (stat_type) {
+ case LIBSSH2_SFTP_SETSTAT:
+ *(s++) = SSH_FXP_SETSTAT;
+ break;
+
+ case LIBSSH2_SFTP_LSTAT:
+ *(s++) = SSH_FXP_LSTAT;
+ break;
+
+ case LIBSSH2_SFTP_STAT:
+ default:
+ *(s++) = SSH_FXP_STAT;
+ }
+ sftp->stat_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->stat_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ if (stat_type == LIBSSH2_SFTP_SETSTAT)
+ s += sftp_attr2bin(s, attrs);
+
+ sftp->stat_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->stat_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->stat_packet, packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ } else if (packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->stat_packet);
+ sftp->stat_packet = NULL;
+ sftp->stat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send STAT/LSTAT/SETSTAT command");
+ }
+ LIBSSH2_FREE(session, sftp->stat_packet);
+ sftp->stat_packet = NULL;
+
+ sftp->stat_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, stat_responses,
+ sftp->stat_request_id, &data, &data_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if (rc) {
+ sftp->stat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Timeout waiting for status message");
+ }
+
+ sftp->stat_state = libssh2_NB_state_idle;
+
+ if (data[0] == SSH_FXP_STATUS) {
+ int retcode;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if (retcode == LIBSSH2_FX_OK) {
+ return 0;
+ } else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+ sftp_bin2attr(attrs, data + 5);
+ LIBSSH2_FREE(session, data);
+
+ return 0;
+}
+
+/* libssh2_sftp_stat_ex
+ * Stat a file or symbolic link
+ */
+LIBSSH2_API int
+libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, int stat_type,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_stat(sftp, path, path_len, stat_type, attrs));
+ return rc;
+}
+
+/* sftp_symlink
+ * Read or set a symlink
+ */
+static int sftp_symlink(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, char *target,
+ unsigned int target_len, int link_type)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len, link_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len =
+ path_len + 13 +
+ ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0);
+ unsigned char *s, *data;
+ static const unsigned char link_responses[2] =
+ { SSH_FXP_NAME, SSH_FXP_STATUS };
+ int retcode;
+
+ if ((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Server does not support SYMLINK or READLINK");
+ }
+
+ if (sftp->symlink_state == libssh2_NB_state_idle) {
+ s = sftp->symlink_packet = LIBSSH2_ALLOC(session, packet_len);
+ if (!sftp->symlink_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "SYMLINK/READLINK/REALPATH packet");
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s on %s",
+ (link_type ==
+ LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading",
+ (link_type ==
+ LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path);
+
+ _libssh2_store_u32(&s, packet_len - 4);
+
+ switch (link_type) {
+ case LIBSSH2_SFTP_REALPATH:
+ *(s++) = SSH_FXP_REALPATH;
+ break;
+
+ case LIBSSH2_SFTP_SYMLINK:
+ *(s++) = SSH_FXP_SYMLINK;
+ break;
+
+ case LIBSSH2_SFTP_READLINK:
+ default:
+ *(s++) = SSH_FXP_READLINK;
+ }
+ sftp->symlink_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->symlink_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ if (link_type == LIBSSH2_SFTP_SYMLINK)
+ _libssh2_store_str(&s, target, target_len);
+
+ sftp->symlink_state = libssh2_NB_state_created;
+ }
+
+ if (sftp->symlink_state == libssh2_NB_state_created) {
+ ssize_t rc = _libssh2_channel_write(channel, 0, sftp->symlink_packet,
+ packet_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if (packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->symlink_packet);
+ sftp->symlink_packet = NULL;
+ sftp->symlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send SYMLINK/READLINK command");
+ }
+ LIBSSH2_FREE(session, sftp->symlink_packet);
+ sftp->symlink_packet = NULL;
+
+ sftp->symlink_state = libssh2_NB_state_sent;
+ }
+
+ retcode = sftp_packet_requirev(sftp, 2, link_responses,
+ sftp->symlink_request_id, &data,
+ &data_len);
+ if (retcode == LIBSSH2_ERROR_EAGAIN)
+ return retcode;
+ else if (retcode) {
+ sftp->symlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, retcode,
+ "Error waiting for status message");
+ }
+
+ sftp->symlink_state = libssh2_NB_state_idle;
+
+ if (data[0] == SSH_FXP_STATUS) {
+ int retcode;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if (retcode == LIBSSH2_FX_OK)
+ return LIBSSH2_ERROR_NONE;
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ if (_libssh2_ntohu32(data + 5) < 1) {
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Invalid READLINK/REALPATH response, "
+ "no name entries");
+ }
+
+ /* this reads a u32 and stores it into a signed 32bit value */
+ link_len = _libssh2_ntohu32(data + 9);
+ if (link_len < target_len) {
+ memcpy(target, data + 13, link_len);
+ target[link_len] = 0;
+ retcode = (int)link_len;
+ }
+ else
+ retcode = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ LIBSSH2_FREE(session, data);
+
+ return retcode;
+}
+
+/* libssh2_sftp_symlink_ex
+ * Read or set a symlink
+ */
+LIBSSH2_API int
+libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, char *target,
+ unsigned int target_len, int link_type)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_symlink(sftp, path, path_len, target, target_len,
+ link_type));
+ return rc;
+}
+
+/* libssh2_sftp_last_error
+ * Returns the last error code reported by SFTP
+ */
+LIBSSH2_API unsigned long
+libssh2_sftp_last_error(LIBSSH2_SFTP *sftp)
+{
+ if(!sftp)
+ return 0;
+
+ return sftp->last_errno;
+}
+
+/* libssh2_sftp_get_channel
+ * Return the channel of sftp, then caller can control the channel's behavior.
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp)
+{
+ if (!sftp)
+ return NULL;
+
+ return sftp->channel;
+}
diff --git a/libs/libssh2/src/sftp.h b/libs/libssh2/src/sftp.h
new file mode 100644
index 0000000000..2ed32cea6d
--- /dev/null
+++ b/libs/libssh2/src/sftp.h
@@ -0,0 +1,237 @@
+#ifndef _LIBSSH2_SFTP_H
+#define _LIBSSH2_SFTP_H
+/*
+ * Copyright (C) 2010 - 2012 by Daniel Stenberg
+ * Author: Daniel Stenberg <daniel@haxx.se>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * MAX_SFTP_OUTGOING_SIZE MUST not be larger than 32500 or so. This is the
+ * amount of data sent in each FXP_WRITE packet
+ */
+#define MAX_SFTP_OUTGOING_SIZE 30000
+
+/* MAX_SFTP_READ_SIZE is how much data is asked for at max in each FXP_READ
+ * packets.
+ */
+#define MAX_SFTP_READ_SIZE 30000
+
+struct sftp_pipeline_chunk {
+ struct list_node node;
+ libssh2_uint64_t offset; /* READ: offset at which to start reading
+ WRITE: not used */
+ size_t len; /* WRITE: size of the data to write
+ READ: how many bytes that was asked for */
+ size_t sent;
+ ssize_t lefttosend; /* if 0, the entire packet has been sent off */
+ uint32_t request_id;
+ unsigned char packet[1]; /* data */
+};
+
+struct sftp_zombie_requests {
+ struct list_node node;
+ uint32_t request_id;
+};
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+struct _LIBSSH2_SFTP_PACKET
+{
+ struct list_node node; /* linked list header */
+ uint32_t request_id;
+ unsigned char *data;
+ size_t data_len; /* payload size */
+};
+
+typedef struct _LIBSSH2_SFTP_PACKET LIBSSH2_SFTP_PACKET;
+
+#define SFTP_HANDLE_MAXLEN 256 /* according to spec! */
+
+struct _LIBSSH2_SFTP_HANDLE
+{
+ struct list_node node;
+
+ LIBSSH2_SFTP *sftp;
+
+ char handle[SFTP_HANDLE_MAXLEN];
+ size_t handle_len;
+
+ enum {
+ LIBSSH2_SFTP_HANDLE_FILE,
+ LIBSSH2_SFTP_HANDLE_DIR
+ } handle_type;
+
+ union _libssh2_sftp_handle_data
+ {
+ struct _libssh2_sftp_handle_file_data
+ {
+ libssh2_uint64_t offset;
+ libssh2_uint64_t offset_sent;
+ size_t acked; /* container for acked data that hasn't been
+ returned to caller yet, used for sftp_write */
+
+ /* 'data' is used by sftp_read() and is allocated data that has
+ been received already from the server but wasn't returned to
+ the caller yet. It is of size 'data_len' and 'data_left is the
+ number of bytes not yet returned, counted from the end of the
+ buffer. */
+ unsigned char *data;
+ size_t data_len;
+ size_t data_left;
+
+ char eof; /* we have read to the end */
+ } file;
+ struct _libssh2_sftp_handle_dir_data
+ {
+ uint32_t names_left;
+ void *names_packet;
+ char *next_name;
+ } dir;
+ } u;
+
+ /* State variables used in libssh2_sftp_close_handle() */
+ libssh2_nonblocking_states close_state;
+ uint32_t close_request_id;
+ unsigned char *close_packet;
+
+ /* list of outstanding packets sent to server */
+ struct list_head packet_list;
+
+};
+
+struct _LIBSSH2_SFTP
+{
+ LIBSSH2_CHANNEL *channel;
+
+ uint32_t request_id, version;
+
+ struct list_head packets;
+
+ /* List of FXP_READ responses to ignore because EOF already received. */
+ struct list_head zombie_requests;
+
+ /* a list of _LIBSSH2_SFTP_HANDLE structs */
+ struct list_head sftp_handles;
+
+ uint32_t last_errno;
+
+ /* Holder for partial packet, use in libssh2_sftp_packet_read() */
+ unsigned char partial_size[4]; /* buffer for size field */
+ size_t partial_size_len; /* size field length */
+ unsigned char *partial_packet; /* The data */
+ uint32_t partial_len; /* Desired number of bytes */
+ size_t partial_received; /* Bytes received so far */
+
+ /* Time that libssh2_sftp_packet_requirev() started reading */
+ time_t requirev_start;
+
+ /* State variables used in libssh2_sftp_open_ex() */
+ libssh2_nonblocking_states open_state;
+ unsigned char *open_packet;
+ uint32_t open_packet_len; /* 32 bit on the wire */
+ size_t open_packet_sent;
+ uint32_t open_request_id;
+
+ /* State variable used in sftp_read() */
+ libssh2_nonblocking_states read_state;
+
+ /* State variable used in sftp_packet_read() */
+ libssh2_nonblocking_states packet_state;
+
+ /* State variable used in sftp_write() */
+ libssh2_nonblocking_states write_state;
+
+ /* State variables used in sftp_fsync() */
+ libssh2_nonblocking_states fsync_state;
+ unsigned char *fsync_packet;
+ uint32_t fsync_request_id;
+
+ /* State variables used in libssh2_sftp_readdir() */
+ libssh2_nonblocking_states readdir_state;
+ unsigned char *readdir_packet;
+ uint32_t readdir_request_id;
+
+ /* State variables used in libssh2_sftp_fstat_ex() */
+ libssh2_nonblocking_states fstat_state;
+ unsigned char *fstat_packet;
+ uint32_t fstat_request_id;
+
+ /* State variables used in libssh2_sftp_unlink_ex() */
+ libssh2_nonblocking_states unlink_state;
+ unsigned char *unlink_packet;
+ uint32_t unlink_request_id;
+
+ /* State variables used in libssh2_sftp_rename_ex() */
+ libssh2_nonblocking_states rename_state;
+ unsigned char *rename_packet;
+ unsigned char *rename_s;
+ uint32_t rename_request_id;
+
+ /* State variables used in libssh2_sftp_fstatvfs() */
+ libssh2_nonblocking_states fstatvfs_state;
+ unsigned char *fstatvfs_packet;
+ uint32_t fstatvfs_request_id;
+
+ /* State variables used in libssh2_sftp_statvfs() */
+ libssh2_nonblocking_states statvfs_state;
+ unsigned char *statvfs_packet;
+ uint32_t statvfs_request_id;
+
+ /* State variables used in libssh2_sftp_mkdir() */
+ libssh2_nonblocking_states mkdir_state;
+ unsigned char *mkdir_packet;
+ uint32_t mkdir_request_id;
+
+ /* State variables used in libssh2_sftp_rmdir() */
+ libssh2_nonblocking_states rmdir_state;
+ unsigned char *rmdir_packet;
+ uint32_t rmdir_request_id;
+
+ /* State variables used in libssh2_sftp_stat() */
+ libssh2_nonblocking_states stat_state;
+ unsigned char *stat_packet;
+ uint32_t stat_request_id;
+
+ /* State variables used in libssh2_sftp_symlink() */
+ libssh2_nonblocking_states symlink_state;
+ unsigned char *symlink_packet;
+ uint32_t symlink_request_id;
+};
+
+#endif
diff --git a/libs/libssh2/src/stdafx.cxx b/libs/libssh2/src/stdafx.cxx
new file mode 100644
index 0000000000..1647228cd0
--- /dev/null
+++ b/libs/libssh2/src/stdafx.cxx
@@ -0,0 +1,2 @@
+
+#include "stdafx.h" \ No newline at end of file
diff --git a/libs/libssh2/src/stdafx.h b/libs/libssh2/src/stdafx.h
new file mode 100644
index 0000000000..6f70f09bee
--- /dev/null
+++ b/libs/libssh2/src/stdafx.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/libs/libssh2/src/transport.c b/libs/libssh2/src/transport.c
new file mode 100644
index 0000000000..8725da0950
--- /dev/null
+++ b/libs/libssh2/src/transport.c
@@ -0,0 +1,891 @@
+/* Copyright (C) 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (C) 2009-2010 by Daniel Stenberg
+ * Author: Daniel Stenberg <daniel@haxx.se>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file handles reading and writing to the SECSH transport layer. RFC4253.
+ */
+
+#include "libssh2_priv.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#ifdef LIBSSH2DEBUG
+#include <stdio.h>
+#endif
+
+#include <assert.h>
+
+#include "transport.h"
+#include "mac.h"
+
+#define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */
+#define MAX_MACSIZE 64 /* MUST fit biggest MAC length we support */
+
+#ifdef LIBSSH2DEBUG
+#define UNPRINTABLE_CHAR '.'
+static void
+debugdump(LIBSSH2_SESSION * session,
+ const char *desc, const unsigned char *ptr, size_t size)
+{
+ size_t i;
+ size_t c;
+ unsigned int width = 0x10;
+ char buffer[256]; /* Must be enough for width*4 + about 30 or so */
+ size_t used;
+ static const char* hex_chars = "0123456789ABCDEF";
+
+ if (!(session->showmask & LIBSSH2_TRACE_TRANS)) {
+ /* not asked for, bail out */
+ return;
+ }
+
+ used = snprintf(buffer, sizeof(buffer), "=> %s (%d bytes)\n",
+ desc, (int) size);
+ if (session->tracehandler)
+ (session->tracehandler)(session, session->tracehandler_context,
+ buffer, used);
+ else
+ fprintf(stderr, "%s", buffer);
+
+ for(i = 0; i < size; i += width) {
+
+ used = snprintf(buffer, sizeof(buffer), "%04lx: ", (long)i);
+
+ /* hex not disabled, show it */
+ for(c = 0; c < width; c++) {
+ if (i + c < size) {
+ buffer[used++] = hex_chars[(ptr[i+c] >> 4) & 0xF];
+ buffer[used++] = hex_chars[ptr[i+c] & 0xF];
+ }
+ else {
+ buffer[used++] = ' ';
+ buffer[used++] = ' ';
+ }
+
+ buffer[used++] = ' ';
+ if ((width/2) - 1 == c)
+ buffer[used++] = ' ';
+ }
+
+ buffer[used++] = ':';
+ buffer[used++] = ' ';
+
+ for(c = 0; (c < width) && (i + c < size); c++) {
+ buffer[used++] = isprint(ptr[i + c]) ?
+ ptr[i + c] : UNPRINTABLE_CHAR;
+ }
+ buffer[used++] = '\n';
+ buffer[used] = 0;
+
+ if (session->tracehandler)
+ (session->tracehandler)(session, session->tracehandler_context,
+ buffer, used);
+ else
+ fprintf(stderr, "%s", buffer);
+ }
+}
+#else
+#define debugdump(a,x,y,z)
+#endif
+
+
+/* decrypt() decrypts 'len' bytes from 'source' to 'dest'.
+ *
+ * returns 0 on success and negative on failure
+ */
+
+static int
+decrypt(LIBSSH2_SESSION * session, unsigned char *source,
+ unsigned char *dest, int len)
+{
+ struct transportpacket *p = &session->packet;
+ int blocksize = session->remote.crypt->blocksize;
+
+ /* if we get called with a len that isn't an even number of blocksizes
+ we risk losing those extra bytes */
+ assert((len % blocksize) == 0);
+
+ while (len >= blocksize) {
+ if (session->remote.crypt->crypt(session, source, blocksize,
+ &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, p->payload);
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+
+ /* if the crypt() function would write to a given address it
+ wouldn't have to memcpy() and we could avoid this memcpy()
+ too */
+ memcpy(dest, source, blocksize);
+
+ len -= blocksize; /* less bytes left */
+ dest += blocksize; /* advance write pointer */
+ source += blocksize; /* advance read pointer */
+ }
+ return LIBSSH2_ERROR_NONE; /* all is fine */
+}
+
+/*
+ * fullpacket() gets called when a full packet has been received and properly
+ * collected.
+ */
+static int
+fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
+{
+ unsigned char macbuf[MAX_MACSIZE];
+ struct transportpacket *p = &session->packet;
+ int rc;
+ int compressed;
+
+ if (session->fullpacket_state == libssh2_NB_state_idle) {
+ session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
+ session->fullpacket_payload_len = p->packet_length - 1;
+
+ if (encrypted) {
+
+ /* Calculate MAC hash */
+ session->remote.mac->hash(session, macbuf, /* store hash here */
+ session->remote.seqno,
+ p->init, 5,
+ p->payload,
+ session->fullpacket_payload_len,
+ &session->remote.mac_abstract);
+
+ /* Compare the calculated hash with the MAC we just read from
+ * the network. The read one is at the very end of the payload
+ * buffer. Note that 'payload_len' here is the packet_length
+ * field which includes the padding but not the MAC.
+ */
+ if (memcmp(macbuf, p->payload + session->fullpacket_payload_len,
+ session->remote.mac->mac_len)) {
+ session->fullpacket_macstate = LIBSSH2_MAC_INVALID;
+ }
+ }
+
+ session->remote.seqno++;
+
+ /* ignore the padding */
+ session->fullpacket_payload_len -= p->padding_length;
+
+ /* Check for and deal with decompression */
+ compressed =
+ session->local.comp != NULL &&
+ session->local.comp->compress &&
+ ((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
+ session->local.comp->use_in_auth);
+
+ if (compressed && session->remote.comp_abstract) {
+ /*
+ * The buffer for the decompression (remote.comp_abstract) is
+ * initialised in time when it is needed so as long it is NULL we
+ * cannot decompress.
+ */
+
+ unsigned char *data;
+ size_t data_len;
+ rc = session->remote.comp->decomp(session,
+ &data, &data_len,
+ LIBSSH2_PACKET_MAXDECOMP,
+ p->payload,
+ session->fullpacket_payload_len,
+ &session->remote.comp_abstract);
+ LIBSSH2_FREE(session, p->payload);
+ if(rc)
+ return rc;
+
+ p->payload = data;
+ session->fullpacket_payload_len = data_len;
+ }
+
+ session->fullpacket_packet_type = p->payload[0];
+
+ debugdump(session, "libssh2_transport_read() plain",
+ p->payload, session->fullpacket_payload_len);
+
+ session->fullpacket_state = libssh2_NB_state_created;
+ }
+
+ if (session->fullpacket_state == libssh2_NB_state_created) {
+ rc = _libssh2_packet_add(session, p->payload,
+ session->fullpacket_payload_len,
+ session->fullpacket_macstate);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ if (rc) {
+ session->fullpacket_state = libssh2_NB_state_idle;
+ return rc;
+ }
+ }
+
+ session->fullpacket_state = libssh2_NB_state_idle;
+
+ return session->fullpacket_packet_type;
+}
+
+
+/*
+ * _libssh2_transport_read
+ *
+ * Collect a packet into the input queue.
+ *
+ * Returns packet type added to input queue (0 if nothing added), or a
+ * negative error number.
+ */
+
+/*
+ * This function reads the binary stream as specified in chapter 6 of RFC4253
+ * "The Secure Shell (SSH) Transport Layer Protocol"
+ *
+ * DOES NOT call _libssh2_error() for ANY error case.
+ */
+int _libssh2_transport_read(LIBSSH2_SESSION * session)
+{
+ int rc;
+ struct transportpacket *p = &session->packet;
+ int remainbuf;
+ int remainpack;
+ int numbytes;
+ int numdecrypt;
+ unsigned char block[MAX_BLOCKSIZE];
+ int blocksize;
+ int encrypted = 1;
+ size_t total_num;
+
+ /* default clear the bit */
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
+
+ /*
+ * All channels, systems, subsystems, etc eventually make it down here
+ * when looking for more incoming data. If a key exchange is going on
+ * (LIBSSH2_STATE_EXCHANGING_KEYS bit is set) then the remote end will
+ * ONLY send key exchange related traffic. In non-blocking mode, there is
+ * a chance to break out of the kex_exchange function with an EAGAIN
+ * status, and never come back to it. If LIBSSH2_STATE_EXCHANGING_KEYS is
+ * active, then we must redirect to the key exchange. However, if
+ * kex_exchange is active (as in it is the one that calls this execution
+ * of packet_read, then don't redirect, as that would be an infinite loop!
+ */
+
+ if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS &&
+ !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) {
+
+ /* Whoever wants a packet won't get anything until the key re-exchange
+ * is done!
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
+ " key re-exchange from _libssh2_transport_read");
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * =============================== NOTE ===============================
+ * I know this is very ugly and not a really good use of "goto", but
+ * this case statement would be even uglier to do it any other way
+ */
+ if (session->readPack_state == libssh2_NB_state_jump1) {
+ session->readPack_state = libssh2_NB_state_idle;
+ encrypted = session->readPack_encrypted;
+ goto libssh2_transport_read_point1;
+ }
+
+ do {
+ if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ if (session->state & LIBSSH2_STATE_NEWKEYS) {
+ blocksize = session->remote.crypt->blocksize;
+ } else {
+ encrypted = 0; /* not encrypted */
+ blocksize = 5; /* not strictly true, but we can use 5 here to
+ make the checks below work fine still */
+ }
+
+ /* read/use a whole big chunk into a temporary area stored in
+ the LIBSSH2_SESSION struct. We will decrypt data from that
+ buffer into the packet buffer so this temp one doesn't have
+ to be able to keep a whole SSH packet, just be large enough
+ so that we can read big chunks from the network layer. */
+
+ /* how much data there is remaining in the buffer to deal with
+ before we should read more from the network */
+ remainbuf = p->writeidx - p->readidx;
+
+ /* if remainbuf turns negative we have a bad internal error */
+ assert(remainbuf >= 0);
+
+ if (remainbuf < blocksize) {
+ /* If we have less than a blocksize left, it is too
+ little data to deal with, read more */
+ ssize_t nread;
+
+ /* move any remainder to the start of the buffer so
+ that we can do a full refill */
+ if (remainbuf) {
+ memmove(p->buf, &p->buf[p->readidx], remainbuf);
+ p->readidx = 0;
+ p->writeidx = remainbuf;
+ } else {
+ /* nothing to move, just zero the indexes */
+ p->readidx = p->writeidx = 0;
+ }
+
+ /* now read a big chunk from the network into the temp buffer */
+ nread =
+ LIBSSH2_RECV(session, &p->buf[remainbuf],
+ PACKETBUFSIZE - remainbuf,
+ LIBSSH2_SOCKET_RECV_FLAGS(session));
+ if (nread <= 0) {
+ /* check if this is due to EAGAIN and return the special
+ return code if so, error out normally otherwise */
+ if ((nread < 0) && (nread == -EAGAIN)) {
+ session->socket_block_directions |=
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error recving %d bytes (got %d)",
+ PACKETBUFSIZE - remainbuf, -nread);
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Recved %d/%d bytes to %p+%d", nread,
+ PACKETBUFSIZE - remainbuf, p->buf, remainbuf);
+
+ debugdump(session, "libssh2_transport_read() raw",
+ &p->buf[remainbuf], nread);
+ /* advance write pointer */
+ p->writeidx += nread;
+
+ /* update remainbuf counter */
+ remainbuf = p->writeidx - p->readidx;
+ }
+
+ /* how much data to deal with from the buffer */
+ numbytes = remainbuf;
+
+ if (!p->total_num) {
+ /* No payload package area allocated yet. To know the
+ size of this payload, we need to decrypt the first
+ blocksize data. */
+
+ if (numbytes < blocksize) {
+ /* we can't act on anything less than blocksize, but this
+ check is only done for the initial block since once we have
+ got the start of a block we can in fact deal with fractions
+ */
+ session->socket_block_directions |=
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ if (encrypted) {
+ rc = decrypt(session, &p->buf[p->readidx], block, blocksize);
+ if (rc != LIBSSH2_ERROR_NONE) {
+ return rc;
+ }
+ /* save the first 5 bytes of the decrypted package, to be
+ used in the hash calculation later down. */
+ memcpy(p->init, &p->buf[p->readidx], 5);
+ } else {
+ /* the data is plain, just copy it verbatim to
+ the working block buffer */
+ memcpy(block, &p->buf[p->readidx], blocksize);
+ }
+
+ /* advance the read pointer */
+ p->readidx += blocksize;
+
+ /* we now have the initial blocksize bytes decrypted,
+ * and we can extract packet and padding length from it
+ */
+ p->packet_length = _libssh2_ntohu32(block);
+ if (p->packet_length < 1)
+ return LIBSSH2_ERROR_DECRYPT;
+
+ p->padding_length = block[4];
+
+ /* total_num is the number of bytes following the initial
+ (5 bytes) packet length and padding length fields */
+ total_num =
+ p->packet_length - 1 +
+ (encrypted ? session->remote.mac->mac_len : 0);
+
+ /* RFC4253 section 6.1 Maximum Packet Length says:
+ *
+ * "All implementations MUST be able to process
+ * packets with uncompressed payload length of 32768
+ * bytes or less and total packet size of 35000 bytes
+ * or less (including length, padding length, payload,
+ * padding, and MAC.)."
+ */
+ if (total_num > LIBSSH2_PACKET_MAXPAYLOAD) {
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
+
+ /* Get a packet handle put data into. We get one to
+ hold all data, including padding and MAC. */
+ p->payload = LIBSSH2_ALLOC(session, total_num);
+ if (!p->payload) {
+ return LIBSSH2_ERROR_ALLOC;
+ }
+ p->total_num = total_num;
+ /* init write pointer to start of payload buffer */
+ p->wptr = p->payload;
+
+ if (blocksize > 5) {
+ /* copy the data from index 5 to the end of
+ the blocksize from the temporary buffer to
+ the start of the decrypted buffer */
+ memcpy(p->wptr, &block[5], blocksize - 5);
+ p->wptr += blocksize - 5; /* advance write pointer */
+ }
+
+ /* init the data_num field to the number of bytes of
+ the package read so far */
+ p->data_num = p->wptr - p->payload;
+
+ /* we already dealt with a blocksize worth of data */
+ numbytes -= blocksize;
+ }
+
+ /* how much there is left to add to the current payload
+ package */
+ remainpack = p->total_num - p->data_num;
+
+ if (numbytes > remainpack) {
+ /* if we have more data in the buffer than what is going into this
+ particular packet, we limit this round to this packet only */
+ numbytes = remainpack;
+ }
+
+ if (encrypted) {
+ /* At the end of the incoming stream, there is a MAC,
+ and we don't want to decrypt that since we need it
+ "raw". We MUST however decrypt the padding data
+ since it is used for the hash later on. */
+ int skip = session->remote.mac->mac_len;
+
+ /* if what we have plus numbytes is bigger than the
+ total minus the skip margin, we should lower the
+ amount to decrypt even more */
+ if ((p->data_num + numbytes) > (p->total_num - skip)) {
+ numdecrypt = (p->total_num - skip) - p->data_num;
+ } else {
+ int frac;
+ numdecrypt = numbytes;
+ frac = numdecrypt % blocksize;
+ if (frac) {
+ /* not an aligned amount of blocks,
+ align it */
+ numdecrypt -= frac;
+ /* and make it no unencrypted data
+ after it */
+ numbytes = 0;
+ }
+ }
+ } else {
+ /* unencrypted data should not be decrypted at all */
+ numdecrypt = 0;
+ }
+
+ /* if there are bytes to decrypt, do that */
+ if (numdecrypt > 0) {
+ /* now decrypt the lot */
+ rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt);
+ if (rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ return rc;
+ }
+
+ /* advance the read pointer */
+ p->readidx += numdecrypt;
+ /* advance write pointer */
+ p->wptr += numdecrypt;
+ /* increase data_num */
+ p->data_num += numdecrypt;
+
+ /* bytes left to take care of without decryption */
+ numbytes -= numdecrypt;
+ }
+
+ /* if there are bytes to copy that aren't decrypted, simply
+ copy them as-is to the target buffer */
+ if (numbytes > 0) {
+ memcpy(p->wptr, &p->buf[p->readidx], numbytes);
+
+ /* advance the read pointer */
+ p->readidx += numbytes;
+ /* advance write pointer */
+ p->wptr += numbytes;
+ /* increase data_num */
+ p->data_num += numbytes;
+ }
+
+ /* now check how much data there's left to read to finish the
+ current packet */
+ remainpack = p->total_num - p->data_num;
+
+ if (!remainpack) {
+ /* we have a full packet */
+ libssh2_transport_read_point1:
+ rc = fullpacket(session, encrypted);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+
+ if (session->packAdd_state != libssh2_NB_state_idle)
+ {
+ /* fullpacket only returns LIBSSH2_ERROR_EAGAIN if
+ * libssh2_packet_add returns LIBSSH2_ERROR_EAGAIN. If that
+ * returns LIBSSH2_ERROR_EAGAIN but the packAdd_state is idle,
+ * then the packet has been added to the brigade, but some
+ * immediate action that was taken based on the packet
+ * type (such as key re-exchange) is not yet complete.
+ * Clear the way for a new packet to be read in.
+ */
+ session->readPack_encrypted = encrypted;
+ session->readPack_state = libssh2_NB_state_jump1;
+ }
+
+ return rc;
+ }
+
+ p->total_num = 0; /* no packet buffer available */
+
+ return rc;
+ }
+ } while (1); /* loop */
+
+ return LIBSSH2_ERROR_SOCKET_RECV; /* we never reach this point */
+}
+
+static int
+send_existing(LIBSSH2_SESSION *session, const unsigned char *data,
+ size_t data_len, ssize_t *ret)
+{
+ ssize_t rc;
+ ssize_t length;
+ struct transportpacket *p = &session->packet;
+
+ if (!p->olen) {
+ *ret = 0;
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ /* send as much as possible of the existing packet */
+ if ((data != p->odata) || (data_len != p->olen)) {
+ /* When we are about to complete the sending of a packet, it is vital
+ that the caller doesn't try to send a new/different packet since
+ we don't add this one up until the previous one has been sent. To
+ make the caller really notice his/hers flaw, we return error for
+ this case */
+ return LIBSSH2_ERROR_BAD_USE;
+ }
+
+ *ret = 1; /* set to make our parent return */
+
+ /* number of bytes left to send */
+ length = p->ototal_num - p->osent;
+
+ rc = LIBSSH2_SEND(session, &p->outbuf[p->osent], length,
+ LIBSSH2_SOCKET_SEND_FLAGS(session));
+ if (rc < 0)
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error sending %d bytes: %d", length, -rc);
+ else {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Sent %d/%d bytes at %p+%d", rc, length, p->outbuf,
+ p->osent);
+ debugdump(session, "libssh2_transport_write send()",
+ &p->outbuf[p->osent], rc);
+ }
+
+ if (rc == length) {
+ /* the remainder of the package was sent */
+ p->ototal_num = 0;
+ p->olen = 0;
+ /* we leave *ret set so that the parent returns as we MUST return back
+ a send success now, so that we don't risk sending EAGAIN later
+ which then would confuse the parent function */
+ return LIBSSH2_ERROR_NONE;
+
+ }
+ else if (rc < 0) {
+ /* nothing was sent */
+ if (rc != -EAGAIN)
+ /* send failure! */
+ return LIBSSH2_ERROR_SOCKET_SEND;
+
+ session->socket_block_directions |= LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ p->osent += rc; /* we sent away this much data */
+
+ return rc < length ? LIBSSH2_ERROR_EAGAIN : LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * libssh2_transport_send
+ *
+ * Send a packet, encrypting it and adding a MAC code if necessary
+ * Returns 0 on success, non-zero on failure.
+ *
+ * The data is provided as _two_ data areas that are combined by this
+ * function. The 'data' part is sent immediately before 'data2'. 'data2' may
+ * be set to NULL to only use a single part.
+ *
+ * Returns LIBSSH2_ERROR_EAGAIN if it would block or if the whole packet was
+ * not sent yet. If it does so, the caller should call this function again as
+ * soon as it is likely that more data can be sent, and this function MUST
+ * then be called with the same argument set (same data pointer and same
+ * data_len) until ERROR_NONE or failure is returned.
+ *
+ * This function DOES NOT call _libssh2_error() on any errors.
+ */
+int _libssh2_transport_send(LIBSSH2_SESSION *session,
+ const unsigned char *data, size_t data_len,
+ const unsigned char *data2, size_t data2_len)
+{
+ int blocksize =
+ (session->state & LIBSSH2_STATE_NEWKEYS) ?
+ session->local.crypt->blocksize : 8;
+ int padding_length;
+ size_t packet_length;
+ int total_length;
+#ifdef RANDOM_PADDING
+ int rand_max;
+ int seed = data[0]; /* FIXME: make this random */
+#endif
+ struct transportpacket *p = &session->packet;
+ int encrypted;
+ int compressed;
+ ssize_t ret;
+ int rc;
+ const unsigned char *orgdata = data;
+ size_t orgdata_len = data_len;
+
+ /*
+ * If the last read operation was interrupted in the middle of a key
+ * exchange, we must complete that key exchange before continuing to write
+ * further data.
+ *
+ * See the similar block in _libssh2_transport_read for more details.
+ */
+ if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS &&
+ !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) {
+ /* Don't write any new packets if we're still in the middle of a key
+ * exchange. */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
+ " key re-exchange from _libssh2_transport_send");
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if (rc)
+ return rc;
+ }
+
+ debugdump(session, "libssh2_transport_write plain", data, data_len);
+ if(data2)
+ debugdump(session, "libssh2_transport_write plain2", data2, data2_len);
+
+ /* FIRST, check if we have a pending write to complete. send_existing
+ only sanity-check data and data_len and not data2 and data2_len!! */
+ rc = send_existing(session, data, data_len, &ret);
+ if (rc)
+ return rc;
+
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND;
+
+ if (ret)
+ /* set by send_existing if data was sent */
+ return rc;
+
+ encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0;
+
+ compressed =
+ session->local.comp != NULL &&
+ session->local.comp->compress &&
+ ((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
+ session->local.comp->use_in_auth);
+
+ if (encrypted && compressed) {
+ /* the idea here is that these function must fail if the output gets
+ larger than what fits in the assigned buffer so thus they don't
+ check the input size as we don't know how much it compresses */
+ size_t dest_len = MAX_SSH_PACKET_LEN-5-256;
+ size_t dest2_len = dest_len;
+
+ /* compress directly to the target buffer */
+ rc = session->local.comp->comp(session,
+ &p->outbuf[5], &dest_len,
+ data, data_len,
+ &session->local.comp_abstract);
+ if(rc)
+ return rc; /* compression failure */
+
+ if(data2 && data2_len) {
+ /* compress directly to the target buffer right after where the
+ previous call put data */
+ dest2_len -= dest_len;
+
+ rc = session->local.comp->comp(session,
+ &p->outbuf[5+dest_len], &dest2_len,
+ data2, data2_len,
+ &session->local.comp_abstract);
+ }
+ else
+ dest2_len = 0;
+ if(rc)
+ return rc; /* compression failure */
+
+ data_len = dest_len + dest2_len; /* use the combined length */
+ }
+ else {
+ if((data_len + data2_len) >= (MAX_SSH_PACKET_LEN-0x100))
+ /* too large packet, return error for this until we make this
+ function split it up and send multiple SSH packets */
+ return LIBSSH2_ERROR_INVAL;
+
+ /* copy the payload data */
+ memcpy(&p->outbuf[5], data, data_len);
+ if(data2 && data2_len)
+ memcpy(&p->outbuf[5+data_len], data2, data2_len);
+ data_len += data2_len; /* use the combined length */
+ }
+
+
+ /* RFC4253 says: Note that the length of the concatenation of
+ 'packet_length', 'padding_length', 'payload', and 'random padding'
+ MUST be a multiple of the cipher block size or 8, whichever is
+ larger. */
+
+ /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == 0 */
+
+ packet_length = data_len + 1 + 4; /* 1 is for padding_length field
+ 4 for the packet_length field */
+
+ /* at this point we have it all except the padding */
+
+ /* first figure out our minimum padding amount to make it an even
+ block size */
+ padding_length = blocksize - (packet_length % blocksize);
+
+ /* if the padding becomes too small we add another blocksize worth
+ of it (taken from the original libssh2 where it didn't have any
+ real explanation) */
+ if (padding_length < 4) {
+ padding_length += blocksize;
+ }
+#ifdef RANDOM_PADDING
+ /* FIXME: we can add padding here, but that also makes the packets
+ bigger etc */
+
+ /* now we can add 'blocksize' to the padding_length N number of times
+ (to "help thwart traffic analysis") but it must be less than 255 in
+ total */
+ rand_max = (255 - padding_length) / blocksize + 1;
+ padding_length += blocksize * (seed % rand_max);
+#endif
+
+ packet_length += padding_length;
+
+ /* append the MAC length to the total_length size */
+ total_length =
+ packet_length + (encrypted ? session->local.mac->mac_len : 0);
+
+ /* store packet_length, which is the size of the whole packet except
+ the MAC and the packet_length field itself */
+ _libssh2_htonu32(p->outbuf, packet_length - 4);
+ /* store padding_length */
+ p->outbuf[4] = (unsigned char)padding_length;
+
+ /* fill the padding area with random junk */
+ _libssh2_random(p->outbuf + 5 + data_len, padding_length);
+
+ if (encrypted) {
+ size_t i;
+
+ /* Calculate MAC hash. Put the output at index packet_length,
+ since that size includes the whole packet. The MAC is
+ calculated on the entire unencrypted packet, including all
+ fields except the MAC field itself. */
+ session->local.mac->hash(session, p->outbuf + packet_length,
+ session->local.seqno, p->outbuf,
+ packet_length, NULL, 0,
+ &session->local.mac_abstract);
+
+ /* Encrypt the whole packet data, one block size at a time.
+ The MAC field is not encrypted. */
+ for(i = 0; i < packet_length; i += session->local.crypt->blocksize) {
+ unsigned char *ptr = &p->outbuf[i];
+ if (session->local.crypt->crypt(session, ptr,
+ session->local.crypt->blocksize,
+ &session->local.crypt_abstract))
+ return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ }
+ }
+
+ session->local.seqno++;
+
+ ret = LIBSSH2_SEND(session, p->outbuf, total_length,
+ LIBSSH2_SOCKET_SEND_FLAGS(session));
+ if (ret < 0)
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error sending %d bytes: %d", total_length, -ret);
+ else {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, "Sent %d/%d bytes at %p",
+ ret, total_length, p->outbuf);
+ debugdump(session, "libssh2_transport_write send()", p->outbuf, ret);
+ }
+
+ if (ret != total_length) {
+ if (ret >= 0 || ret == -EAGAIN) {
+ /* the whole packet could not be sent, save the rest */
+ session->socket_block_directions |= LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ p->odata = orgdata;
+ p->olen = orgdata_len;
+ p->osent = ret <= 0 ? 0 : ret;
+ p->ototal_num = total_length;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ return LIBSSH2_ERROR_SOCKET_SEND;
+ }
+
+ /* the whole thing got sent away */
+ p->odata = NULL;
+ p->olen = 0;
+
+ return LIBSSH2_ERROR_NONE; /* all is good */
+}
diff --git a/libs/libssh2/src/transport.h b/libs/libssh2/src/transport.h
new file mode 100644
index 0000000000..89982a67f0
--- /dev/null
+++ b/libs/libssh2/src/transport.h
@@ -0,0 +1,87 @@
+#ifndef __LIBSSH2_TRANSPORT_H
+#define __LIBSSH2_TRANSPORT_H
+
+/* Copyright (C) 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (C) 2009-2010 by Daniel Stenberg
+ * Author: Daniel Stenberg <daniel@haxx.se>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file handles reading and writing to the SECSH transport layer. RFC4253.
+ */
+
+#include "libssh2_priv.h"
+#include "packet.h"
+
+
+/*
+ * libssh2_transport_send
+ *
+ * Send a packet, encrypting it and adding a MAC code if necessary
+ * Returns 0 on success, non-zero on failure.
+ *
+ * The data is provided as _two_ data areas that are combined by this
+ * function. The 'data' part is sent immediately before 'data2'. 'data2' can
+ * be set to NULL (or data2_len to 0) to only use a single part.
+ *
+ * Returns LIBSSH2_ERROR_EAGAIN if it would block or if the whole packet was
+ * not sent yet. If it does so, the caller should call this function again as
+ * soon as it is likely that more data can be sent, and this function MUST
+ * then be called with the same argument set (same data pointer and same
+ * data_len) until ERROR_NONE or failure is returned.
+ *
+ * This function DOES NOT call _libssh2_error() on any errors.
+ */
+int _libssh2_transport_send(LIBSSH2_SESSION *session,
+ const unsigned char *data, size_t data_len,
+ const unsigned char *data2, size_t data2_len);
+
+/*
+ * _libssh2_transport_read
+ *
+ * Collect a packet into the input brigade block only controls whether or not
+ * to wait for a packet to start.
+ *
+ * Returns packet type added to input brigade (PACKET_NONE if nothing added),
+ * or PACKET_FAIL on failure and PACKET_EAGAIN if it couldn't process a full
+ * packet.
+ */
+
+/*
+ * This function reads the binary stream as specified in chapter 6 of RFC4253
+ * "The Secure Shell (SSH) Transport Layer Protocol"
+ */
+int _libssh2_transport_read(LIBSSH2_SESSION * session);
+
+#endif /* __LIBSSH2_TRANSPORT_H */
diff --git a/libs/libssh2/src/userauth.c b/libs/libssh2/src/userauth.c
new file mode 100644
index 0000000000..cdfa25e663
--- /dev/null
+++ b/libs/libssh2/src/userauth.c
@@ -0,0 +1,1917 @@
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2005 Mikhail Gusarov <dottedmag@dottedmag.net>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <assert.h>
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include "transport.h"
+#include "session.h"
+#include "userauth.h"
+
+/* libssh2_userauth_list
+ *
+ * List authentication methods
+ * Will yield successful login if "none" happens to be allowable for this user
+ * Not a common configuration for any SSH server though
+ * username should be NULL, or a null terminated string
+ */
+static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
+ unsigned int username_len)
+{
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ /* packet_type(1) + username_len(4) + service_len(4) +
+ service(14)"ssh-connection" + method_len(4) = 27 */
+ unsigned long methods_len;
+ unsigned char *s;
+ int rc;
+
+ if (session->userauth_list_state == libssh2_NB_state_idle) {
+ /* Zero the whole thing out */
+ memset(&session->userauth_list_packet_requirev_state, 0,
+ sizeof(session->userauth_list_packet_requirev_state));
+
+ session->userauth_list_data_len = username_len + 27;
+
+ s = session->userauth_list_data =
+ LIBSSH2_ALLOC(session, session->userauth_list_data_len);
+ if (!session->userauth_list_data) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for userauth_list");
+ return NULL;
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", 14);
+ _libssh2_store_u32(&s, 4); /* send "none" separately */
+
+ session->userauth_list_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_list_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_list_data,
+ session->userauth_list_data_len,
+ (unsigned char *)"none", 4);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ }
+ /* now free the packet that was sent */
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+
+ if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-none request");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ session->userauth_list_state = libssh2_NB_state_sent;
+ }
+
+ if (session->userauth_list_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_list_data,
+ &session->userauth_list_data_len, 0,
+ NULL, 0,
+ &session->userauth_list_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ } else if (rc) {
+ _libssh2_error(session, rc, "Failed getting response");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ if (session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ /* Wow, who'dve thought... */
+ _libssh2_error(session, LIBSSH2_ERROR_NONE, "No error");
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ methods_len = _libssh2_ntohu32(session->userauth_list_data + 1);
+
+ /* Do note that the memory areas overlap! */
+ memmove(session->userauth_list_data, session->userauth_list_data + 5,
+ methods_len);
+ session->userauth_list_data[methods_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Permitted auth methods: %s",
+ session->userauth_list_data);
+ }
+
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return (char *) session->userauth_list_data;
+}
+
+/* libssh2_userauth_list
+ *
+ * List authentication methods
+ * Will yield successful login if "none" happens to be allowable for this user
+ * Not a common configuration for any SSH server though
+ * username should be NULL, or a null terminated string
+ */
+LIBSSH2_API char *
+libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user,
+ unsigned int user_len)
+{
+ char *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ userauth_list(session, user, user_len));
+ return ptr;
+}
+
+/*
+ * libssh2_userauth_authenticated
+ *
+ * Returns: 0 if not yet authenticated
+ * 1 if already authenticated
+ */
+LIBSSH2_API int
+libssh2_userauth_authenticated(LIBSSH2_SESSION * session)
+{
+ return (session->state & LIBSSH2_STATE_AUTHENTICATED)?1:0;
+}
+
+
+
+/* userauth_password
+ * Plain ol' login
+ */
+static int
+userauth_password(LIBSSH2_SESSION *session,
+ const char *username, unsigned int username_len,
+ const unsigned char *password, unsigned int password_len,
+ LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
+{
+ unsigned char *s;
+ static const unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0
+ };
+ int rc;
+
+ if (session->userauth_pswd_state == libssh2_NB_state_idle) {
+ /* Zero the whole thing out */
+ memset(&session->userauth_pswd_packet_requirev_state, 0,
+ sizeof(session->userauth_pswd_packet_requirev_state));
+
+ /*
+ * 40 = packet_type(1) + username_len(4) + service_len(4) +
+ * service(14)"ssh-connection" + method_len(4) + method(8)"password" +
+ * chgpwdbool(1) + password_len(4) */
+ session->userauth_pswd_data_len = username_len + 40;
+
+ session->userauth_pswd_data0 =
+ (unsigned char) ~SSH_MSG_USERAUTH_PASSWD_CHANGEREQ;
+
+ /* TODO: remove this alloc with a fixed buffer in the session
+ struct */
+ s = session->userauth_pswd_data =
+ LIBSSH2_ALLOC(session, session->userauth_pswd_data_len);
+ if (!session->userauth_pswd_data) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "userauth-password request");
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1);
+ _libssh2_store_str(&s, "password", sizeof("password") - 1);
+ *s++ = '\0';
+ _libssh2_store_u32(&s, password_len);
+ /* 'password' is sent separately */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting to login using password authentication");
+
+ session->userauth_pswd_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_pswd_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_pswd_data,
+ session->userauth_pswd_data_len,
+ password, password_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block writing password request");
+ }
+
+ /* now free the sent packet */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+
+ if (rc) {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-password request");
+ }
+
+ session->userauth_pswd_state = libssh2_NB_state_sent;
+ }
+
+ password_response:
+
+ if ((session->userauth_pswd_state == libssh2_NB_state_sent)
+ || (session->userauth_pswd_state == libssh2_NB_state_sent1)
+ || (session->userauth_pswd_state == libssh2_NB_state_sent2)) {
+ if (session->userauth_pswd_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pswd_data,
+ &session->userauth_pswd_data_len,
+ 0, NULL, 0,
+ &session->
+ userauth_pswd_packet_requirev_state);
+
+ if (rc) {
+ if (rc != LIBSSH2_ERROR_EAGAIN)
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+
+ return _libssh2_error(session, rc,
+ "Waiting for password response");
+ }
+
+ if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password authentication successful");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return 0;
+ } else if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password authentication failed");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed "
+ "(username/password)");
+ }
+
+ session->userauth_pswd_newpw = NULL;
+ session->userauth_pswd_newpw_len = 0;
+
+ session->userauth_pswd_state = libssh2_NB_state_sent1;
+ }
+
+ if ((session->userauth_pswd_data[0] ==
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)
+ || (session->userauth_pswd_data0 ==
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)) {
+ session->userauth_pswd_data0 = SSH_MSG_USERAUTH_PASSWD_CHANGEREQ;
+
+ if ((session->userauth_pswd_state == libssh2_NB_state_sent1) ||
+ (session->userauth_pswd_state == libssh2_NB_state_sent2)) {
+ if (session->userauth_pswd_state == libssh2_NB_state_sent1) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password change required");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ }
+ if (passwd_change_cb) {
+ if (session->userauth_pswd_state == libssh2_NB_state_sent1) {
+ passwd_change_cb(session,
+ &session->userauth_pswd_newpw,
+ &session->userauth_pswd_newpw_len,
+ &session->abstract);
+ if (!session->userauth_pswd_newpw) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PASSWORD_EXPIRED,
+ "Password expired, and "
+ "callback failed");
+ }
+
+ /* basic data_len + newpw_len(4) */
+ session->userauth_pswd_data_len =
+ username_len + password_len + 44;
+
+ s = session->userauth_pswd_data =
+ LIBSSH2_ALLOC(session,
+ session->userauth_pswd_data_len);
+ if (!session->userauth_pswd_data) {
+ LIBSSH2_FREE(session,
+ session->userauth_pswd_newpw);
+ session->userauth_pswd_newpw = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for userauth password "
+ "change request");
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection",
+ sizeof("ssh-connection") - 1);
+ _libssh2_store_str(&s, "password",
+ sizeof("password") - 1);
+ *s++ = 0x01;
+ _libssh2_store_str(&s, (char *)password, password_len);
+ _libssh2_store_u32(&s,
+ session->userauth_pswd_newpw_len);
+ /* send session->userauth_pswd_newpw separately */
+
+ session->userauth_pswd_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->userauth_pswd_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session,
+ session->userauth_pswd_data,
+ session->userauth_pswd_data_len,
+ (unsigned char *)
+ session->userauth_pswd_newpw,
+ session->userauth_pswd_newpw_len);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting");
+ }
+
+ /* free the allocated packets again */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pswd_newpw);
+ session->userauth_pswd_newpw = NULL;
+
+ if (rc) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth "
+ "password-change request");
+ }
+
+ /*
+ * Ugliest use of goto ever. Blame it on the
+ * askN => requirev migration.
+ */
+ session->userauth_pswd_state = libssh2_NB_state_sent;
+ goto password_response;
+ }
+ }
+ } else {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED,
+ "Password Expired, and no callback "
+ "specified");
+ }
+ }
+ }
+
+ /* FAILURE */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed");
+}
+
+/*
+ * libssh2_userauth_password_ex
+ *
+ * Plain ol' login
+ */
+
+LIBSSH2_API int
+libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username,
+ unsigned int username_len, const char *password,
+ unsigned int password_len,
+ LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_password(session, username, username_len,
+ (unsigned char *)password, password_len,
+ passwd_change_cb));
+ return rc;
+}
+
+static int
+memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *pubkeyfiledata,
+ size_t pubkeyfiledata_len)
+{
+ unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
+ size_t pubkey_len = pubkeyfiledata_len;
+ unsigned int tmp_len;
+
+ if (pubkeyfiledata_len <= 1) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public key file");
+ }
+
+ pubkey = LIBSSH2_ALLOC(session, pubkeyfiledata_len);
+ if (!pubkey) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key data");
+ }
+
+ memcpy(pubkey, pubkeyfiledata, pubkeyfiledata_len);
+
+ /*
+ * Remove trailing whitespace
+ */
+ while (pubkey_len && isspace(pubkey[pubkey_len - 1]))
+ pubkey_len--;
+
+ if (!pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Missing public key data");
+ }
+
+ if ((sp1 = memchr(pubkey, ' ', pubkey_len)) == NULL) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid public key data");
+ }
+
+ sp1++;
+
+ if ((sp2 = memchr(sp1, ' ', pubkey_len - (sp1 - pubkey - 1))) == NULL) {
+ /* Assume that the id string is missing, but that it's okay */
+ sp2 = pubkey + pubkey_len;
+ }
+
+ if (libssh2_base64_decode(session, (char **) &tmp, &tmp_len,
+ (char *) sp1, sp2 - sp1)) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid key data, not base64 encoded");
+ }
+
+ /* Wasting some bytes here (okay, more than some), but since it's likely
+ * to be freed soon anyway, we'll just avoid the extra free/alloc and call
+ * it a wash
+ */
+ *method = pubkey;
+ *method_len = sp1 - pubkey - 1;
+
+ *pubkeydata = tmp;
+ *pubkeydata_len = tmp_len;
+
+ return 0;
+}
+
+/*
+ * file_read_publickey
+ *
+ * Read a public key from an id_???.pub style file
+ *
+ * Returns an allocated string containing the decoded key in *pubkeydata
+ * on success.
+ * Returns an allocated string containing the key method (e.g. "ssh-dss")
+ * in method on success.
+ */
+static int
+file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *pubkeyfile)
+{
+ FILE *fd;
+ char c;
+ unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
+ size_t pubkey_len = 0, sp_len;
+ unsigned int tmp_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading public key file: %s",
+ pubkeyfile);
+ /* Read Public Key */
+ fd = fopen(pubkeyfile, "r");
+ if (!fd) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open public key file");
+ }
+ while (!feof(fd) && 1 == fread(&c, 1, 1, fd) && c != '\r' && c != '\n') {
+ pubkey_len++;
+ }
+ rewind(fd);
+
+ if (pubkey_len <= 1) {
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public key file");
+ }
+
+ pubkey = LIBSSH2_ALLOC(session, pubkey_len);
+ if (!pubkey) {
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key data");
+ }
+ if (fread(pubkey, 1, pubkey_len, fd) != pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to read public key from file");
+ }
+ fclose(fd);
+ /*
+ * Remove trailing whitespace
+ */
+ while (pubkey_len && isspace(pubkey[pubkey_len - 1])) {
+ pubkey_len--;
+ }
+
+ if (!pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Missing public key data");
+ }
+
+ if ((sp1 = memchr(pubkey, ' ', pubkey_len)) == NULL) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid public key data");
+ }
+
+ sp1++;
+
+ sp_len = sp1 > pubkey ? (sp1 - pubkey) - 1 : 0;
+ if ((sp2 = memchr(sp1, ' ', pubkey_len - sp_len)) == NULL) {
+ /* Assume that the id string is missing, but that it's okay */
+ sp2 = pubkey + pubkey_len;
+ }
+
+ if (libssh2_base64_decode(session, (char **) &tmp, &tmp_len,
+ (char *) sp1, sp2 - sp1)) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid key data, not base64 encoded");
+ }
+
+ /* Wasting some bytes here (okay, more than some), but since it's likely
+ * to be freed soon anyway, we'll just avoid the extra free/alloc and call
+ * it a wash */
+ *method = pubkey;
+ *method_len = sp1 - pubkey - 1;
+
+ *pubkeydata = tmp;
+ *pubkeydata_len = tmp_len;
+
+ return 0;
+}
+
+static int
+memory_read_privatekey(LIBSSH2_SESSION * session,
+ const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
+ void **hostkey_abstract,
+ const unsigned char *method, int method_len,
+ const char *privkeyfiledata, size_t privkeyfiledata_len,
+ const char *passphrase)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
+ libssh2_hostkey_methods();
+
+ *hostkey_method = NULL;
+ *hostkey_abstract = NULL;
+ while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
+ if ((*hostkey_methods_avail)->initPEMFromMemory
+ && strncmp((*hostkey_methods_avail)->name, (const char *) method,
+ method_len) == 0) {
+ *hostkey_method = *hostkey_methods_avail;
+ break;
+ }
+ hostkey_methods_avail++;
+ }
+ if (!*hostkey_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No handler for specified private key");
+ }
+
+ if ((*hostkey_method)->
+ initPEMFromMemory(session, privkeyfiledata, privkeyfiledata_len,
+ (unsigned char *) passphrase,
+ hostkey_abstract)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to initialize private key from file");
+ }
+
+ return 0;
+}
+
+/* libssh2_file_read_privatekey
+ * Read a PEM encoded private key from an id_??? style file
+ */
+static int
+file_read_privatekey(LIBSSH2_SESSION * session,
+ const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
+ void **hostkey_abstract,
+ const unsigned char *method, int method_len,
+ const char *privkeyfile, const char *passphrase)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
+ libssh2_hostkey_methods();
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading private key file: %s",
+ privkeyfile);
+ *hostkey_method = NULL;
+ *hostkey_abstract = NULL;
+ while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
+ if ((*hostkey_methods_avail)->initPEM
+ && strncmp((*hostkey_methods_avail)->name, (const char *) method,
+ method_len) == 0) {
+ *hostkey_method = *hostkey_methods_avail;
+ break;
+ }
+ hostkey_methods_avail++;
+ }
+ if (!*hostkey_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No handler for specified private key");
+ }
+
+ if ((*hostkey_method)->
+ initPEM(session, privkeyfile, (unsigned char *) passphrase,
+ hostkey_abstract)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to initialize private key from file");
+ }
+
+ return 0;
+}
+
+struct privkey_file {
+ const char *filename;
+ const char *passphrase;
+};
+
+static int
+sign_frommemory(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ struct privkey_file *pk_file = (struct privkey_file *) (*abstract);
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ void *hostkey_abstract;
+ struct iovec datavec;
+ int rc;
+
+ rc = memory_read_privatekey(session, &privkeyobj, &hostkey_abstract,
+ session->userauth_pblc_method,
+ session->userauth_pblc_method_len,
+ pk_file->filename,
+ strlen(pk_file->filename),
+ pk_file->passphrase);
+ if(rc)
+ return rc;
+
+ libssh2_prepare_iovec(&datavec, 1);
+ datavec.iov_base = (void *)data;
+ datavec.iov_len = data_len;
+
+ if (privkeyobj->signv(session, sig, sig_len, 1, &datavec,
+ &hostkey_abstract)) {
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, abstract);
+ }
+ return -1;
+ }
+
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return 0;
+}
+
+static int
+sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ struct privkey_file *privkey_file = (struct privkey_file *) (*abstract);
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ void *hostkey_abstract;
+ struct iovec datavec;
+ int rc;
+
+ rc = file_read_privatekey(session, &privkeyobj, &hostkey_abstract,
+ session->userauth_pblc_method,
+ session->userauth_pblc_method_len,
+ privkey_file->filename,
+ privkey_file->passphrase);
+ if(rc)
+ return rc;
+
+ libssh2_prepare_iovec(&datavec, 1);
+ datavec.iov_base = (void *)data;
+ datavec.iov_len = data_len;
+
+ if (privkeyobj->signv(session, sig, sig_len, 1, &datavec,
+ &hostkey_abstract)) {
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return -1;
+ }
+
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return 0;
+}
+
+
+
+/* userauth_hostbased_fromfile
+ * Authenticate using a keypair found in the named files
+ */
+static int
+userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
+ const char *username, size_t username_len,
+ const char *publickey, const char *privatekey,
+ const char *passphrase, const char *hostname,
+ size_t hostname_len,
+ const char *local_username,
+ size_t local_username_len)
+{
+ int rc;
+
+ if (session->userauth_host_state == libssh2_NB_state_idle) {
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ unsigned char *pubkeydata, *sig = NULL;
+ size_t pubkeydata_len = 0;
+ size_t sig_len = 0;
+ void *abstract;
+ unsigned char buf[5];
+ struct iovec datavec[4];
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_host_packet_requirev_state, 0,
+ sizeof(session->userauth_host_packet_requirev_state));
+
+ if (publickey) {
+ rc = file_read_publickey(session, &session->userauth_host_method,
+ &session->userauth_host_method_len,
+ &pubkeydata, &pubkeydata_len, publickey);
+ if(rc)
+ /* Note: file_read_publickey() calls _libssh2_error() */
+ return rc;
+ }
+ else {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfile(session,
+ &session->userauth_host_method,
+ &session->userauth_host_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekey, passphrase);
+ if (rc)
+ /* libssh2_pub_priv_keyfile calls _libssh2_error() */
+ return rc;
+ }
+
+ /*
+ * 52 = packet_type(1) + username_len(4) + servicename_len(4) +
+ * service_name(14)"ssh-connection" + authmethod_len(4) +
+ * authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) +
+ * hostname_len(4) + local_username_len(4)
+ */
+ session->userauth_host_packet_len =
+ username_len + session->userauth_host_method_len + hostname_len +
+ local_username_len + pubkeydata_len + 52;
+
+ /*
+ * Preallocate space for an overall length, method name again,
+ * and the signature, which won't be any larger than the size of
+ * the publickeydata itself
+ */
+ session->userauth_host_s = session->userauth_host_packet =
+ LIBSSH2_ALLOC(session,
+ session->userauth_host_packet_len + 4 +
+ (4 + session->userauth_host_method_len) +
+ (4 + pubkeydata_len));
+ if (!session->userauth_host_packet) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, pubkeydata);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory");
+ }
+
+ *(session->userauth_host_s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&session->userauth_host_s, username, username_len);
+ _libssh2_store_str(&session->userauth_host_s, "ssh-connection", 14);
+ _libssh2_store_str(&session->userauth_host_s, "hostbased", 9);
+ _libssh2_store_str(&session->userauth_host_s,
+ (const char *)session->userauth_host_method,
+ session->userauth_host_method_len);
+ _libssh2_store_str(&session->userauth_host_s, (const char *)pubkeydata,
+ pubkeydata_len);
+ LIBSSH2_FREE(session, pubkeydata);
+ _libssh2_store_str(&session->userauth_host_s, hostname, hostname_len);
+ _libssh2_store_str(&session->userauth_host_s, local_username,
+ local_username_len);
+
+ rc = file_read_privatekey(session, &privkeyobj, &abstract,
+ session->userauth_host_method,
+ session->userauth_host_method_len,
+ privatekey, passphrase);
+ if(rc) {
+ /* Note: file_read_privatekey() calls _libssh2_error() */
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ return rc;
+ }
+
+ _libssh2_htonu32(buf, session->session_id_len);
+ libssh2_prepare_iovec(datavec, 4);
+ datavec[0].iov_base = (void *)buf;
+ datavec[0].iov_len = 4;
+ datavec[1].iov_base = (void *)session->session_id;
+ datavec[1].iov_len = session->session_id_len;
+ datavec[2].iov_base = (void *)session->userauth_host_packet;
+ datavec[2].iov_len = session->userauth_host_packet_len;
+
+ if (privkeyobj && privkeyobj->signv &&
+ privkeyobj->signv(session, &sig, &sig_len, 3,
+ datavec, &abstract)) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ if (privkeyobj->dtor) {
+ privkeyobj->dtor(session, &abstract);
+ }
+ return -1;
+ }
+
+ if (privkeyobj && privkeyobj->dtor) {
+ privkeyobj->dtor(session, &abstract);
+ }
+
+ if (sig_len > pubkeydata_len) {
+ unsigned char *newpacket;
+ /* Should *NEVER* happen, but...well.. better safe than sorry */
+ newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet,
+ session->userauth_host_packet_len + 4 +
+ (4 + session->userauth_host_method_len)
+ + (4 + sig_len)); /* PK sigblob */
+ if (!newpacket) {
+ LIBSSH2_FREE(session, sig);
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating additional space for "
+ "userauth-hostbased packet");
+ }
+ session->userauth_host_packet = newpacket;
+ }
+
+ session->userauth_host_s =
+ session->userauth_host_packet + session->userauth_host_packet_len;
+
+ _libssh2_store_u32(&session->userauth_host_s,
+ 4 + session->userauth_host_method_len + 4 + sig_len);
+ _libssh2_store_str(&session->userauth_host_s,
+ (const char *)session->userauth_host_method,
+ session->userauth_host_method_len);
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+
+ _libssh2_store_str(&session->userauth_host_s, (const char *)sig,
+ sig_len);
+ LIBSSH2_FREE(session, sig);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting hostbased authentication");
+
+ session->userauth_host_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_host_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_host_packet,
+ session->userauth_host_s -
+ session->userauth_host_packet,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ }
+ else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ session->userauth_host_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-hostbased request");
+ }
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+
+ session->userauth_host_state = libssh2_NB_state_sent;
+ }
+
+ if (session->userauth_host_state == libssh2_NB_state_sent) {
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ size_t data_len;
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_host_data,
+ &data_len, 0, NULL, 0,
+ &session->
+ userauth_host_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ }
+
+ session->userauth_host_state = libssh2_NB_state_idle;
+ if (rc) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Auth failed");
+ }
+
+ if (session->userauth_host_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Hostbased authentication successful");
+ /* We are us and we've proved it. */
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ session->userauth_host_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ return 0;
+ }
+ }
+
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ session->userauth_host_data = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid signature for supplied public key, or bad "
+ "username/public key combination");
+}
+
+/* libssh2_userauth_hostbased_fromfile_ex
+ * Authenticate using a keypair found in the named files
+ */
+LIBSSH2_API int
+libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ const char *host,
+ unsigned int host_len,
+ const char *localuser,
+ unsigned int localuser_len)
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_hostbased_fromfile(session, user, user_len,
+ publickey, privatekey,
+ passphrase, host, host_len,
+ localuser, localuser_len));
+ return rc;
+}
+
+
+
+int
+_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *username,
+ unsigned int username_len,
+ const unsigned char *pubkeydata,
+ unsigned long pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
+ void *abstract)
+{
+ unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_PK_OK, 0
+ };
+ int rc;
+ unsigned char *s;
+
+ if (session->userauth_pblc_state == libssh2_NB_state_idle) {
+
+ /*
+ * The call to _libssh2_ntohu32 later relies on pubkeydata having at
+ * least 4 valid bytes containing the length of the method name.
+ */
+ if (pubkeydata_len < 4)
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key, too short");
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_pblc_packet_requirev_state, 0,
+ sizeof(session->userauth_pblc_packet_requirev_state));
+
+ /*
+ * As an optimisation, userauth_publickey_fromfile reuses a
+ * previously allocated copy of the method name to avoid an extra
+ * allocation/free.
+ * For other uses, we allocate and populate it here.
+ */
+ if (!session->userauth_pblc_method) {
+ session->userauth_pblc_method_len = _libssh2_ntohu32(pubkeydata);
+
+ if(session->userauth_pblc_method_len > pubkeydata_len)
+ /* the method length simply cannot be longer than the entire
+ passed in data, so we use this to detect crazy input
+ data */
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key");
+
+ session->userauth_pblc_method =
+ LIBSSH2_ALLOC(session, session->userauth_pblc_method_len);
+ if (!session->userauth_pblc_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key "
+ "data");
+ }
+ memcpy(session->userauth_pblc_method, pubkeydata + 4,
+ session->userauth_pblc_method_len);
+ }
+ /*
+ * The length of the method name read from plaintext prefix in the
+ * file must match length embedded in the key.
+ * TODO: The data should match too but we don't check that. Should we?
+ */
+ else if (session->userauth_pblc_method_len !=
+ _libssh2_ntohu32(pubkeydata))
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key");
+
+ /*
+ * 45 = packet_type(1) + username_len(4) + servicename_len(4) +
+ * service_name(14)"ssh-connection" + authmethod_len(4) +
+ * authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) +
+ * publickey_len(4)
+ */
+ session->userauth_pblc_packet_len =
+ username_len + session->userauth_pblc_method_len + pubkeydata_len +
+ 45;
+
+ /*
+ * Preallocate space for an overall length, method name again, and the
+ * signature, which won't be any larger than the size of the
+ * publickeydata itself.
+ *
+ * Note that the 'pubkeydata_len' extra bytes allocated here will not
+ * be used in this first send, but will be used in the later one where
+ * this same allocation is re-used.
+ */
+ s = session->userauth_pblc_packet =
+ LIBSSH2_ALLOC(session,
+ session->userauth_pblc_packet_len + 4 +
+ (4 + session->userauth_pblc_method_len)
+ + (4 + pubkeydata_len));
+ if (!session->userauth_pblc_packet) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory");
+ }
+
+ *s++ = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", 14);
+ _libssh2_store_str(&s, "publickey", 9);
+
+ session->userauth_pblc_b = s;
+ /* Not sending signature with *this* packet */
+ *s++ = 0;
+
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+ _libssh2_store_str(&s, (const char *)pubkeydata, pubkeydata_len);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication");
+
+ session->userauth_pblc_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-publickey request");
+ }
+
+ session->userauth_pblc_state = libssh2_NB_state_sent;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pblc_data,
+ &session->userauth_pblc_data_len, 0,
+ NULL, 0,
+ &session->
+ userauth_pblc_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ }
+ else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Waiting for USERAUTH response");
+ }
+
+ if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Pubkey authentication prematurely successful");
+ /*
+ * God help any SSH server that allows an UNVERIFIED
+ * public key to validate the user
+ */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Username/PublicKey combination invalid");
+ }
+
+ /* Semi-Success! */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+
+ *session->userauth_pblc_b = 0x01;
+ session->userauth_pblc_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_sent1) {
+ unsigned char *buf;
+ unsigned char *sig;
+ size_t sig_len;
+
+ s = buf = LIBSSH2_ALLOC(session, 4 + session->session_id_len
+ + session->userauth_pblc_packet_len);
+ if (!buf) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "userauth-publickey signed data");
+ }
+
+ _libssh2_store_str(&s, (const char *)session->session_id,
+ session->session_id_len);
+
+ memcpy (s, session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len);
+ s += session->userauth_pblc_packet_len;
+
+ rc = sign_callback(session, &sig, &sig_len, buf, s - buf, abstract);
+ LIBSSH2_FREE(session, buf);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Callback returned error");
+ }
+
+ /*
+ * If this function was restarted, pubkeydata_len might still be 0
+ * which will cause an unnecessary but harmless realloc here.
+ */
+ if (sig_len > pubkeydata_len) {
+ unsigned char *newpacket;
+ /* Should *NEVER* happen, but...well.. better safe than sorry */
+ newpacket = LIBSSH2_REALLOC(session,
+ session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len + 4 +
+ (4 + session->userauth_pblc_method_len)
+ + (4 + sig_len)); /* PK sigblob */
+ if (!newpacket) {
+ LIBSSH2_FREE(session, sig);
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating additional space for "
+ "userauth-publickey packet");
+ }
+ session->userauth_pblc_packet = newpacket;
+ }
+
+ s = session->userauth_pblc_packet + session->userauth_pblc_packet_len;
+ session->userauth_pblc_b = NULL;
+
+ _libssh2_store_u32(&s,
+ 4 + session->userauth_pblc_method_len + 4 + sig_len);
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+
+ _libssh2_store_str(&s, (const char *)sig, sig_len);
+ LIBSSH2_FREE(session, sig);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication -- phase 2");
+
+ session->userauth_pblc_s = s;
+ session->userauth_pblc_state = libssh2_NB_state_sent2;
+ }
+
+ if (session->userauth_pblc_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session, session->userauth_pblc_packet,
+ session->userauth_pblc_s -
+ session->userauth_pblc_packet,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-publickey request");
+ }
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+
+ session->userauth_pblc_state = libssh2_NB_state_sent3;
+ }
+
+ /* PK_OK is no longer valid */
+ reply_codes[2] = 0;
+
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pblc_data,
+ &session->userauth_pblc_data_len, 0, NULL, 0,
+ &session->userauth_pblc_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ } else if (rc) {
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Waiting for publickey USERAUTH response");
+ }
+
+ if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Publickey authentication successful");
+ /* We are us and we've proved it. */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid signature for supplied public key, or bad "
+ "username/public key combination");
+}
+
+ /*
+ * userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
+static int
+userauth_publickey_frommemory(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const char *publickeydata,
+ size_t publickeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+ struct privkey_file privkey_file;
+ void *abstract = &privkey_file;
+ int rc;
+
+ privkey_file.filename = privatekeydata;
+ privkey_file.passphrase = passphrase;
+
+ if (session->userauth_pblc_state == libssh2_NB_state_idle) {
+ if (publickeydata_len && publickeydata) {
+ rc = memory_read_publickey(session, &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ publickeydata, publickeydata_len);
+ if(rc)
+ return rc;
+ }
+ else if (privatekeydata_len && privatekeydata) {
+ /* Compute public key from private key. */
+ if (_libssh2_pub_priv_keyfilememory(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekeydata, privatekeydata_len,
+ passphrase))
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key.");
+ }
+ else {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public and private key.");
+ }
+ }
+
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ sign_frommemory, &abstract);
+ if(pubkeydata)
+ LIBSSH2_FREE(session, pubkeydata);
+
+ return rc;
+}
+
+/*
+ * userauth_publickey_fromfile
+ * Authenticate using a keypair found in the named files
+ */
+static int
+userauth_publickey_fromfile(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+ struct privkey_file privkey_file;
+ void *abstract = &privkey_file;
+ int rc;
+
+ privkey_file.filename = privatekey;
+ privkey_file.passphrase = passphrase;
+
+ if (session->userauth_pblc_state == libssh2_NB_state_idle) {
+ if (publickey) {
+ rc = file_read_publickey(session, &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,publickey);
+ if (rc)
+ return rc;
+ }
+ else {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfile(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekey, passphrase);
+
+ /* _libssh2_pub_priv_keyfile calls _libssh2_error() */
+ if (rc)
+ return rc;
+ }
+ }
+
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ sign_fromfile, &abstract);
+ if(pubkeydata)
+ LIBSSH2_FREE(session, pubkeydata);
+
+ return rc;
+}
+
+/* libssh2_userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session,
+ const char *user,
+ size_t user_len,
+ const char *publickeyfiledata,
+ size_t publickeyfiledata_len,
+ const char *privatekeyfiledata,
+ size_t privatekeyfiledata_len,
+ const char *passphrase)
+{
+ int rc;
+
+ if(NULL == passphrase)
+ /* if given a NULL pointer, make it point to a zero-length
+ string to save us from having to check this all over */
+ passphrase="";
+
+ BLOCK_ADJUST(rc, session,
+ userauth_publickey_frommemory(session, user, user_len,
+ publickeyfiledata,
+ publickeyfiledata_len,
+ privatekeyfiledata,
+ privatekeyfiledata_len,
+ passphrase));
+ return rc;
+}
+
+/* libssh2_userauth_publickey_fromfile_ex
+ * Authenticate using a keypair found in the named files
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ int rc;
+
+ if(NULL == passphrase)
+ /* if given a NULL pointer, make it point to a zero-length
+ string to save us from having to check this all over */
+ passphrase="";
+
+ BLOCK_ADJUST(rc, session,
+ userauth_publickey_fromfile(session, user, user_len,
+ publickey, privatekey,
+ passphrase));
+ return rc;
+}
+
+/* libssh2_userauth_publickey_ex
+ * Authenticate using an external callback function
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *user,
+ const unsigned char *pubkeydata,
+ size_t pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
+ void **abstract)
+{
+ int rc;
+
+ if(!session)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, session,
+ _libssh2_userauth_publickey(session, user, strlen(user),
+ pubkeydata, pubkeydata_len,
+ sign_callback, abstract));
+ return rc;
+}
+
+
+
+/*
+ * userauth_keyboard_interactive
+ *
+ * Authenticate using a challenge-response authentication
+ */
+static int
+userauth_keyboard_interactive(LIBSSH2_SESSION * session,
+ const char *username,
+ unsigned int username_len,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))
+{
+ unsigned char *s;
+ int rc;
+
+ static const unsigned char reply_codes[4] = {
+ SSH_MSG_USERAUTH_SUCCESS,
+ SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0
+ };
+ unsigned int language_tag_len;
+ unsigned int i;
+
+ if (session->userauth_kybd_state == libssh2_NB_state_idle) {
+ session->userauth_kybd_auth_name = NULL;
+ session->userauth_kybd_auth_instruction = NULL;
+ session->userauth_kybd_num_prompts = 0;
+ session->userauth_kybd_auth_failure = 1;
+ session->userauth_kybd_prompts = NULL;
+ session->userauth_kybd_responses = NULL;
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_kybd_packet_requirev_state, 0,
+ sizeof(session->userauth_kybd_packet_requirev_state));
+
+ session->userauth_kybd_packet_len =
+ 1 /* byte SSH_MSG_USERAUTH_REQUEST */
+ + 4 + username_len /* string user name (ISO-10646 UTF-8, as
+ defined in [RFC-3629]) */
+ + 4 + 14 /* string service name (US-ASCII) */
+ + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */
+ + 4 + 0 /* string language tag (as defined in
+ [RFC-3066]) */
+ + 4 + 0 /* string submethods (ISO-10646 UTF-8) */
+ ;
+
+ session->userauth_kybd_data = s =
+ LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len);
+ if (!s) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive authentication");
+ }
+
+ *s++ = SSH_MSG_USERAUTH_REQUEST;
+
+ /* user name */
+ _libssh2_store_str(&s, username, username_len);
+
+ /* service name */
+ _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1);
+
+ /* "keyboard-interactive" */
+ _libssh2_store_str(&s, "keyboard-interactive",
+ sizeof("keyboard-interactive") - 1);
+ /* language tag */
+ _libssh2_store_u32(&s, 0);
+
+ /* submethods */
+ _libssh2_store_u32(&s, 0);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting keyboard-interactive authentication");
+
+ session->userauth_kybd_state = libssh2_NB_state_created;
+ }
+
+ if (session->userauth_kybd_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_kybd_data,
+ session->userauth_kybd_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ } else if (rc) {
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send keyboard-interactive request");
+ }
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+
+ session->userauth_kybd_state = libssh2_NB_state_sent;
+ }
+
+ for(;;) {
+ if (session->userauth_kybd_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_kybd_data,
+ &session->userauth_kybd_data_len,
+ 0, NULL, 0,
+ &session->
+ userauth_kybd_packet_requirev_state);
+ if (rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ } else if (rc) {
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Waiting for keyboard USERAUTH response");
+ }
+
+ if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive authentication successful");
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive authentication failed");
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed "
+ "(keyboard-interactive)");
+ }
+
+ /* server requested PAM-like conversation */
+ s = session->userauth_kybd_data + 1;
+
+ /* string name (ISO-10646 UTF-8) */
+ session->userauth_kybd_auth_name_len = _libssh2_ntohu32(s);
+ s += 4;
+ if(session->userauth_kybd_auth_name_len) {
+ session->userauth_kybd_auth_name =
+ LIBSSH2_ALLOC(session,
+ session->userauth_kybd_auth_name_len);
+ if (!session->userauth_kybd_auth_name) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive 'name' "
+ "request field");
+ goto cleanup;
+ }
+ memcpy(session->userauth_kybd_auth_name, s,
+ session->userauth_kybd_auth_name_len);
+ s += session->userauth_kybd_auth_name_len;
+ }
+
+ /* string instruction (ISO-10646 UTF-8) */
+ session->userauth_kybd_auth_instruction_len = _libssh2_ntohu32(s);
+ s += 4;
+ if(session->userauth_kybd_auth_instruction_len) {
+ session->userauth_kybd_auth_instruction =
+ LIBSSH2_ALLOC(session,
+ session->userauth_kybd_auth_instruction_len);
+ if (!session->userauth_kybd_auth_instruction) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive 'instruction' "
+ "request field");
+ goto cleanup;
+ }
+ memcpy(session->userauth_kybd_auth_instruction, s,
+ session->userauth_kybd_auth_instruction_len);
+ s += session->userauth_kybd_auth_instruction_len;
+ }
+
+ /* string language tag (as defined in [RFC-3066]) */
+ language_tag_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* ignoring this field as deprecated */
+ s += language_tag_len;
+
+ /* int num-prompts */
+ session->userauth_kybd_num_prompts = _libssh2_ntohu32(s);
+ s += 4;
+
+ if(session->userauth_kybd_num_prompts) {
+ session->userauth_kybd_prompts =
+ LIBSSH2_CALLOC(session,
+ sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) *
+ session->userauth_kybd_num_prompts);
+ if (!session->userauth_kybd_prompts) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive prompts array");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_responses =
+ LIBSSH2_CALLOC(session,
+ sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) *
+ session->userauth_kybd_num_prompts);
+ if (!session->userauth_kybd_responses) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive responses array");
+ goto cleanup;
+ }
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ /* string prompt[1] (ISO-10646 UTF-8) */
+ session->userauth_kybd_prompts[i].length =
+ _libssh2_ntohu32(s);
+ s += 4;
+ session->userauth_kybd_prompts[i].text =
+ LIBSSH2_CALLOC(session,
+ session->userauth_kybd_prompts[i].length);
+ if (!session->userauth_kybd_prompts[i].text) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive prompt message");
+ goto cleanup;
+ }
+ memcpy(session->userauth_kybd_prompts[i].text, s,
+ session->userauth_kybd_prompts[i].length);
+ s += session->userauth_kybd_prompts[i].length;
+
+ /* boolean echo[1] */
+ session->userauth_kybd_prompts[i].echo = *s++;
+ }
+ }
+
+ response_callback(session->userauth_kybd_auth_name,
+ session->userauth_kybd_auth_name_len,
+ session->userauth_kybd_auth_instruction,
+ session->userauth_kybd_auth_instruction_len,
+ session->userauth_kybd_num_prompts,
+ session->userauth_kybd_prompts,
+ session->userauth_kybd_responses,
+ &session->abstract);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive response callback function"
+ " invoked");
+
+ session->userauth_kybd_packet_len =
+ 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */
+ + 4 /* int num-responses */
+ ;
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ /* string response[1] (ISO-10646 UTF-8) */
+ session->userauth_kybd_packet_len +=
+ 4 + session->userauth_kybd_responses[i].length;
+ }
+
+ /* A new userauth_kybd_data area is to be allocated, free the
+ former one. */
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+
+ session->userauth_kybd_data = s =
+ LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len);
+ if (!s) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for keyboard-"
+ "interactive response packet");
+ goto cleanup;
+ }
+
+ *s = SSH_MSG_USERAUTH_INFO_RESPONSE;
+ s++;
+ _libssh2_store_u32(&s, session->userauth_kybd_num_prompts);
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ _libssh2_store_str(&s,
+ session->userauth_kybd_responses[i].text,
+ session->userauth_kybd_responses[i].length);
+ }
+
+ session->userauth_kybd_state = libssh2_NB_state_sent1;
+ }
+
+ if (session->userauth_kybd_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_transport_send(session, session->userauth_kybd_data,
+ session->userauth_kybd_packet_len,
+ NULL, 0);
+ if (rc == LIBSSH2_ERROR_EAGAIN)
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ if (rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-keyboard-interactive"
+ " request");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_auth_failure = 0;
+ }
+
+ cleanup:
+ /*
+ * It's safe to clean all the data here, because unallocated pointers
+ * are filled by zeroes
+ */
+
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+
+ if (session->userauth_kybd_prompts) {
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ LIBSSH2_FREE(session, session->userauth_kybd_prompts[i].text);
+ session->userauth_kybd_prompts[i].text = NULL;
+ }
+ }
+
+ if (session->userauth_kybd_responses) {
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ LIBSSH2_FREE(session,
+ session->userauth_kybd_responses[i].text);
+ session->userauth_kybd_responses[i].text = NULL;
+ }
+ }
+
+ if(session->userauth_kybd_prompts) {
+ LIBSSH2_FREE(session, session->userauth_kybd_prompts);
+ session->userauth_kybd_prompts = NULL;
+ }
+ if(session->userauth_kybd_responses) {
+ LIBSSH2_FREE(session, session->userauth_kybd_responses);
+ session->userauth_kybd_responses = NULL;
+ }
+ if(session->userauth_kybd_auth_name) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_name);
+ session->userauth_kybd_auth_name = NULL;
+ }
+ if(session->userauth_kybd_auth_instruction) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction);
+ session->userauth_kybd_auth_instruction = NULL;
+ }
+
+ if (session->userauth_kybd_auth_failure) {
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return -1;
+ }
+
+ session->userauth_kybd_state = libssh2_NB_state_sent;
+ }
+}
+
+/*
+ * libssh2_userauth_keyboard_interactive_ex
+ *
+ * Authenticate using a challenge-response authentication
+ */
+LIBSSH2_API int
+libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_keyboard_interactive(session, user, user_len,
+ response_callback));
+ return rc;
+}
diff --git a/libs/libssh2/src/userauth.h b/libs/libssh2/src/userauth.h
new file mode 100644
index 0000000000..c0442ae158
--- /dev/null
+++ b/libs/libssh2/src/userauth.h
@@ -0,0 +1,50 @@
+#ifndef LIBSSH2_USERAUTH_H
+#define LIBSSH2_USERAUTH_H
+/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
+ * Copyright (c) 2009-2010 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+int
+_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *username,
+ unsigned int username_len,
+ const unsigned char *pubkeydata,
+ unsigned long pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
+ void *abstract);
+
+#endif /* LIBSSH2_USERAUTH_H */
diff --git a/libs/libssh2/src/version.c b/libs/libssh2/src/version.c
new file mode 100644
index 0000000000..408f83a398
--- /dev/null
+++ b/libs/libssh2/src/version.c
@@ -0,0 +1,54 @@
+/* Copyright (C) 2009 Daniel Stenberg. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+#include "libssh2_priv.h"
+
+/*
+ libssh2_version() can be used like this:
+
+ if (!libssh2_version(LIBSSH2_VERSION_NUM)) {
+ fprintf (stderr, "Runtime libssh2 version too old!\n");
+ exit(1);
+ }
+*/
+LIBSSH2_API
+const char *libssh2_version(int req_version_num)
+{
+ if(req_version_num <= LIBSSH2_VERSION_NUM)
+ return LIBSSH2_VERSION;
+ return NULL; /* this is not a suitable library! */
+}
diff --git a/libs/libssh2/src/wincng.c b/libs/libssh2/src/wincng.c
new file mode 100644
index 0000000000..d3271b3e33
--- /dev/null
+++ b/libs/libssh2/src/wincng.c
@@ -0,0 +1,2076 @@
+/*
+ * Copyright (C) 2013-2015 Marc Hoersken <info@marc-hoersken.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+#ifdef LIBSSH2_WINCNG /* compile only if we build with wincng */
+
+/* required for cross-compilation against the w64 mingw-runtime package */
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
+#undef _WIN32_WINNT
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+/* specify the required libraries for dependencies using MSVC */
+#ifdef _MSC_VER
+#pragma comment(lib, "bcrypt.lib")
+#ifdef HAVE_LIBCRYPT32
+#pragma comment(lib, "crypt32.lib")
+#endif
+#endif
+
+#include <windows.h>
+#include <bcrypt.h>
+#include <math.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_LIBCRYPT32
+#include <wincrypt.h>
+#endif
+
+#define PEM_RSA_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_RSA_FOOTER "-----END RSA PRIVATE KEY-----"
+#define PEM_DSA_HEADER "-----BEGIN DSA PRIVATE KEY-----"
+#define PEM_DSA_FOOTER "-----END DSA PRIVATE KEY-----"
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Missing definitions (for MinGW[-w64])
+ */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+#ifndef BCRYPT_RNG_ALGORITHM
+#define BCRYPT_RNG_ALGORITHM L"RNG"
+#endif
+
+#ifndef BCRYPT_MD5_ALGORITHM
+#define BCRYPT_MD5_ALGORITHM L"MD5"
+#endif
+
+#ifndef BCRYPT_SHA1_ALGORITHM
+#define BCRYPT_SHA1_ALGORITHM L"SHA1"
+#endif
+
+#ifndef BCRYPT_SHA256_ALGORITHM
+#define BCRYPT_SHA256_ALGORITHM L"SHA256"
+#endif
+
+#ifndef BCRYPT_SHA512_ALGORITHM
+#define BCRYPT_SHA512_ALGORITHM L"SHA512"
+#endif
+
+#ifndef BCRYPT_RSA_ALGORITHM
+#define BCRYPT_RSA_ALGORITHM L"RSA"
+#endif
+
+#ifndef BCRYPT_DSA_ALGORITHM
+#define BCRYPT_DSA_ALGORITHM L"DSA"
+#endif
+
+#ifndef BCRYPT_AES_ALGORITHM
+#define BCRYPT_AES_ALGORITHM L"AES"
+#endif
+
+#ifndef BCRYPT_RC4_ALGORITHM
+#define BCRYPT_RC4_ALGORITHM L"RC4"
+#endif
+
+#ifndef BCRYPT_3DES_ALGORITHM
+#define BCRYPT_3DES_ALGORITHM L"3DES"
+#endif
+
+#ifndef BCRYPT_ALG_HANDLE_HMAC_FLAG
+#define BCRYPT_ALG_HANDLE_HMAC_FLAG 0x00000008
+#endif
+
+#ifndef BCRYPT_DSA_PUBLIC_BLOB
+#define BCRYPT_DSA_PUBLIC_BLOB L"DSAPUBLICBLOB"
+#endif
+
+#ifndef BCRYPT_DSA_PUBLIC_MAGIC
+#define BCRYPT_DSA_PUBLIC_MAGIC 0x42505344 /* DSPB */
+#endif
+
+#ifndef BCRYPT_DSA_PRIVATE_BLOB
+#define BCRYPT_DSA_PRIVATE_BLOB L"DSAPRIVATEBLOB"
+#endif
+
+#ifndef BCRYPT_DSA_PRIVATE_MAGIC
+#define BCRYPT_DSA_PRIVATE_MAGIC 0x56505344 /* DSPV */
+#endif
+
+#ifndef BCRYPT_RSAPUBLIC_BLOB
+#define BCRYPT_RSAPUBLIC_BLOB L"RSAPUBLICBLOB"
+#endif
+
+#ifndef BCRYPT_RSAPUBLIC_MAGIC
+#define BCRYPT_RSAPUBLIC_MAGIC 0x31415352 /* RSA1 */
+#endif
+
+#ifndef BCRYPT_RSAFULLPRIVATE_BLOB
+#define BCRYPT_RSAFULLPRIVATE_BLOB L"RSAFULLPRIVATEBLOB"
+#endif
+
+#ifndef BCRYPT_RSAFULLPRIVATE_MAGIC
+#define BCRYPT_RSAFULLPRIVATE_MAGIC 0x33415352 /* RSA3 */
+#endif
+
+#ifndef BCRYPT_KEY_DATA_BLOB
+#define BCRYPT_KEY_DATA_BLOB L"KeyDataBlob"
+#endif
+
+#ifndef BCRYPT_MESSAGE_BLOCK_LENGTH
+#define BCRYPT_MESSAGE_BLOCK_LENGTH L"MessageBlockLength"
+#endif
+
+#ifndef BCRYPT_NO_KEY_VALIDATION
+#define BCRYPT_NO_KEY_VALIDATION 0x00000008
+#endif
+
+#ifndef BCRYPT_BLOCK_PADDING
+#define BCRYPT_BLOCK_PADDING 0x00000001
+#endif
+
+#ifndef BCRYPT_PAD_NONE
+#define BCRYPT_PAD_NONE 0x00000001
+#endif
+
+#ifndef BCRYPT_PAD_PKCS1
+#define BCRYPT_PAD_PKCS1 0x00000002
+#endif
+
+#ifndef BCRYPT_PAD_OAEP
+#define BCRYPT_PAD_OAEP 0x00000004
+#endif
+
+#ifndef BCRYPT_PAD_PSS
+#define BCRYPT_PAD_PSS 0x00000008
+#endif
+
+#ifndef CRYPT_STRING_ANY
+#define CRYPT_STRING_ANY 0x00000007
+#endif
+
+#ifndef LEGACY_RSAPRIVATE_BLOB
+#define LEGACY_RSAPRIVATE_BLOB L"CAPIPRIVATEBLOB"
+#endif
+
+#ifndef PKCS_RSA_PRIVATE_KEY
+#define PKCS_RSA_PRIVATE_KEY (LPCSTR)43
+#endif
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Generic functions
+ */
+
+void
+_libssh2_wincng_init(void)
+{
+ int ret;
+
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRNG,
+ BCRYPT_RNG_ALGORITHM, NULL, 0);
+
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashMD5,
+ BCRYPT_MD5_ALGORITHM, NULL, 0);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA1,
+ BCRYPT_SHA1_ALGORITHM, NULL, 0);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA256,
+ BCRYPT_SHA256_ALGORITHM, NULL, 0);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA512,
+ BCRYPT_SHA512_ALGORITHM, NULL, 0);
+
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacMD5,
+ BCRYPT_MD5_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA1,
+ BCRYPT_SHA1_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA256,
+ BCRYPT_SHA256_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA512,
+ BCRYPT_SHA512_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRSA,
+ BCRYPT_RSA_ALGORITHM, NULL, 0);
+ (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDSA,
+ BCRYPT_DSA_ALGORITHM, NULL, 0);
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgAES_CBC,
+ BCRYPT_AES_ALGORITHM, NULL, 0);
+ if (BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlgAES_CBC, BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_CBC,
+ sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0);
+ }
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRC4_NA,
+ BCRYPT_RC4_ALGORITHM, NULL, 0);
+ if (BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlgRC4_NA, BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_NA,
+ sizeof(BCRYPT_CHAIN_MODE_NA), 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0);
+ }
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlg3DES_CBC,
+ BCRYPT_3DES_ALGORITHM, NULL, 0);
+ if (BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlg3DES_CBC, BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_CBC,
+ sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0);
+ }
+ }
+}
+
+void
+_libssh2_wincng_free(void)
+{
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRNG, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashMD5, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA1, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA256, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA512, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacMD5, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA1, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA256, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA512, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRSA, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDSA, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0);
+
+ memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng));
+}
+
+int
+_libssh2_wincng_random(void *buf, int len)
+{
+ int ret;
+
+ ret = BCryptGenRandom(_libssh2_wincng.hAlgRNG, buf, len, 0);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+static void
+_libssh2_wincng_safe_free(void *buf, int len)
+{
+#ifndef LIBSSH2_CLEAR_MEMORY
+ (void)len;
+#endif
+
+ if (!buf)
+ return;
+
+#ifdef LIBSSH2_CLEAR_MEMORY
+ if (len > 0)
+ SecureZeroMemory(buf, len);
+#endif
+
+ free(buf);
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Hash functions
+ */
+
+int
+_libssh2_wincng_hash_init(_libssh2_wincng_hash_ctx *ctx,
+ BCRYPT_ALG_HANDLE hAlg, unsigned long hashlen,
+ unsigned char *key, unsigned long keylen)
+{
+ BCRYPT_HASH_HANDLE hHash;
+ unsigned char *pbHashObject;
+ unsigned long dwHashObject, dwHash, cbData;
+ int ret;
+
+ ret = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH,
+ (unsigned char *)&dwHash,
+ sizeof(dwHash),
+ &cbData, 0);
+ if ((!BCRYPT_SUCCESS(ret)) || dwHash != hashlen) {
+ return -1;
+ }
+
+ ret = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH,
+ (unsigned char *)&dwHashObject,
+ sizeof(dwHashObject),
+ &cbData, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ return -1;
+ }
+
+ pbHashObject = malloc(dwHashObject);
+ if (!pbHashObject) {
+ return -1;
+ }
+
+
+ ret = BCryptCreateHash(hAlg, &hHash,
+ pbHashObject, dwHashObject,
+ key, keylen, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(pbHashObject, dwHashObject);
+ return -1;
+ }
+
+
+ ctx->hHash = hHash;
+ ctx->pbHashObject = pbHashObject;
+ ctx->dwHashObject = dwHashObject;
+ ctx->cbHash = dwHash;
+
+ return 0;
+}
+
+int
+_libssh2_wincng_hash_update(_libssh2_wincng_hash_ctx *ctx,
+ const unsigned char *data, unsigned long datalen)
+{
+ int ret;
+
+ ret = BCryptHashData(ctx->hHash, (unsigned char *)data, datalen, 0);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_hash_final(_libssh2_wincng_hash_ctx *ctx,
+ unsigned char *hash)
+{
+ int ret;
+
+ ret = BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0);
+
+ BCryptDestroyHash(ctx->hHash);
+ ctx->hHash = NULL;
+
+ _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject);
+ ctx->pbHashObject = NULL;
+ ctx->dwHashObject = 0;
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_hash(unsigned char *data, unsigned long datalen,
+ BCRYPT_ALG_HANDLE hAlg,
+ unsigned char *hash, unsigned long hashlen)
+{
+ _libssh2_wincng_hash_ctx ctx;
+ int ret;
+
+ ret = _libssh2_wincng_hash_init(&ctx, hAlg, hashlen, NULL, 0);
+ if (!ret) {
+ ret = _libssh2_wincng_hash_update(&ctx, data, datalen);
+ ret |= _libssh2_wincng_hash_final(&ctx, hash);
+ }
+
+ return ret;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: HMAC functions
+ */
+
+int
+_libssh2_wincng_hmac_final(_libssh2_wincng_hash_ctx *ctx,
+ unsigned char *hash)
+{
+ int ret;
+
+ ret = BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void
+_libssh2_wincng_hmac_cleanup(_libssh2_wincng_hash_ctx *ctx)
+{
+ BCryptDestroyHash(ctx->hHash);
+ ctx->hHash = NULL;
+
+ _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject);
+ ctx->pbHashObject = NULL;
+ ctx->dwHashObject = 0;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Key functions
+ */
+
+int
+_libssh2_wincng_key_sha1_verify(_libssh2_wincng_key_ctx *ctx,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m,
+ unsigned long m_len,
+ unsigned long flags)
+{
+ BCRYPT_PKCS1_PADDING_INFO paddingInfoPKCS1;
+ void *pPaddingInfo;
+ unsigned char *data, *hash;
+ unsigned long datalen, hashlen;
+ int ret;
+
+ datalen = m_len;
+ data = malloc(datalen);
+ if (!data) {
+ return -1;
+ }
+
+ hashlen = SHA_DIGEST_LENGTH;
+ hash = malloc(hashlen);
+ if (!hash) {
+ free(data);
+ return -1;
+ }
+
+ memcpy(data, m, datalen);
+
+ ret = _libssh2_wincng_hash(data, datalen,
+ _libssh2_wincng.hAlgHashSHA1,
+ hash, hashlen);
+
+ _libssh2_wincng_safe_free(data, datalen);
+
+ if (ret) {
+ _libssh2_wincng_safe_free(hash, hashlen);
+ return -1;
+ }
+
+ datalen = sig_len;
+ data = malloc(datalen);
+ if (!data) {
+ _libssh2_wincng_safe_free(hash, hashlen);
+ return -1;
+ }
+
+ if (flags & BCRYPT_PAD_PKCS1) {
+ paddingInfoPKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM;
+ pPaddingInfo = &paddingInfoPKCS1;
+ } else
+ pPaddingInfo = NULL;
+
+ memcpy(data, sig, datalen);
+
+ ret = BCryptVerifySignature(ctx->hKey, pPaddingInfo,
+ hash, hashlen, data, datalen, flags);
+
+ _libssh2_wincng_safe_free(hash, hashlen);
+ _libssh2_wincng_safe_free(data, datalen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+#ifdef HAVE_LIBCRYPT32
+static int
+_libssh2_wincng_load_pem(LIBSSH2_SESSION *session,
+ const char *filename,
+ const char *passphrase,
+ const char *headerbegin,
+ const char *headerend,
+ unsigned char **data,
+ unsigned int *datalen)
+{
+ FILE *fp;
+ int ret;
+
+ (void)passphrase;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ ret = _libssh2_pem_parse(session, headerbegin, headerend,
+ fp, data, datalen);
+
+ fclose(fp);
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_load_private(LIBSSH2_SESSION *session,
+ const char *filename,
+ const char *passphrase,
+ unsigned char **ppbEncoded,
+ unsigned long *pcbEncoded,
+ int tryLoadRSA, int tryLoadDSA)
+{
+ unsigned char *data = NULL;
+ unsigned int datalen = 0;
+ int ret = -1;
+
+ if (ret && tryLoadRSA) {
+ ret = _libssh2_wincng_load_pem(session, filename, passphrase,
+ PEM_RSA_HEADER, PEM_RSA_FOOTER,
+ &data, &datalen);
+ }
+
+ if (ret && tryLoadDSA) {
+ ret = _libssh2_wincng_load_pem(session, filename, passphrase,
+ PEM_DSA_HEADER, PEM_DSA_FOOTER,
+ &data, &datalen);
+ }
+
+ if (!ret) {
+ *ppbEncoded = data;
+ *pcbEncoded = datalen;
+ }
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_load_private_memory(LIBSSH2_SESSION *session,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase,
+ unsigned char **ppbEncoded,
+ unsigned long *pcbEncoded,
+ int tryLoadRSA, int tryLoadDSA)
+{
+ unsigned char *data = NULL;
+ unsigned int datalen = 0;
+ int ret = -1;
+
+ (void)passphrase;
+
+ if (ret && tryLoadRSA) {
+ ret = _libssh2_pem_parse_memory(session,
+ PEM_RSA_HEADER, PEM_RSA_FOOTER,
+ privatekeydata, privatekeydata_len,
+ &data, &datalen);
+ }
+
+ if (ret && tryLoadDSA) {
+ ret = _libssh2_pem_parse_memory(session,
+ PEM_DSA_HEADER, PEM_DSA_FOOTER,
+ privatekeydata, privatekeydata_len,
+ &data, &datalen);
+ }
+
+ if (!ret) {
+ *ppbEncoded = data;
+ *pcbEncoded = datalen;
+ }
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_asn_decode(unsigned char *pbEncoded,
+ unsigned long cbEncoded,
+ LPCSTR lpszStructType,
+ unsigned char **ppbDecoded,
+ unsigned long *pcbDecoded)
+{
+ unsigned char *pbDecoded = NULL;
+ unsigned long cbDecoded = 0;
+ int ret;
+
+ ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ lpszStructType,
+ pbEncoded, cbEncoded, 0, NULL,
+ NULL, &cbDecoded);
+ if (!ret) {
+ return -1;
+ }
+
+ pbDecoded = malloc(cbDecoded);
+ if (!pbDecoded) {
+ return -1;
+ }
+
+ ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ lpszStructType,
+ pbEncoded, cbEncoded, 0, NULL,
+ pbDecoded, &cbDecoded);
+ if (!ret) {
+ _libssh2_wincng_safe_free(pbDecoded, cbDecoded);
+ return -1;
+ }
+
+
+ *ppbDecoded = pbDecoded;
+ *pcbDecoded = cbDecoded;
+
+ return 0;
+}
+
+static int
+_libssh2_wincng_bn_ltob(unsigned char *pbInput,
+ unsigned long cbInput,
+ unsigned char **ppbOutput,
+ unsigned long *pcbOutput)
+{
+ unsigned char *pbOutput;
+ unsigned long cbOutput, index, offset, length;
+
+ if (cbInput < 1) {
+ return 0;
+ }
+
+ offset = 0;
+ length = cbInput - 1;
+ cbOutput = cbInput;
+ if (pbInput[length] & (1 << 7)) {
+ offset++;
+ cbOutput += offset;
+ }
+
+ pbOutput = (unsigned char *)malloc(cbOutput);
+ if (!pbOutput) {
+ return -1;
+ }
+
+ pbOutput[0] = 0;
+ for (index = 0; ((index + offset) < cbOutput)
+ && (index < cbInput); index++) {
+ pbOutput[index + offset] = pbInput[length - index];
+ }
+
+
+ *ppbOutput = pbOutput;
+ *pcbOutput = cbOutput;
+
+ return 0;
+}
+
+static int
+_libssh2_wincng_asn_decode_bn(unsigned char *pbEncoded,
+ unsigned long cbEncoded,
+ unsigned char **ppbDecoded,
+ unsigned long *pcbDecoded)
+{
+ unsigned char *pbDecoded = NULL, *pbInteger;
+ unsigned long cbDecoded = 0, cbInteger;
+ int ret;
+
+ ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded,
+ X509_MULTI_BYTE_UINT,
+ &pbInteger, &cbInteger);
+ if (!ret) {
+ ret = _libssh2_wincng_bn_ltob(((PCRYPT_DATA_BLOB)pbInteger)->pbData,
+ ((PCRYPT_DATA_BLOB)pbInteger)->cbData,
+ &pbDecoded, &cbDecoded);
+ if (!ret) {
+ *ppbDecoded = pbDecoded;
+ *pcbDecoded = cbDecoded;
+ }
+ _libssh2_wincng_safe_free(pbInteger, cbInteger);
+ }
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_asn_decode_bns(unsigned char *pbEncoded,
+ unsigned long cbEncoded,
+ unsigned char ***prpbDecoded,
+ unsigned long **prcbDecoded,
+ unsigned long *pcbCount)
+{
+ PCRYPT_DER_BLOB pBlob;
+ unsigned char *pbDecoded, **rpbDecoded;
+ unsigned long cbDecoded, *rcbDecoded, index, length;
+ int ret;
+
+ ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded,
+ X509_SEQUENCE_OF_ANY,
+ &pbDecoded, &cbDecoded);
+ if (!ret) {
+ length = ((PCRYPT_DATA_BLOB)pbDecoded)->cbData;
+
+ rpbDecoded = malloc(sizeof(PBYTE) * length);
+ if (rpbDecoded) {
+ rcbDecoded = malloc(sizeof(DWORD) * length);
+ if (rcbDecoded) {
+ for (index = 0; index < length; index++) {
+ pBlob = &((PCRYPT_DER_BLOB)
+ ((PCRYPT_DATA_BLOB)pbDecoded)->pbData)[index];
+ ret = _libssh2_wincng_asn_decode_bn(pBlob->pbData,
+ pBlob->cbData,
+ &rpbDecoded[index],
+ &rcbDecoded[index]);
+ if (ret)
+ break;
+ }
+
+ if (!ret) {
+ *prpbDecoded = rpbDecoded;
+ *prcbDecoded = rcbDecoded;
+ *pcbCount = length;
+ } else {
+ for (length = 0; length < index; length++) {
+ _libssh2_wincng_safe_free(rpbDecoded[length],
+ rcbDecoded[length]);
+ rpbDecoded[length] = NULL;
+ rcbDecoded[length] = 0;
+ }
+ free(rpbDecoded);
+ free(rcbDecoded);
+ }
+ } else {
+ free(rpbDecoded);
+ ret = -1;
+ }
+ } else {
+ ret = -1;
+ }
+
+ _libssh2_wincng_safe_free(pbDecoded, cbDecoded);
+ }
+
+ return ret;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+static unsigned long
+_libssh2_wincng_bn_size(const unsigned char *bignum,
+ unsigned long length)
+{
+ unsigned long offset;
+
+ if (!bignum)
+ return 0;
+
+ length--;
+
+ offset = 0;
+ while (!(*(bignum + offset)) && (offset < length))
+ offset++;
+
+ length++;
+
+ return length - offset;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: RSA functions
+ */
+
+int
+_libssh2_wincng_rsa_new(libssh2_rsa_ctx **rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata,
+ unsigned long coefflen)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_RSAKEY_BLOB *rsakey;
+ LPCWSTR lpszBlobType;
+ unsigned char *key;
+ unsigned long keylen, offset, mlen, p1len = 0, p2len = 0;
+ int ret;
+
+ mlen = max(_libssh2_wincng_bn_size(ndata, nlen),
+ _libssh2_wincng_bn_size(ddata, dlen));
+ offset = sizeof(BCRYPT_RSAKEY_BLOB);
+ keylen = offset + elen + mlen;
+ if (ddata && dlen > 0) {
+ p1len = max(_libssh2_wincng_bn_size(pdata, plen),
+ _libssh2_wincng_bn_size(e1data, e1len));
+ p2len = max(_libssh2_wincng_bn_size(qdata, qlen),
+ _libssh2_wincng_bn_size(e2data, e2len));
+ keylen += p1len * 3 + p2len * 2 + mlen;
+ }
+
+ key = malloc(keylen);
+ if (!key) {
+ return -1;
+ }
+
+ memset(key, 0, keylen);
+
+
+ /* https://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */
+ rsakey = (BCRYPT_RSAKEY_BLOB *)key;
+ rsakey->BitLength = mlen * 8;
+ rsakey->cbPublicExp = elen;
+ rsakey->cbModulus = mlen;
+
+ memcpy(key + offset, edata, elen);
+ offset += elen;
+
+ if (nlen < mlen)
+ memcpy(key + offset + mlen - nlen, ndata, nlen);
+ else
+ memcpy(key + offset, ndata + nlen - mlen, mlen);
+
+ if (ddata && dlen > 0) {
+ offset += mlen;
+
+ if (plen < p1len)
+ memcpy(key + offset + p1len - plen, pdata, plen);
+ else
+ memcpy(key + offset, pdata + plen - p1len, p1len);
+ offset += p1len;
+
+ if (qlen < p2len)
+ memcpy(key + offset + p2len - qlen, qdata, qlen);
+ else
+ memcpy(key + offset, qdata + qlen - p2len, p2len);
+ offset += p2len;
+
+ if (e1len < p1len)
+ memcpy(key + offset + p1len - e1len, e1data, e1len);
+ else
+ memcpy(key + offset, e1data + e1len - p1len, p1len);
+ offset += p1len;
+
+ if (e2len < p2len)
+ memcpy(key + offset + p2len - e2len, e2data, e2len);
+ else
+ memcpy(key + offset, e2data + e2len - p2len, p2len);
+ offset += p2len;
+
+ if (coefflen < p1len)
+ memcpy(key + offset + p1len - coefflen, coeffdata, coefflen);
+ else
+ memcpy(key + offset, coeffdata + coefflen - p1len, p1len);
+ offset += p1len;
+
+ if (dlen < mlen)
+ memcpy(key + offset + mlen - dlen, ddata, dlen);
+ else
+ memcpy(key + offset, ddata + dlen - mlen, mlen);
+
+ lpszBlobType = BCRYPT_RSAFULLPRIVATE_BLOB;
+ rsakey->Magic = BCRYPT_RSAFULLPRIVATE_MAGIC;
+ rsakey->cbPrime1 = p1len;
+ rsakey->cbPrime2 = p2len;
+ } else {
+ lpszBlobType = BCRYPT_RSAPUBLIC_BLOB;
+ rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC;
+ rsakey->cbPrime1 = 0;
+ rsakey->cbPrime2 = 0;
+ }
+
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, lpszBlobType,
+ &hKey, key, keylen, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(key, keylen);
+ return -1;
+ }
+
+
+ *rsa = malloc(sizeof(libssh2_rsa_ctx));
+ if (!(*rsa)) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(key, keylen);
+ return -1;
+ }
+
+ (*rsa)->hKey = hKey;
+ (*rsa)->pbKeyObject = key;
+ (*rsa)->cbKeyObject = keylen;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBCRYPT32
+static int
+_libssh2_wincng_rsa_new_private_parse(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ unsigned char *pbEncoded,
+ unsigned long cbEncoded)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ unsigned char *pbStructInfo;
+ unsigned long cbStructInfo;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded,
+ PKCS_RSA_PRIVATE_KEY,
+ &pbStructInfo, &cbStructInfo);
+
+ _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
+
+ if (ret) {
+ return -1;
+ }
+
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL,
+ LEGACY_RSAPRIVATE_BLOB, &hKey,
+ pbStructInfo, cbStructInfo, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo);
+ return -1;
+ }
+
+
+ *rsa = malloc(sizeof(libssh2_rsa_ctx));
+ if (!(*rsa)) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo);
+ return -1;
+ }
+
+ (*rsa)->hKey = hKey;
+ (*rsa)->pbKeyObject = pbStructInfo;
+ (*rsa)->cbKeyObject = cbStructInfo;
+
+ return 0;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+int
+_libssh2_wincng_rsa_new_private(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ unsigned long cbEncoded;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_load_private(session, filename,
+ (const char *)passphrase,
+ &pbEncoded, &cbEncoded, 1, 0);
+ if (ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_rsa_new_private_parse(rsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)rsa;
+ (void)filename;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to load RSA key from private key file: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ unsigned long cbEncoded;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len,
+ (const char *)passphrase,
+ &pbEncoded, &cbEncoded, 1, 0);
+ if (ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_rsa_new_private_parse(rsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)rsa;
+ (void)filedata;
+ (void)filedata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract private key from memory: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_rsa_sha1_verify(libssh2_rsa_ctx *rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m,
+ unsigned long m_len)
+{
+ return _libssh2_wincng_key_sha1_verify(rsa, sig, sig_len, m, m_len,
+ BCRYPT_PAD_PKCS1);
+}
+
+int
+_libssh2_wincng_rsa_sha1_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ BCRYPT_PKCS1_PADDING_INFO paddingInfo;
+ unsigned char *data, *sig;
+ unsigned long cbData, datalen, siglen;
+ int ret;
+
+ datalen = (unsigned long)hash_len;
+ data = malloc(datalen);
+ if (!data) {
+ return -1;
+ }
+
+ paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
+
+ memcpy(data, hash, datalen);
+
+ ret = BCryptSignHash(rsa->hKey, &paddingInfo,
+ data, datalen, NULL, 0,
+ &cbData, BCRYPT_PAD_PKCS1);
+ if (BCRYPT_SUCCESS(ret)) {
+ siglen = cbData;
+ sig = LIBSSH2_ALLOC(session, siglen);
+ if (sig) {
+ ret = BCryptSignHash(rsa->hKey, &paddingInfo,
+ data, datalen, sig, siglen,
+ &cbData, BCRYPT_PAD_PKCS1);
+ if (BCRYPT_SUCCESS(ret)) {
+ *signature_len = siglen;
+ *signature = sig;
+ } else {
+ LIBSSH2_FREE(session, sig);
+ }
+ } else
+ ret = STATUS_NO_MEMORY;
+ }
+
+ _libssh2_wincng_safe_free(data, datalen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void
+_libssh2_wincng_rsa_free(libssh2_rsa_ctx *rsa)
+{
+ if (!rsa)
+ return;
+
+ BCryptDestroyKey(rsa->hKey);
+ rsa->hKey = NULL;
+
+ _libssh2_wincng_safe_free(rsa->pbKeyObject, rsa->cbKeyObject);
+ _libssh2_wincng_safe_free(rsa, sizeof(libssh2_rsa_ctx));
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: DSA functions
+ */
+
+#if LIBSSH2_DSA
+int
+_libssh2_wincng_dsa_new(libssh2_dsa_ctx **dsa,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *gdata,
+ unsigned long glen,
+ const unsigned char *ydata,
+ unsigned long ylen,
+ const unsigned char *xdata,
+ unsigned long xlen)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_DSA_KEY_BLOB *dsakey;
+ LPCWSTR lpszBlobType;
+ unsigned char *key;
+ unsigned long keylen, offset, length;
+ int ret;
+
+ length = max(max(_libssh2_wincng_bn_size(pdata, plen),
+ _libssh2_wincng_bn_size(gdata, glen)),
+ _libssh2_wincng_bn_size(ydata, ylen));
+ offset = sizeof(BCRYPT_DSA_KEY_BLOB);
+ keylen = offset + length * 3;
+ if (xdata && xlen > 0)
+ keylen += 20;
+
+ key = malloc(keylen);
+ if (!key) {
+ return -1;
+ }
+
+ memset(key, 0, keylen);
+
+
+ /* https://msdn.microsoft.com/library/windows/desktop/aa833126.aspx */
+ dsakey = (BCRYPT_DSA_KEY_BLOB *)key;
+ dsakey->cbKey = length;
+
+ memset(dsakey->Count, -1, sizeof(dsakey->Count));
+ memset(dsakey->Seed, -1, sizeof(dsakey->Seed));
+
+ if (qlen < 20)
+ memcpy(dsakey->q + 20 - qlen, qdata, qlen);
+ else
+ memcpy(dsakey->q, qdata + qlen - 20, 20);
+
+ if (plen < length)
+ memcpy(key + offset + length - plen, pdata, plen);
+ else
+ memcpy(key + offset, pdata + plen - length, length);
+ offset += length;
+
+ if (glen < length)
+ memcpy(key + offset + length - glen, gdata, glen);
+ else
+ memcpy(key + offset, gdata + glen - length, length);
+ offset += length;
+
+ if (ylen < length)
+ memcpy(key + offset + length - ylen, ydata, ylen);
+ else
+ memcpy(key + offset, ydata + ylen - length, length);
+
+ if (xdata && xlen > 0) {
+ offset += length;
+
+ if (xlen < 20)
+ memcpy(key + offset + 20 - xlen, xdata, xlen);
+ else
+ memcpy(key + offset, xdata + xlen - 20, 20);
+
+ lpszBlobType = BCRYPT_DSA_PRIVATE_BLOB;
+ dsakey->dwMagic = BCRYPT_DSA_PRIVATE_MAGIC;
+ } else {
+ lpszBlobType = BCRYPT_DSA_PUBLIC_BLOB;
+ dsakey->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC;
+ }
+
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgDSA, NULL, lpszBlobType,
+ &hKey, key, keylen, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(key, keylen);
+ return -1;
+ }
+
+
+ *dsa = malloc(sizeof(libssh2_dsa_ctx));
+ if (!(*dsa)) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(key, keylen);
+ return -1;
+ }
+
+ (*dsa)->hKey = hKey;
+ (*dsa)->pbKeyObject = key;
+ (*dsa)->cbKeyObject = keylen;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBCRYPT32
+static int
+_libssh2_wincng_dsa_new_private_parse(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ unsigned char *pbEncoded,
+ unsigned long cbEncoded)
+{
+ unsigned char **rpbDecoded;
+ unsigned long *rcbDecoded, index, length;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded,
+ &rpbDecoded, &rcbDecoded, &length);
+
+ _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
+
+ if (ret) {
+ return -1;
+ }
+
+
+ if (length == 6) {
+ ret = _libssh2_wincng_dsa_new(dsa,
+ rpbDecoded[1], rcbDecoded[1],
+ rpbDecoded[2], rcbDecoded[2],
+ rpbDecoded[3], rcbDecoded[3],
+ rpbDecoded[4], rcbDecoded[4],
+ rpbDecoded[5], rcbDecoded[5]);
+ } else {
+ ret = -1;
+ }
+
+ for (index = 0; index < length; index++) {
+ _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]);
+ rpbDecoded[index] = NULL;
+ rcbDecoded[index] = 0;
+ }
+
+ free(rpbDecoded);
+ free(rcbDecoded);
+
+ return ret;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+int
+_libssh2_wincng_dsa_new_private(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ unsigned long cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private(session, filename,
+ (const char *)passphrase,
+ &pbEncoded, &cbEncoded, 0, 1);
+ if (ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_dsa_new_private_parse(dsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)dsa;
+ (void)filename;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to load DSA key from private key file: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_dsa_new_private_frommemory(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ unsigned long cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len,
+ (const char *)passphrase,
+ &pbEncoded, &cbEncoded, 0, 1);
+ if (ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_dsa_new_private_parse(dsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)dsa;
+ (void)filedata;
+ (void)filedata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract private key from memory: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_dsa_sha1_verify(libssh2_dsa_ctx *dsa,
+ const unsigned char *sig_fixed,
+ const unsigned char *m,
+ unsigned long m_len)
+{
+ return _libssh2_wincng_key_sha1_verify(dsa, sig_fixed, 40, m, m_len, 0);
+}
+
+int
+_libssh2_wincng_dsa_sha1_sign(libssh2_dsa_ctx *dsa,
+ const unsigned char *hash,
+ unsigned long hash_len,
+ unsigned char *sig_fixed)
+{
+ unsigned char *data, *sig;
+ unsigned long cbData, datalen, siglen;
+ int ret;
+
+ datalen = hash_len;
+ data = malloc(datalen);
+ if (!data) {
+ return -1;
+ }
+
+ memcpy(data, hash, datalen);
+
+ ret = BCryptSignHash(dsa->hKey, NULL, data, datalen,
+ NULL, 0, &cbData, 0);
+ if (BCRYPT_SUCCESS(ret)) {
+ siglen = cbData;
+ if (siglen == 40) {
+ sig = malloc(siglen);
+ if (sig) {
+ ret = BCryptSignHash(dsa->hKey, NULL, data, datalen,
+ sig, siglen, &cbData, 0);
+ if (BCRYPT_SUCCESS(ret)) {
+ memcpy(sig_fixed, sig, siglen);
+ }
+
+ _libssh2_wincng_safe_free(sig, siglen);
+ } else
+ ret = STATUS_NO_MEMORY;
+ } else
+ ret = STATUS_NO_MEMORY;
+ }
+
+ _libssh2_wincng_safe_free(data, datalen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void
+_libssh2_wincng_dsa_free(libssh2_dsa_ctx *dsa)
+{
+ if (!dsa)
+ return;
+
+ BCryptDestroyKey(dsa->hKey);
+ dsa->hKey = NULL;
+
+ _libssh2_wincng_safe_free(dsa->pbKeyObject, dsa->cbKeyObject);
+ _libssh2_wincng_safe_free(dsa, sizeof(libssh2_dsa_ctx));
+}
+#endif
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Key functions
+ */
+
+#ifdef HAVE_LIBCRYPT32
+static unsigned long
+_libssh2_wincng_pub_priv_write(unsigned char *key,
+ unsigned long offset,
+ const unsigned char *bignum,
+ const unsigned long length)
+{
+ _libssh2_htonu32(key + offset, length);
+ offset += 4;
+
+ memcpy(key + offset, bignum, length);
+ offset += length;
+
+ return offset;
+}
+
+static int
+_libssh2_wincng_pub_priv_keyfile_parse(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ unsigned char *pbEncoded,
+ unsigned long cbEncoded)
+{
+ unsigned char **rpbDecoded;
+ unsigned long *rcbDecoded;
+ unsigned char *key = NULL, *mth = NULL;
+ unsigned long keylen = 0, mthlen = 0;
+ unsigned long index, offset, length;
+ int ret;
+
+ ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded,
+ &rpbDecoded, &rcbDecoded, &length);
+
+ _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
+
+ if (ret) {
+ return -1;
+ }
+
+
+ if (length == 9) { /* private RSA key */
+ mthlen = 7;
+ mth = LIBSSH2_ALLOC(session, mthlen);
+ if (mth) {
+ memcpy(mth, "ssh-rsa", mthlen);
+ } else {
+ ret = -1;
+ }
+
+
+ keylen = 4 + mthlen + 4 + rcbDecoded[2] + 4 + rcbDecoded[1];
+ key = LIBSSH2_ALLOC(session, keylen);
+ if (key) {
+ offset = _libssh2_wincng_pub_priv_write(key, 0, mth, mthlen);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[2],
+ rcbDecoded[2]);
+
+ _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[1],
+ rcbDecoded[1]);
+ } else {
+ ret = -1;
+ }
+
+ } else if (length == 6) { /* private DSA key */
+ mthlen = 7;
+ mth = LIBSSH2_ALLOC(session, mthlen);
+ if (mth) {
+ memcpy(mth, "ssh-dss", mthlen);
+ } else {
+ ret = -1;
+ }
+
+ keylen = 4 + mthlen + 4 + rcbDecoded[1] + 4 + rcbDecoded[2]
+ + 4 + rcbDecoded[3] + 4 + rcbDecoded[4];
+ key = LIBSSH2_ALLOC(session, keylen);
+ if (key) {
+ offset = _libssh2_wincng_pub_priv_write(key, 0, mth, mthlen);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[1],
+ rcbDecoded[1]);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[2],
+ rcbDecoded[2]);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[3],
+ rcbDecoded[3]);
+
+ _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[4],
+ rcbDecoded[4]);
+ } else {
+ ret = -1;
+ }
+
+ } else {
+ ret = -1;
+ }
+
+
+ for (index = 0; index < length; index++) {
+ _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]);
+ rpbDecoded[index] = NULL;
+ rcbDecoded[index] = 0;
+ }
+
+ free(rpbDecoded);
+ free(rcbDecoded);
+
+
+ if (ret) {
+ if (mth)
+ LIBSSH2_FREE(session, mth);
+ if (key)
+ LIBSSH2_FREE(session, key);
+ } else {
+ *method = mth;
+ *method_len = mthlen;
+ *pubkeydata = key;
+ *pubkeydata_len = keylen;
+ }
+
+ return ret;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+int
+_libssh2_wincng_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ unsigned long cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private(session, privatekey, passphrase,
+ &pbEncoded, &cbEncoded, 1, 1);
+ if (ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_pub_priv_keyfile_parse(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pbEncoded, cbEncoded);
+#else
+ (void)method;
+ (void)method_len;
+ (void)pubkeydata;
+ (void)pubkeydata_len;
+ (void)privatekey;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to load public key from private key file: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ unsigned long cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private_memory(session, privatekeydata,
+ privatekeydata_len, passphrase,
+ &pbEncoded, &cbEncoded, 1, 1);
+ if (ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_pub_priv_keyfile_parse(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pbEncoded, cbEncoded);
+#else
+ (void)method;
+ (void)method_len;
+ (void)pubkeydata_len;
+ (void)pubkeydata;
+ (void)privatekeydata;
+ (void)privatekeydata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract public key from private key in memory: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Cipher functions
+ */
+
+int
+_libssh2_wincng_cipher_init(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ unsigned char *iv,
+ unsigned char *secret,
+ int encrypt)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_KEY_DATA_BLOB_HEADER *header;
+ unsigned char *pbKeyObject, *pbIV, *key;
+ unsigned long dwKeyObject, dwIV, dwBlockLength, cbData, keylen;
+ int ret;
+
+ (void)encrypt;
+
+ ret = BCryptGetProperty(*type.phAlg, BCRYPT_OBJECT_LENGTH,
+ (unsigned char *)&dwKeyObject,
+ sizeof(dwKeyObject),
+ &cbData, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ return -1;
+ }
+
+ ret = BCryptGetProperty(*type.phAlg, BCRYPT_BLOCK_LENGTH,
+ (unsigned char *)&dwBlockLength,
+ sizeof(dwBlockLength),
+ &cbData, 0);
+ if (!BCRYPT_SUCCESS(ret)) {
+ return -1;
+ }
+
+ pbKeyObject = malloc(dwKeyObject);
+ if (!pbKeyObject) {
+ return -1;
+ }
+
+
+ keylen = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + type.dwKeyLength;
+ key = malloc(keylen);
+ if (!key) {
+ free(pbKeyObject);
+ return -1;
+ }
+
+
+ header = (BCRYPT_KEY_DATA_BLOB_HEADER *)key;
+ header->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
+ header->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
+ header->cbKeyData = type.dwKeyLength;
+
+ memcpy(key + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER),
+ secret, type.dwKeyLength);
+
+ ret = BCryptImportKey(*type.phAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey,
+ pbKeyObject, dwKeyObject, key, keylen, 0);
+
+ _libssh2_wincng_safe_free(key, keylen);
+
+ if (!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject);
+ return -1;
+ }
+
+ if (type.dwUseIV) {
+ pbIV = malloc(dwBlockLength);
+ if (!pbIV) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject);
+ return -1;
+ }
+ dwIV = dwBlockLength;
+ memcpy(pbIV, iv, dwIV);
+ } else {
+ pbIV = NULL;
+ dwIV = 0;
+ }
+
+
+ ctx->hKey = hKey;
+ ctx->pbKeyObject = pbKeyObject;
+ ctx->pbIV = pbIV;
+ ctx->dwKeyObject = dwKeyObject;
+ ctx->dwIV = dwIV;
+ ctx->dwBlockLength = dwBlockLength;
+
+ return 0;
+}
+
+int
+_libssh2_wincng_cipher_crypt(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ int encrypt,
+ unsigned char *block,
+ size_t blocklen)
+{
+ unsigned char *pbOutput;
+ unsigned long cbOutput, cbInput;
+ int ret;
+
+ (void)type;
+
+ cbInput = (unsigned long)blocklen;
+
+ if (encrypt) {
+ ret = BCryptEncrypt(ctx->hKey, block, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0);
+ } else {
+ ret = BCryptDecrypt(ctx->hKey, block, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0);
+ }
+ if (BCRYPT_SUCCESS(ret)) {
+ pbOutput = malloc(cbOutput);
+ if (pbOutput) {
+ if (encrypt) {
+ ret = BCryptEncrypt(ctx->hKey, block, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV,
+ pbOutput, cbOutput, &cbOutput, 0);
+ } else {
+ ret = BCryptDecrypt(ctx->hKey, block, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV,
+ pbOutput, cbOutput, &cbOutput, 0);
+ }
+ if (BCRYPT_SUCCESS(ret)) {
+ memcpy(block, pbOutput, cbOutput);
+ }
+
+ _libssh2_wincng_safe_free(pbOutput, cbOutput);
+ } else
+ ret = STATUS_NO_MEMORY;
+ }
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void
+_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx)
+{
+ BCryptDestroyKey(ctx->hKey);
+ ctx->hKey = NULL;
+
+ _libssh2_wincng_safe_free(ctx->pbKeyObject, ctx->dwKeyObject);
+ ctx->pbKeyObject = NULL;
+ ctx->dwKeyObject = 0;
+
+ _libssh2_wincng_safe_free(ctx->pbIV, ctx->dwBlockLength);
+ ctx->pbIV = NULL;
+ ctx->dwBlockLength = 0;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: BigNumber functions
+ */
+
+_libssh2_bn *
+_libssh2_wincng_bignum_init(void)
+{
+ _libssh2_bn *bignum;
+
+ bignum = (_libssh2_bn *)malloc(sizeof(_libssh2_bn));
+ if (bignum) {
+ bignum->bignum = NULL;
+ bignum->length = 0;
+ }
+
+ return bignum;
+}
+
+static int
+_libssh2_wincng_bignum_resize(_libssh2_bn *bn, unsigned long length)
+{
+ unsigned char *bignum;
+
+ if (!bn)
+ return -1;
+
+ if (length == bn->length)
+ return 0;
+
+#ifdef LIBSSH2_CLEAR_MEMORY
+ if (bn->bignum && bn->length > 0 && length < bn->length) {
+ SecureZeroMemory(bn->bignum + length, bn->length - length);
+ }
+#endif
+
+ bignum = realloc(bn->bignum, length);
+ if (!bignum)
+ return -1;
+
+ bn->bignum = bignum;
+ bn->length = length;
+
+ return 0;
+}
+
+int
+_libssh2_wincng_bignum_rand(_libssh2_bn *rnd, int bits, int top, int bottom)
+{
+ unsigned char *bignum;
+ unsigned long length;
+
+ if (!rnd)
+ return -1;
+
+ length = (unsigned long)(ceil((float)bits / 8) * sizeof(unsigned char));
+ if (_libssh2_wincng_bignum_resize(rnd, length))
+ return -1;
+
+ bignum = rnd->bignum;
+
+ if (_libssh2_wincng_random(bignum, length))
+ return -1;
+
+ /* calculate significant bits in most significant byte */
+ bits %= 8;
+
+ /* fill most significant byte with zero padding */
+ bignum[0] &= (1 << (8 - bits)) - 1;
+
+ /* set some special last bits in most significant byte */
+ if (top == 0)
+ bignum[0] |= (1 << (7 - bits));
+ else if (top == 1)
+ bignum[0] |= (3 << (6 - bits));
+
+ /* make odd by setting first bit in least significant byte */
+ if (bottom)
+ bignum[length - 1] |= 1;
+
+ return 0;
+}
+
+int
+_libssh2_wincng_bignum_mod_exp(_libssh2_bn *r,
+ _libssh2_bn *a,
+ _libssh2_bn *p,
+ _libssh2_bn *m,
+ _libssh2_bn_ctx *bnctx)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_RSAKEY_BLOB *rsakey;
+ unsigned char *key, *bignum;
+ unsigned long keylen, offset, length;
+ int ret;
+
+ (void)bnctx;
+
+ if (!r || !a || !p || !m)
+ return -1;
+
+ offset = sizeof(BCRYPT_RSAKEY_BLOB);
+ keylen = offset + p->length + m->length;
+
+ key = malloc(keylen);
+ if (!key)
+ return -1;
+
+
+ /* https://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */
+ rsakey = (BCRYPT_RSAKEY_BLOB *)key;
+ rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC;
+ rsakey->BitLength = m->length * 8;
+ rsakey->cbPublicExp = p->length;
+ rsakey->cbModulus = m->length;
+ rsakey->cbPrime1 = 0;
+ rsakey->cbPrime2 = 0;
+
+ memcpy(key + offset, p->bignum, p->length);
+ offset += p->length;
+
+ memcpy(key + offset, m->bignum, m->length);
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL,
+ BCRYPT_RSAPUBLIC_BLOB, &hKey, key, keylen,
+ BCRYPT_NO_KEY_VALIDATION);
+
+ if (BCRYPT_SUCCESS(ret)) {
+ ret = BCryptEncrypt(hKey, a->bignum, a->length, NULL, NULL, 0,
+ NULL, 0, &length, BCRYPT_PAD_NONE);
+ if (BCRYPT_SUCCESS(ret)) {
+ if (!_libssh2_wincng_bignum_resize(r, length)) {
+ length = max(a->length, length);
+ bignum = malloc(length);
+ if (bignum) {
+ offset = length - a->length;
+ memset(bignum, 0, offset);
+ memcpy(bignum + offset, a->bignum, a->length);
+
+ ret = BCryptEncrypt(hKey, bignum, length, NULL, NULL, 0,
+ r->bignum, r->length, &offset,
+ BCRYPT_PAD_NONE);
+
+ _libssh2_wincng_safe_free(bignum, length);
+
+ if (BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_bignum_resize(r, offset);
+ }
+ } else
+ ret = STATUS_NO_MEMORY;
+ } else
+ ret = STATUS_NO_MEMORY;
+ }
+
+ BCryptDestroyKey(hKey);
+ }
+
+ _libssh2_wincng_safe_free(key, keylen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_bignum_set_word(_libssh2_bn *bn, unsigned long word)
+{
+ unsigned long offset, number, bits, length;
+
+ if (!bn)
+ return -1;
+
+ bits = 0;
+ number = word;
+ while (number >>= 1)
+ bits++;
+
+ length = (unsigned long) (ceil(((double)(bits + 1)) / 8.0) *
+ sizeof(unsigned char));
+ if (_libssh2_wincng_bignum_resize(bn, length))
+ return -1;
+
+ for (offset = 0; offset < length; offset++)
+ bn->bignum[offset] = (word >> (offset * 8)) & 0xff;
+
+ return 0;
+}
+
+unsigned long
+_libssh2_wincng_bignum_bits(const _libssh2_bn *bn)
+{
+ unsigned char number;
+ unsigned long offset, length, bits;
+
+ if (!bn)
+ return 0;
+
+ length = bn->length - 1;
+
+ offset = 0;
+ while (!(*(bn->bignum + offset)) && (offset < length))
+ offset++;
+
+ bits = (length - offset) * 8;
+ number = bn->bignum[offset];
+
+ while (number >>= 1)
+ bits++;
+
+ bits++;
+
+ return bits;
+}
+
+void
+_libssh2_wincng_bignum_from_bin(_libssh2_bn *bn, unsigned long len,
+ const unsigned char *bin)
+{
+ unsigned char *bignum;
+ unsigned long offset, length, bits;
+
+ if (!bn || !bin || !len)
+ return;
+
+ if (_libssh2_wincng_bignum_resize(bn, len))
+ return;
+
+ memcpy(bn->bignum, bin, len);
+
+ bits = _libssh2_wincng_bignum_bits(bn);
+ length = (unsigned long) (ceil(((double)bits) / 8.0) *
+ sizeof(unsigned char));
+
+ offset = bn->length - length;
+ if (offset > 0) {
+ memmove(bn->bignum, bn->bignum + offset, length);
+
+#ifdef LIBSSH2_CLEAR_MEMORY
+ SecureZeroMemory(bn->bignum + length, offset);
+#endif
+
+ bignum = realloc(bn->bignum, length);
+ if (bignum) {
+ bn->bignum = bignum;
+ bn->length = length;
+ }
+ }
+}
+
+void
+_libssh2_wincng_bignum_to_bin(const _libssh2_bn *bn, unsigned char *bin)
+{
+ if (bin && bn && bn->bignum && bn->length > 0) {
+ memcpy(bin, bn->bignum, bn->length);
+ }
+}
+
+void
+_libssh2_wincng_bignum_free(_libssh2_bn *bn)
+{
+ if (bn) {
+ if (bn->bignum) {
+ _libssh2_wincng_safe_free(bn->bignum, bn->length);
+ bn->bignum = NULL;
+ }
+ bn->length = 0;
+ _libssh2_wincng_safe_free(bn, sizeof(_libssh2_bn));
+ }
+}
+
+
+/*
+ * Windows CNG backend: other functions
+ */
+
+void _libssh2_init_aes_ctr(void)
+{
+ /* no implementation */
+ (void)0;
+}
+
+#endif /* LIBSSH2_WINCNG */
diff --git a/libs/libssh2/src/wincng.h b/libs/libssh2/src/wincng.h
new file mode 100644
index 0000000000..5219db7f8a
--- /dev/null
+++ b/libs/libssh2/src/wincng.h
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2013-2015 Marc Hoersken <info@marc-hoersken.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/* required for cross-compilation against the w64 mingw-runtime package */
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
+#undef _WIN32_WINNT
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include <windows.h>
+#include <bcrypt.h>
+
+
+#define LIBSSH2_MD5 1
+
+#define LIBSSH2_HMAC_RIPEMD 0
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#define LIBSSH2_AES 1
+#define LIBSSH2_AES_CTR 0
+#define LIBSSH2_BLOWFISH 0
+#define LIBSSH2_RC4 1
+#define LIBSSH2_CAST 0
+#define LIBSSH2_3DES 1
+
+#define LIBSSH2_RSA 1
+#define LIBSSH2_DSA 1
+
+#define MD5_DIGEST_LENGTH 16
+#define SHA_DIGEST_LENGTH 20
+#define SHA256_DIGEST_LENGTH 32
+#define SHA512_DIGEST_LENGTH 64
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Global context handles
+ */
+
+struct _libssh2_wincng_ctx {
+ BCRYPT_ALG_HANDLE hAlgRNG;
+ BCRYPT_ALG_HANDLE hAlgHashMD5;
+ BCRYPT_ALG_HANDLE hAlgHashSHA1;
+ BCRYPT_ALG_HANDLE hAlgHashSHA256;
+ BCRYPT_ALG_HANDLE hAlgHashSHA512;
+ BCRYPT_ALG_HANDLE hAlgHmacMD5;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA1;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA256;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA512;
+ BCRYPT_ALG_HANDLE hAlgRSA;
+ BCRYPT_ALG_HANDLE hAlgDSA;
+ BCRYPT_ALG_HANDLE hAlgAES_CBC;
+ BCRYPT_ALG_HANDLE hAlgRC4_NA;
+ BCRYPT_ALG_HANDLE hAlg3DES_CBC;
+};
+
+struct _libssh2_wincng_ctx _libssh2_wincng;
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Generic functions
+ */
+
+void _libssh2_wincng_init(void);
+void _libssh2_wincng_free(void);
+
+#define libssh2_crypto_init() \
+ _libssh2_wincng_init()
+#define libssh2_crypto_exit() \
+ _libssh2_wincng_free()
+
+#define _libssh2_random(buf, len) \
+ _libssh2_wincng_random(buf, len)
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Hash structure
+ */
+
+typedef struct __libssh2_wincng_hash_ctx {
+ BCRYPT_HASH_HANDLE hHash;
+ unsigned char *pbHashObject;
+ unsigned long dwHashObject;
+ unsigned long cbHash;
+} _libssh2_wincng_hash_ctx;
+
+/*
+ * Windows CNG backend: Hash functions
+ */
+
+#define libssh2_sha1_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha1_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA1, \
+ SHA_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha1_update(ctx, data, datalen) \
+ _libssh2_wincng_hash_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_sha1_final(ctx, hash) \
+ _libssh2_wincng_hash_final(&ctx, hash)
+#define libssh2_sha1(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA1, \
+ hash, SHA_DIGEST_LENGTH)
+
+#define libssh2_sha256_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha256_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA256, \
+ SHA256_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha256_update(ctx, data, datalen) \
+ _libssh2_wincng_hash_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_sha256_final(ctx, hash) \
+ _libssh2_wincng_hash_final(&ctx, hash)
+#define libssh2_sha256(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA256, \
+ hash, SHA256_DIGEST_LENGTH)
+
+#define libssh2_sha512_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha512_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA512, \
+ SHA512_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha512_update(ctx, data, datalen) \
+ _libssh2_wincng_hash_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_sha512_final(ctx, hash) \
+ _libssh2_wincng_hash_final(&ctx, hash)
+#define libssh2_sha512(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA512, \
+ hash, SHA512_DIGEST_LENGTH)
+
+#define libssh2_md5_ctx _libssh2_wincng_hash_ctx
+#define libssh2_md5_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashMD5, \
+ MD5_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_md5_update(ctx, data, datalen) \
+ _libssh2_wincng_hash_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_md5_final(ctx, hash) \
+ _libssh2_wincng_hash_final(&ctx, hash)
+#define libssh2_md5(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashMD5, \
+ hash, MD5_DIGEST_LENGTH)
+
+/*
+ * Windows CNG backend: HMAC functions
+ */
+
+#define libssh2_hmac_ctx _libssh2_wincng_hash_ctx
+#define libssh2_hmac_ctx_init(ctx)
+#define libssh2_hmac_sha1_init(ctx, key, keylen) \
+ _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA1, \
+ SHA_DIGEST_LENGTH, key, keylen)
+#define libssh2_hmac_md5_init(ctx, key, keylen) \
+ _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacMD5, \
+ MD5_DIGEST_LENGTH, key, keylen)
+#define libssh2_hmac_ripemd160_init(ctx, key, keylen)
+ /* not implemented */
+#define libssh2_hmac_sha256_init(ctx, key, keylen) \
+ _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA256, \
+ SHA256_DIGEST_LENGTH, key, keylen)
+#define libssh2_hmac_sha512_init(ctx, key, keylen) \
+ _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA512, \
+ SHA512_DIGEST_LENGTH, key, keylen)
+#define libssh2_hmac_update(ctx, data, datalen) \
+ _libssh2_wincng_hash_update(&ctx, (unsigned char *) data, datalen)
+#define libssh2_hmac_final(ctx, hash) \
+ _libssh2_wincng_hmac_final(&ctx, hash)
+#define libssh2_hmac_cleanup(ctx) \
+ _libssh2_wincng_hmac_cleanup(ctx)
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Key Context structure
+ */
+
+typedef struct __libssh2_wincng_key_ctx {
+ BCRYPT_KEY_HANDLE hKey;
+ unsigned char *pbKeyObject;
+ unsigned long cbKeyObject;
+} _libssh2_wincng_key_ctx;
+
+
+/*
+ * Windows CNG backend: RSA functions
+ */
+
+#define libssh2_rsa_ctx _libssh2_wincng_key_ctx
+#define _libssh2_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len) \
+ _libssh2_wincng_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len)
+#define _libssh2_rsa_new_private(rsactx, s, filename, passphrase) \
+ _libssh2_wincng_rsa_new_private(rsactx, s, filename, passphrase)
+#define _libssh2_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_wincng_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase)
+#define _libssh2_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len) \
+ _libssh2_wincng_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len)
+#define _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len) \
+ _libssh2_wincng_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len)
+#define _libssh2_rsa_free(rsactx) \
+ _libssh2_wincng_rsa_free(rsactx)
+
+/*
+ * Windows CNG backend: DSA functions
+ */
+
+#define libssh2_dsa_ctx _libssh2_wincng_key_ctx
+#define _libssh2_dsa_new(dsactx, p, p_len, q, q_len, \
+ g, g_len, y, y_len, x, x_len) \
+ _libssh2_wincng_dsa_new(dsactx, p, p_len, q, q_len, \
+ g, g_len, y, y_len, x, x_len)
+#define _libssh2_dsa_new_private(dsactx, s, filename, passphrase) \
+ _libssh2_wincng_dsa_new_private(dsactx, s, filename, passphrase)
+#define _libssh2_dsa_new_private_frommemory(dsactx, s, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_wincng_dsa_new_private_frommemory(dsactx, s, filedata, \
+ filedata_len, passphrase)
+#define _libssh2_dsa_sha1_sign(dsactx, hash, hash_len, sig) \
+ _libssh2_wincng_dsa_sha1_sign(dsactx, hash, hash_len, sig)
+#define _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len) \
+ _libssh2_wincng_dsa_sha1_verify(dsactx, sig, m, m_len)
+#define _libssh2_dsa_free(dsactx) \
+ _libssh2_wincng_dsa_free(dsactx)
+
+/*
+ * Windows CNG backend: Key functions
+ */
+
+#define _libssh2_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw) \
+ _libssh2_wincng_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw)
+#define _libssh2_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw) \
+ _libssh2_wincng_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw)
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Cipher Context structure
+ */
+
+struct _libssh2_wincng_cipher_ctx {
+ BCRYPT_KEY_HANDLE hKey;
+ unsigned char *pbKeyObject;
+ unsigned char *pbIV;
+ unsigned long dwKeyObject;
+ unsigned long dwIV;
+ unsigned long dwBlockLength;
+};
+
+#define _libssh2_cipher_ctx struct _libssh2_wincng_cipher_ctx
+
+/*
+ * Windows CNG backend: Cipher Type structure
+ */
+
+struct _libssh2_wincng_cipher_type {
+ BCRYPT_ALG_HANDLE *phAlg;
+ unsigned long dwKeyLength;
+ unsigned long dwUseIV;
+};
+
+#define _libssh2_cipher_type(type) struct _libssh2_wincng_cipher_type type
+
+#define _libssh2_cipher_aes256ctr { NULL, 32, 1 } /* not supported */
+#define _libssh2_cipher_aes192ctr { NULL, 24, 1 } /* not supported */
+#define _libssh2_cipher_aes128ctr { NULL, 16, 1 } /* not supported */
+#define _libssh2_cipher_aes256 { &_libssh2_wincng.hAlgAES_CBC, 32, 1 }
+#define _libssh2_cipher_aes192 { &_libssh2_wincng.hAlgAES_CBC, 24, 1 }
+#define _libssh2_cipher_aes128 { &_libssh2_wincng.hAlgAES_CBC, 16, 1 }
+#define _libssh2_cipher_blowfish { NULL, 16, 0 } /* not supported */
+#define _libssh2_cipher_arcfour { &_libssh2_wincng.hAlgRC4_NA, 16, 0 }
+#define _libssh2_cipher_cast5 { NULL, 16, 0 } /* not supported */
+#define _libssh2_cipher_3des { &_libssh2_wincng.hAlg3DES_CBC, 24, 1 }
+
+/*
+ * Windows CNG backend: Cipher functions
+ */
+
+#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
+ _libssh2_wincng_cipher_init(ctx, type, iv, secret, encrypt)
+#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen) \
+ _libssh2_wincng_cipher_crypt(ctx, type, encrypt, block, blocklen)
+#define _libssh2_cipher_dtor(ctx) \
+ _libssh2_wincng_cipher_dtor(ctx)
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: BigNumber Context
+ */
+
+#define _libssh2_bn_ctx int /* not used */
+#define _libssh2_bn_ctx_new() 0 /* not used */
+#define _libssh2_bn_ctx_free(bnctx) ((void)0) /* not used */
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: BigNumber structure
+ */
+
+struct _libssh2_wincng_bignum {
+ unsigned char *bignum;
+ unsigned long length;
+};
+
+#define _libssh2_bn struct _libssh2_wincng_bignum
+
+/*
+ * Windows CNG backend: BigNumber functions
+ */
+
+_libssh2_bn *_libssh2_wincng_bignum_init(void);
+
+#define _libssh2_bn_init() \
+ _libssh2_wincng_bignum_init()
+#define _libssh2_bn_init_from_bin() \
+ _libssh2_bn_init()
+#define _libssh2_bn_rand(bn, bits, top, bottom) \
+ _libssh2_wincng_bignum_rand(bn, bits, top, bottom)
+#define _libssh2_bn_mod_exp(r, a, p, m, ctx) \
+ _libssh2_wincng_bignum_mod_exp(r, a, p, m, ctx)
+#define _libssh2_bn_set_word(bn, word) \
+ _libssh2_wincng_bignum_set_word(bn, word)
+#define _libssh2_bn_from_bin(bn, len, bin) \
+ _libssh2_wincng_bignum_from_bin(bn, len, bin)
+#define _libssh2_bn_to_bin(bn, bin) \
+ _libssh2_wincng_bignum_to_bin(bn, bin)
+#define _libssh2_bn_bytes(bn) bn->length
+#define _libssh2_bn_bits(bn) \
+ _libssh2_wincng_bignum_bits(bn)
+#define _libssh2_bn_free(bn) \
+ _libssh2_wincng_bignum_free(bn)
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: forward declarations
+ */
+void _libssh2_wincng_init(void);
+void _libssh2_wincng_free(void);
+int _libssh2_wincng_random(void *buf, int len);
+void _libssh2_init_aes_ctr(void);
+
+int
+_libssh2_wincng_hash_init(_libssh2_wincng_hash_ctx *ctx,
+ BCRYPT_ALG_HANDLE hAlg, unsigned long hashlen,
+ unsigned char *key, unsigned long keylen);
+int
+_libssh2_wincng_hash_update(_libssh2_wincng_hash_ctx *ctx,
+ const unsigned char *data, unsigned long datalen);
+int
+_libssh2_wincng_hash_final(_libssh2_wincng_hash_ctx *ctx,
+ unsigned char *hash);
+int
+_libssh2_wincng_hash(unsigned char *data, unsigned long datalen,
+ BCRYPT_ALG_HANDLE hAlg,
+ unsigned char *hash, unsigned long hashlen);
+
+int
+_libssh2_wincng_hmac_final(_libssh2_wincng_hash_ctx *ctx,
+ unsigned char *hash);
+void
+_libssh2_wincng_hmac_cleanup(_libssh2_wincng_hash_ctx *ctx);
+
+int
+_libssh2_wincng_key_sha1_verify(_libssh2_wincng_key_ctx *ctx,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m,
+ unsigned long m_len,
+ unsigned long flags);
+
+int
+_libssh2_wincng_rsa_new(libssh2_rsa_ctx **rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata,
+ unsigned long coefflen);
+int
+_libssh2_wincng_rsa_new_private(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase);
+int
+_libssh2_wincng_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+int
+_libssh2_wincng_rsa_sha1_verify(libssh2_rsa_ctx *rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m,
+ unsigned long m_len);
+int
+_libssh2_wincng_rsa_sha1_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len);
+void
+_libssh2_wincng_rsa_free(libssh2_rsa_ctx *rsa);
+
+#if LIBSSH2_DSA
+int
+_libssh2_wincng_dsa_new(libssh2_dsa_ctx **dsa,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *gdata,
+ unsigned long glen,
+ const unsigned char *ydata,
+ unsigned long ylen,
+ const unsigned char *xdata,
+ unsigned long xlen);
+int
+_libssh2_wincng_dsa_new_private(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase);
+int
+_libssh2_wincng_dsa_new_private_frommemory(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+int
+_libssh2_wincng_dsa_sha1_verify(libssh2_dsa_ctx *dsa,
+ const unsigned char *sig_fixed,
+ const unsigned char *m,
+ unsigned long m_len);
+int
+_libssh2_wincng_dsa_sha1_sign(libssh2_dsa_ctx *dsa,
+ const unsigned char *hash,
+ unsigned long hash_len,
+ unsigned char *sig_fixed);
+void
+_libssh2_wincng_dsa_free(libssh2_dsa_ctx *dsa);
+#endif
+
+int
+_libssh2_wincng_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase);
+int
+_libssh2_wincng_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase);
+
+int
+_libssh2_wincng_cipher_init(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ unsigned char *iv,
+ unsigned char *secret,
+ int encrypt);
+int
+_libssh2_wincng_cipher_crypt(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ int encrypt,
+ unsigned char *block,
+ size_t blocklen);
+void
+_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx);
+
+_libssh2_bn *
+_libssh2_wincng_bignum_init(void);
+int
+_libssh2_wincng_bignum_rand(_libssh2_bn *rnd, int bits, int top, int bottom);
+int
+_libssh2_wincng_bignum_mod_exp(_libssh2_bn *r,
+ _libssh2_bn *a,
+ _libssh2_bn *p,
+ _libssh2_bn *m,
+ _libssh2_bn_ctx *bnctx);
+int
+_libssh2_wincng_bignum_set_word(_libssh2_bn *bn, unsigned long word);
+unsigned long
+_libssh2_wincng_bignum_bits(const _libssh2_bn *bn);
+void
+_libssh2_wincng_bignum_from_bin(_libssh2_bn *bn, unsigned long len,
+ const unsigned char *bin);
+void
+_libssh2_wincng_bignum_to_bin(const _libssh2_bn *bn, unsigned char *bin);
+void
+_libssh2_wincng_bignum_free(_libssh2_bn *bn);