diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4c0af8e..8570694 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,6 @@ variables: CENTOS7_BUILD: buildenv-centos7 TUMBLEWEED_BUILD: buildenv-tumbleweed MINGW_BUILD: buildenv-mingw - DEBIAN_CROSS_BUILD: buildenv-debian-cross # pkd tests fail on CentOS7 docker images, so we don't use -DSERVER_TESTING=ON centos7/openssl_1.0.x/x86_64: @@ -34,6 +33,7 @@ fedora/openssl_1.1.x/x86_64: -DPICKY_DEVELOPER=ON -DWITH_BLOWFISH_CIPHER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON + -DWITH_DEBUG_CRYPTO=ON -DWITH_DEBUG_PACKET=ON -DWITH_DEBUG_CALLTRACE=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure @@ -153,6 +153,8 @@ fedora/undefined-sanitizer: - obj/ fedora/csbuild: + variables: + GIT_DEPTH: "100" image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD script: - | @@ -168,8 +170,7 @@ fedora/csbuild: - csbuild --build-dir=obj-csbuild - --prep-cmd="rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON @SRCDIR@" - --build-cmd "make clean && make -j$(nproc)" + --build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON @SRCDIR@ && make clean && make -j$(nproc)" --git-commit-range $CI_COMMIT_RANGE --color --print-current --print-fixed @@ -215,7 +216,7 @@ fedora/libgcrypt/x86_64: -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON - -DWITH_GCRYPT=ON .. && + -DWITH_GCRYPT=ON -DWITH_DEBUG_CRYPTO=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared @@ -235,7 +236,7 @@ fedora/mbedtls/x86_64: -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON - -DWITH_MBEDTLS=ON .. && + -DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared @@ -295,33 +296,6 @@ fedora/mingw32: paths: - obj/ -.Debian.cross.template: &Debian_cross_template - stage: test - image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$DEBIAN_CROSS_BUILD - script: - - build=$(dpkg-architecture -qDEB_HOST_GNU_TYPE) - - host="${CI_JOB_NAME#*.cross.}" - - mkdir -p obj && cd obj && cmake - -DCMAKE_C_COMPILER="$(which $host-gcc)" - -DCMAKE_CXX_COMPILER="$(which $host-g++)" - -DCMAKE_BUILD_TYPE=RelWithDebInfo - -DUNIT_TESTING=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON - -DWITH_PCAP=ON .. && - make -j$(nproc) && - ctest --output-on-failure - tags: - - shared - except: - - tags - artifacts: - expire_in: 1 week - when: on_failure - paths: - - obj/ - -.Debian.cross.mips-linux-gnu: - <<: *Debian_cross_template - tumbleweed/openssl_1.1.x/x86_64/gcc: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD script: @@ -330,7 +304,7 @@ tumbleweed/openssl_1.1.x/x86_64/gcc: -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config - -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. && + -DUNIT_TESTING=ON -DSERVER_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared @@ -371,7 +345,7 @@ tumbleweed/openssl_1.1.x/x86_64/gcc7: -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config - -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. && + -DUNIT_TESTING=ON -DSERVER_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared @@ -413,7 +387,8 @@ tumbleweed/openssl_1.1.x/x86_64/clang: -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config - -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. && + -DUNIT_TESTING=ON + -DSERVER_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared @@ -446,7 +421,7 @@ tumbleweed/undefined-sanitizer: -DCMAKE_BUILD_TYPE=UndefinedSanitizer -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON - -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. && + -DUNIT_TESTING=ON -DSERVER_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared @@ -468,7 +443,7 @@ tumbleweed/static-analysis: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DPICKY_DEVELOPER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON - -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. && + -DUNIT_TESTING=ON -DSERVER_TESTING=ON .. && scan-build --status-bugs -o scan make -j$(nproc) tags: - shared @@ -481,9 +456,11 @@ tumbleweed/static-analysis: - obj/scan visualstudio/x86_64: + variables: + ErrorActionPreference: STOP script: - $env:VCPKG_DEFAULT_TRIPLET="x64-windows" - - cd obj + - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1} - cmake -A x64 -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_TOOLCHAIN_FILE" @@ -509,9 +486,11 @@ visualstudio/x86_64: - obj/ visualstudio/x86: + variables: + ErrorActionPreference: STOP script: - $env:VCPKG_DEFAULT_TRIPLET="x86-windows" - - cd obj + - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1} - cmake -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_TOOLCHAIN_FILE" -DPICKY_DEVELOPER=ON diff --git a/CMakeLists.txt b/CMakeLists.txt index b9f6ef4..41976ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") include(DefineCMakeDefaults) include(DefineCompilerFlags) -project(libssh VERSION 0.9.0 LANGUAGES C) +project(libssh VERSION 0.9.4 LANGUAGES C) # global needed variable set(APPLICATION_NAME ${PROJECT_NAME}) @@ -22,16 +22,16 @@ set(APPLICATION_NAME ${PROJECT_NAME}) # Increment AGE. Set REVISION to 0 # If the source code was changed, but there were no interface changes: # Increment REVISION. -set(LIBRARY_VERSION "4.8.1") +set(LIBRARY_VERSION "4.8.5") set(LIBRARY_SOVERSION "4") # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked # add definitions include(DefinePlatformDefaults) -include(DefineInstallationPaths) include(DefineOptions.cmake) include(CPackConfig.cmake) +include(GNUInstallDirs) include(CompilerChecks.cmake) @@ -117,7 +117,7 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc DESTINATION - ${LIB_INSTALL_DIR}/pkgconfig + ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT pkgconfig ) @@ -133,21 +133,13 @@ write_basic_package_version_file(libssh-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) -# libssh-config.cmake -configure_package_config_file(${PROJECT_NAME}-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_DIR}/${PROJECT_NAME} - PATH_VARS INCLUDE_INSTALL_DIR LIB_INSTALL_DIR) - install( FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake DESTINATION - ${CMAKE_INSTALL_DIR}/${PROJECT_NAME} + ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} COMPONENT - devel -) + devel) if (WITH_EXAMPLES) add_subdirectory(examples) @@ -213,6 +205,11 @@ endif (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND) add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source DEPENDS ${_SYMBOL_TARGET}) +# Link compile database for clangd +execute_process(COMMAND cmake -E create_symlink + "${CMAKE_BINARY_DIR}/compile_commands.json" + "${CMAKE_SOURCE_DIR}/compile_commands.json") + message(STATUS "********************************************") message(STATUS "********** ${PROJECT_NAME} build options : **********") @@ -225,7 +222,7 @@ message(STATUS "Server support : ${WITH_SERVER}") message(STATUS "GSSAPI support : ${WITH_GSSAPI}") message(STATUS "GEX support : ${WITH_GEX}") message(STATUS "Pcap debugging support : ${WITH_PCAP}") -message(STATUS "With static library: ${WITH_STATIC_LIB}") +message(STATUS "Build shared library: ${BUILD_SHARED_LIBS}") message(STATUS "Unit testing: ${UNIT_TESTING}") message(STATUS "Client code testing: ${CLIENT_TESTING}") message(STATUS "Blowfish cipher support: ${WITH_BLOWFISH_CIPHER}") diff --git a/CPackConfig.cmake b/CPackConfig.cmake index 51b300c..c4a3598 100644 --- a/CPackConfig.cmake +++ b/CPackConfig.cmake @@ -10,7 +10,7 @@ set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) # SOURCE GENERATOR set(CPACK_SOURCE_GENERATOR "TXZ") -set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]git/;.gitignore;/build*;/obj*;tags;cscope.*") +set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]git/;/[.]clangd/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") ### NSIS INSTALLER @@ -23,7 +23,7 @@ if (WIN32) set(CPACK_GENERATOR "${CPACK_GENERATOR};NSIS") set(CPACK_NSIS_DISPLAY_NAME "The SSH Library") set(CPACK_NSIS_COMPRESSOR "/SOLID zlib") - set(CPACK_NSIS_MENU_LINKS "http://www.libssh.org/" "libssh homepage") + set(CPACK_NSIS_MENU_LINKS "https://www.libssh.org/" "libssh homepage") endif (NSIS_MAKE) endif (WIN32) diff --git a/ChangeLog b/ChangeLog index d54889a..e8bc19d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,46 @@ ChangeLog ========== +version 0.9.4 (released 2020-04-09) + * Fixed CVE-2020-1730 - Possible DoS in client and server when handling + AES-CTR keys with OpenSSL + * Added diffie-hellman-group14-sha256 + * Fixed serveral possible memory leaks + +version 0.9.3 (released 2019-12-10) + * Fixed CVE-2019-14889 - SCP: Unsanitized location leads to command execution + * SSH-01-003 Client: Missing NULL check leads to crash in erroneous state + * SSH-01-006 General: Various unchecked Null-derefs cause DOS + * SSH-01-007 PKI Gcrypt: Potential UAF/double free with RSA pubkeys + * SSH-01-010 SSH: Deprecated hash function in fingerprinting + * SSH-01-013 Conf-Parsing: Recursive wildcards in hostnames lead to DOS + * SSH-01-014 Conf-Parsing: Integer underflow leads to OOB array access + * SSH-01-001 State Machine: Initial machine states should be set explicitly + * SSH-01-002 Kex: Differently bound macros used to iterate same array + * SSH-01-005 Code-Quality: Integer sign confusion during assignments + * SSH-01-008 SCP: Protocol Injection via unescaped File Names + * SSH-01-009 SSH: Update documentation which RFCs are implemented + * SSH-01-012 PKI: Information leak via uninitialized stack buffer + +version 0.9.2 (released 2019-11-07) + * Fixed libssh-config.cmake + * Fixed issues with rsa algorithm negotiation (T191) + * Fixed detection of OpenSSL ed25519 support (T197) + +version 0.9.1 (released 2019-10-25) + * Added support for Ed25519 via OpenSSL + * Added support for X25519 via OpenSSL + * Added support for localuser in Match keyword + * Fixed Match keyword to be case sensitive + * Fixed compilation with LibreSSL + * Fixed error report of channel open (T75) + * Fixed sftp documentation (T137) + * Fixed known_hosts parsing (T156) + * Fixed build issue with MinGW (T157) + * Fixed build with gcc 9 (T164) + * Fixed deprecation issues (T165) + * Fixed known_hosts directory creation (T166) + version 0.9.0 (released 2019-06-28) * Added support for AES-GCM * Added improved rekeying support diff --git a/CompilerChecks.cmake b/CompilerChecks.cmake index a3e4b5c..5bdc05c 100644 --- a/CompilerChecks.cmake +++ b/CompilerChecks.cmake @@ -42,6 +42,7 @@ if (UNIX) add_c_compiler_flag("-Wstrict-overflow=2" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wno-format-zero-length" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wmissing-field-initializers" SUPPORTED_COMPILER_FLAGS) + add_c_compiler_flag("-Wsign-compare" SUPPORTED_COMPILER_FLAGS) check_c_compiler_flag("-Wformat" REQUIRED_FLAGS_WFORMAT) if (REQUIRED_FLAGS_WFORMAT) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 643a0a4..c8bb2aa 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -9,10 +9,7 @@ include(TestBigEndian) set(PACKAGE ${PROJECT_NAME}) set(VERSION ${PROJECT_VERSION}) -set(DATADIR ${DATA_INSTALL_DIR}) -set(LIBDIR ${LIB_INSTALL_DIR}) -set(PLUGINDIR "${PLUGIN_INSTALL_DIR}-${LIBRARY_SOVERSION}") -set(SYSCONFDIR ${SYSCONF_INSTALL_DIR}) +set(SYSCONFDIR ${CMAKE_INSTALL_SYSCONFDIR}) set(BINARYDIR ${CMAKE_BINARY_DIR}) set(SOURCEDIR ${CMAKE_SOURCE_DIR}) @@ -133,10 +130,35 @@ if (OPENSSL_FOUND) set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + check_function_exists(FIPS_mode HAVE_OPENSSL_FIPS_MODE) + + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES) + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + check_function_exists(EVP_DigestSign HAVE_OPENSSL_EVP_DIGESTSIGN) + + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + check_function_exists(EVP_DigestVerify HAVE_OPENSSL_EVP_DIGESTVERIFY) + check_function_exists(OPENSSL_ia32cap_loc HAVE_OPENSSL_IA32CAP_LOC) + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + check_symbol_exists(EVP_PKEY_ED25519 "openssl/evp.h" FOUND_OPENSSL_ED25519) + + if (HAVE_OPENSSL_EVP_DIGESTSIGN AND HAVE_OPENSSL_EVP_DIGESTVERIFY AND + FOUND_OPENSSL_ED25519) + set(HAVE_OPENSSL_ED25519 1) + endif() + + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + check_symbol_exists(EVP_PKEY_X25519 "openssl/evp.h" HAVE_OPENSSL_X25519) + unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) endif() diff --git a/DefineOptions.cmake b/DefineOptions.cmake index 52b68a7..b82a501 100644 --- a/DefineOptions.cmake +++ b/DefineOptions.cmake @@ -2,7 +2,6 @@ option(WITH_GSSAPI "Build with GSSAPI support" ON) option(WITH_ZLIB "Build with ZLIB support" ON) option(WITH_SFTP "Build with SFTP support" ON) option(WITH_SERVER "Build with SSH server support" ON) -option(WITH_STATIC_LIB "Build with a static library" OFF) option(WITH_DEBUG_CRYPTO "Build with cryto debug output" OFF) option(WITH_DEBUG_PACKET "Build with packet debug output" OFF) option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON) @@ -11,6 +10,7 @@ option(WITH_MBEDTLS "Compile against libmbedtls" OFF) option(WITH_BLOWFISH_CIPHER "Compile with blowfish support" OFF) option(WITH_PCAP "Compile with Pcap generation support" ON) option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) +option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(UNIT_TESTING "Build with unit tests" OFF) option(CLIENT_TESTING "Build with client tests; requires openssh" OFF) option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" OFF) @@ -34,13 +34,9 @@ if (WITH_BENCHMARKS) set(CLIENT_TESTING ON) endif() -if (WITH_STATIC_LIB) - set(BUILD_STATIC_LIB ON) -endif (WITH_STATIC_LIB) - -if (UNIT_TESTING) +if (UNIT_TESTING OR CLIENT_TESTING OR SERVER_TESTING) set(BUILD_STATIC_LIB ON) -endif (UNIT_TESTING) +endif() if (WITH_NACL) set(WITH_NACL ON) diff --git a/INSTALL b/INSTALL index 0ccb500..0f0cebf 100644 --- a/INSTALL +++ b/INSTALL @@ -7,11 +7,11 @@ In order to build libssh, you need to install several components: - A C compiler -- [CMake](http://www.cmake.org) >= 2.6.0. -- [openssl](http://www.openssl.org) >= 0.9.8 +- [CMake](https://www.cmake.org) >= 2.6.0. +- [openssl](https://www.openssl.org) >= 0.9.8 or -- [gcrypt](http://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4 -- [libz](http://www.zlib.net) >= 1.2 +- [gcrypt](https://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4 +- [libz](https://www.zlib.net) >= 1.2 optional: - [cmocka](https://cmocka.org/) >= 1.1.0 @@ -117,4 +117,4 @@ This document is written using [Markdown][] syntax, making it possible to provide usable information in both plain text and HTML format. Whenever modifying this document please use [Markdown][] syntax. -[markdown]: http://www.daringfireball.net/projects/markdown +[markdown]: https://www.daringfireball.net/projects/markdown diff --git a/README b/README index 3c9feec..44100e7 100644 --- a/README +++ b/README @@ -31,7 +31,7 @@ If you ask yourself how to compile libssh, please read INSTALL before anything. 3* Where ? -_-_-_-_-_-_ -http://www.libssh.org +https://www.libssh.org 4* Contributing -_-_-_-_-_-_-_-_-_ diff --git a/README.CodingStyle b/README.CodingStyle index 11acd8d..7489cce 100644 --- a/README.CodingStyle +++ b/README.CodingStyle @@ -60,7 +60,7 @@ following to $HOME/.vimrc: You can use the Vim gitmodline plugin to store this in the git config: - http://git.cryptomilk.org/projects/vim-gitmodeline.git/ + https://git.cryptomilk.org/projects/vim-gitmodeline.git/ For Vim, the following settings in $HOME/.vimrc will also deal with displaying trailing whitespace: diff --git a/SubmittingPatches b/SubmittingPatches index f7504ef..bd38fae 100644 --- a/SubmittingPatches +++ b/SubmittingPatches @@ -23,7 +23,7 @@ much easier to work with individuals who have ownership than corporate legal departments if we ever need to make reasonable compromises with people using and working with libssh. -We track the ownership of every part of libssh via http://git.libssh.org, +We track the ownership of every part of libssh via https://git.libssh.org, our source code control system, so we know the provenance of every piece of code that is committed to libssh. @@ -85,7 +85,7 @@ By making a contribution to this project, I certify that: Free Software Foundation; either version 2.1 of the License, or (at the option of the project) any later version. - http://www.gnu.org/licenses/lgpl-2.1.html + https://www.gnu.org/licenses/lgpl-2.1.html We will maintain a copy of that email as a record that you have the diff --git a/config.h.cmake b/config.h.cmake index 5e4edc8..98a72f6 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -4,10 +4,6 @@ /* Version number of package */ #cmakedefine VERSION "${PROJECT_VERSION}" -#cmakedefine LOCALEDIR "${LOCALE_INSTALL_DIR}" -#cmakedefine DATADIR "${DATADIR}" -#cmakedefine LIBDIR "${LIBDIR}" -#cmakedefine PLUGINDIR "${PLUGINDIR}" #cmakedefine SYSCONFDIR "${SYSCONFDIR}" #cmakedefine BINARYDIR "${BINARYDIR}" #cmakedefine SOURCEDIR "${SOURCEDIR}" @@ -101,6 +97,12 @@ /* Define to 1 if you have gl_flags as a glob_t sturct member */ #cmakedefine HAVE_GLOB_GL_FLAGS_MEMBER 1 +/* Define to 1 if you have OpenSSL with Ed25519 support */ +#cmakedefine HAVE_OPENSSL_ED25519 1 + +/* Define to 1 if you have OpenSSL with X25519 support */ +#cmakedefine HAVE_OPENSSL_X25519 1 + /*************************** FUNCTIONS ***************************/ /* Define to 1 if you have the `EVP_aes128_ctr' function. */ @@ -124,6 +126,15 @@ /* Define to 1 if you have the `EVP_KDF_CTX_new_id' function. */ #cmakedefine HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID 1 +/* Define to 1 if you have the `FIPS_mode' function. */ +#cmakedefine HAVE_OPENSSL_FIPS_MODE 1 + +/* Define to 1 if you have the `EVP_DigestSign' function. */ +#cmakedefine HAVE_OPENSSL_EVP_DIGESTSIGN 1 + +/* Define to 1 if you have the `EVP_DigestVerify' function. */ +#cmakedefine HAVE_OPENSSL_EVP_DIGESTVERIFY 1 + /* Define to 1 if you have the `OPENSSL_ia32cap_loc' function. */ #cmakedefine HAVE_OPENSSL_IA32CAP_LOC 1 diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index f96b027..8a66649 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -15,6 +15,8 @@ if (DOXYGEN_FOUND) set(DOXYGEN_MARKDOWN_SUPPORT YES) set(DOXYGEN_PREDEFINED DOXYGEN + WITH_SERVER + WITH_SFTP PRINTF_ATTRIBUTE(x,y)) set(DOXYGEN_EXCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/that_style) diff --git a/doc/curve25519-sha256@libssh.org.txt b/doc/curve25519-sha256@libssh.org.txt index 0317fe4..7554190 100644 --- a/doc/curve25519-sha256@libssh.org.txt +++ b/doc/curve25519-sha256@libssh.org.txt @@ -112,8 +112,8 @@ This number is calculated using the following procedure: This conversion follows the network byte order. This step differs from RFC5656. -[RFC5656] http://tools.ietf.org/html/rfc5656 +[RFC5656] https://tools.ietf.org/html/rfc5656 [SCHNEIER] https://www.schneier.com/blog/archives/2013/09/the_nsa_is_brea.html#c1675929 -[DJB] http://cr.yp.to/talks/2013.05.31/slides-dan+tanja-20130531-4x3.pdf +[DJB] https://cr.yp.to/talks/2013.05.31/slides-dan+tanja-20130531-4x3.pdf [Curve25519] "Curve25519: new Diffie-Hellman speed records." - http://cr.yp.to/ecdh/curve25519-20060209.pdf + https://cr.yp.to/ecdh/curve25519-20060209.pdf diff --git a/doc/linking.dox b/doc/linking.dox index 042086e..7ae0d31 100644 --- a/doc/linking.dox +++ b/doc/linking.dox @@ -28,6 +28,6 @@ the dllimport attribute. @endcode If you're are statically linking with OpenSSL, read the "Linking your -application" section in the NOTES. in the OpenSSL source tree! +application" section in the NOTES.[OS] in the OpenSSL source tree! */ diff --git a/doc/mainpage.dox b/doc/mainpage.dox index d319385..f4ff4d8 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -39,8 +39,8 @@ The libssh library provides: - Client and server support - SSHv2 and SSHv1 protocol support - - Supports Linux, UNIX, BSD, Solaris, OS/2 and Windows - - Automated test cases with nightly tests + - Supports Linux, UNIX, BSD, Solaris, OS/2 and Windows + - Automated test cases with nightly tests - Event model based on poll(2), or a poll(2)-emulation. @section main-copyright Copyright Policy @@ -111,7 +111,7 @@ By making a contribution to this project, I certify that: Free Software Foundation; either version 2.1 of the License, or (at the option of the project) any later version. -http://www.gnu.org/licenses/lgpl-2.1.html +https://www.gnu.org/licenses/lgpl-2.1.html @endverbatim We will maintain a copy of that email as a record that you have the rights to @@ -151,47 +151,79 @@ The libssh Team The following RFC documents described SSH-2 protcol as an Internet standard. - - RFC 4250, + - RFC 4250, The Secure Shell (SSH) Protocol Assigned Numbers - - RFC 4251, + - RFC 4251, The Secure Shell (SSH) Protocol Architecture - - RFC 4252, + - RFC 4252, The Secure Shell (SSH) Authentication Protocol - - RFC 4253, + - RFC 4253, The Secure Shell (SSH) Transport Layer Protocol - - RFC 4254, + - RFC 4254, The Secure Shell (SSH) Connection Protocol - - RFC 4255, + - RFC 4255, Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints - - RFC 4256, + (not implemented in libssh) + - RFC 4256, Generic Message Exchange Authentication for the Secure Shell Protocol (SSH) - - RFC 4335, + - RFC 4335, The Secure Shell (SSH) Session Channel Break Extension - - RFC 4344, + - RFC 4344, The Secure Shell (SSH) Transport Layer Encryption Modes - - RFC 4345, + - RFC 4345, Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol It was later modified and expanded by the following RFCs. - - RFC 4419, + - RFC 4419, Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol - - RFC 4432, + - RFC 4432, RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol - - RFC 4462, + (not implemented in libssh) + - RFC 4462, Generic Security Service Application Program Interface (GSS-API) Authentication and Key Exchange for the Secure Shell (SSH) Protocol - - RFC 4716, + (only the authentication implemented in libssh) + - RFC 4716, The Secure Shell (SSH) Public Key File Format - - RFC 5647, + (not implemented in libssh) + - RFC 5647, AES Galois Counter Mode for the Secure Shell Transport Layer Protocol - - RFC 5656, + (the algorithm negotiation implemented according to openssh.com) + - RFC 5656, Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer + - RFC 6594, + Use of the SHA-256 Algorithm with RSA, DSA, and ECDSA in SSHFP Resource Records + (not implemented in libssh) + - RFC 6668, + SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol + - RFC 7479, + Using Ed25519 in SSHFP Resource Records + (not implemented in libssh) + - RFC 8160, + IUTF8 Terminal Mode in Secure Shell (SSH) + (not handled in libssh) + - RFC 8270, + Increase the Secure Shell Minimum Recommended Diffie-Hellman Modulus Size to 2048 Bits + - RFC 8308, + Extension Negotiation in the Secure Shell (SSH) Protocol + (only the "server-sig-algs" extension implemented) + - RFC 8332, + Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol + +There are also drafts that are being currently developed and followed. + + - draft-ietf-curdle-ssh-kex-sha2-10 + Key Exchange (KEX) Method Updates and Recommendations for Secure Shell (SSH) + - draft-miller-ssh-agent-03 + SSH Agent Protocol + - draft-ietf-curdle-ssh-curves-12 + Secure Shell (SSH) Key Exchange Method using Curve25519 and Curve448 Interesting cryptography documents: - - PKCS #11, PKCS #11 reference documents, describing interface with smartcards. + - PKCS #11, PKCS #11 reference documents, describing interface with smartcards. @subsection main-rfc-sftp Secure Shell File Transfer Protocol (SFTP) @@ -199,26 +231,22 @@ The protocol is not an Internet standard but it is still widely implemented. OpenSSH and most other implementation implement Version 3 of the protocol. We do the same in libssh. - - + - draft-ietf-secsh-filexfer-02.txt, SSH File Transfer Protocol @subsection main-rfc-extensions Secure Shell Extensions -The libssh project has an extension to support Curve25519 which is also supported by -the OpenSSH project. - - - curve25519-sha256@libssh.org, - Curve25519-SHA256 for ECDH KEX - The OpenSSH project has defined some extensions to the protocol. We support some of them like the statvfs calls in SFTP or the ssh-agent. - - + - OpenSSH's deviations and extensions - - - OpenSSH's ssh-agent - - + - OpenSSH's pubkey certificate authentication + - + chacha20-poly1305@openssh.com authenticated encryption mode + - + OpenSSH private key format (openssh-key-v1) */ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d064fd5..70a296c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -6,10 +6,7 @@ set(examples_SRCS connect_ssh.c ) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} -) +include_directories(${libssh_BINARY_DIR}) if (ARGP_INCLUDE_DIR) include_directories(${ARGP_INCLUDE_DIR}) @@ -18,68 +15,68 @@ endif() if (UNIX AND NOT WIN32) add_executable(libssh_scp libssh_scp.c ${examples_SRCS}) target_compile_options(libssh_scp PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(libssh_scp ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(libssh_scp ssh::ssh) add_executable(scp_download scp_download.c ${examples_SRCS}) target_compile_options(scp_download PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(scp_download ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(scp_download ssh::ssh) add_executable(sshnetcat sshnetcat.c ${examples_SRCS}) target_compile_options(sshnetcat PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(sshnetcat ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(sshnetcat ssh::ssh) if (WITH_SFTP) add_executable(samplesftp samplesftp.c ${examples_SRCS}) target_compile_options(samplesftp PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(samplesftp ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(samplesftp ssh::ssh) endif (WITH_SFTP) add_executable(ssh-client ssh_client.c ${examples_SRCS}) target_compile_options(ssh-client PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(ssh-client ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(ssh-client ssh::ssh) if (WITH_SERVER AND (ARGP_LIBRARY OR HAVE_ARGP_H)) if (HAVE_LIBUTIL) add_executable(ssh_server_fork ssh_server_fork.c) target_compile_options(ssh_server_fork PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(ssh_server_fork ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARY} util) + target_link_libraries(ssh_server_fork ssh::ssh ${ARGP_LIBRARY} util) endif (HAVE_LIBUTIL) if (WITH_GSSAPI AND GSSAPI_FOUND) add_executable(samplesshd-cb samplesshd-cb.c) target_compile_options(samplesshd-cb PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(samplesshd-cb ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARY}) + target_link_libraries(samplesshd-cb ssh::ssh ${ARGP_LIBRARY}) add_executable(proxy proxy.c) target_compile_options(proxy PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(proxy ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARY}) + target_link_libraries(proxy ssh::ssh ${ARGP_LIBRARY}) add_executable(sshd_direct-tcpip sshd_direct-tcpip.c) target_compile_options(sshd_direct-tcpip PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(sshd_direct-tcpip ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARY}) + target_link_libraries(sshd_direct-tcpip ssh::ssh ${ARGP_LIBRARY}) endif (WITH_GSSAPI AND GSSAPI_FOUND) add_executable(samplesshd-kbdint samplesshd-kbdint.c) target_compile_options(samplesshd-kbdint PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(samplesshd-kbdint ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARY}) + target_link_libraries(samplesshd-kbdint ssh::ssh ${ARGP_LIBRARY}) endif() endif (UNIX AND NOT WIN32) add_executable(exec exec.c ${examples_SRCS}) target_compile_options(exec PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) -target_link_libraries(exec ${LIBSSH_SHARED_LIBRARY}) +target_link_libraries(exec ssh::ssh) add_executable(senddata senddata.c ${examples_SRCS}) target_compile_options(senddata PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) -target_link_libraries(senddata ${LIBSSH_SHARED_LIBRARY}) +target_link_libraries(senddata ssh::ssh) add_executable(keygen keygen.c) target_compile_options(keygen PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) -target_link_libraries(keygen ${LIBSSH_SHARED_LIBRARY}) +target_link_libraries(keygen ssh::ssh) add_executable(libsshpp libsshpp.cpp) -target_link_libraries(libsshpp ${LIBSSH_SHARED_LIBRARY}) +target_link_libraries(libsshpp ssh::ssh) add_executable(libsshpp_noexcept libsshpp_noexcept.cpp) -target_link_libraries(libsshpp_noexcept ${LIBSSH_SHARED_LIBRARY}) +target_link_libraries(libsshpp_noexcept ssh::ssh) diff --git a/examples/authentication.c b/examples/authentication.c index 375987a..7c47c8b 100644 --- a/examples/authentication.c +++ b/examples/authentication.c @@ -234,7 +234,7 @@ int authenticate_console(ssh_session session) banner = ssh_get_issue_banner(session); if (banner) { printf("%s\n",banner); - ssh_string_free_char(banner); + SSH_STRING_FREE_CHAR(banner); } return rc; diff --git a/examples/libssh_scp.c b/examples/libssh_scp.c index ff38b83..7a30b37 100644 --- a/examples/libssh_scp.c +++ b/examples/libssh_scp.c @@ -257,14 +257,15 @@ static int open_location(struct location *loc, int flag) { * @param recursive Copy also directories */ static int do_copy(struct location *src, struct location *dest, int recursive) { - int size; + size_t size; socket_t fd; struct stat s; int w, r; char buffer[16384]; - int total = 0; - int mode; + size_t total = 0; + mode_t mode; char *filename = NULL; + /* recursive mode doesn't work yet */ (void)recursive; /* Get the file name and size*/ @@ -302,7 +303,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { fprintf(stderr, "Error: %s\n", ssh_get_error(src->session)); - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); return -1; } } while(r != SSH_SCP_REQUEST_NEWFILE); @@ -315,7 +316,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { fprintf(stderr, "error: %s\n", ssh_get_error(dest->session)); - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); ssh_scp_free(dest->scp); dest->scp = NULL; return -1; @@ -330,7 +331,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { if (src->is_ssh) { ssh_scp_deny_request(src->scp, "Cannot open local file"); } - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); return -1; } } @@ -346,7 +347,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { fprintf(stderr, "Error reading scp: %s\n", ssh_get_error(src->session)); - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); return -1; } @@ -363,7 +364,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { fprintf(stderr, "Error reading file: %s\n", strerror(errno)); - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); return -1; } } @@ -376,7 +377,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { ssh_get_error(dest->session)); ssh_scp_free(dest->scp); dest->scp = NULL; - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); return -1; } } else { @@ -385,7 +386,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { fprintf(stderr, "Error writing in local file: %s\n", strerror(errno)); - ssh_string_free_char(filename); + SSH_STRING_FREE_CHAR(filename); return -1; } } @@ -393,8 +394,8 @@ static int do_copy(struct location *src, struct location *dest, int recursive) { } while(total < size); - ssh_string_free_char(filename); - printf("wrote %d bytes\n", total); + SSH_STRING_FREE_CHAR(filename); + printf("wrote %zu bytes\n", total); return 0; } diff --git a/examples/ssh_client.c b/examples/ssh_client.c index 7675e91..54ecd30 100644 --- a/examples/ssh_client.c +++ b/examples/ssh_client.c @@ -1,16 +1,17 @@ -/* client.c */ -/* -Copyright 2003-2009 Aris Adamantiadis - -This file is part of the SSH Library +/* ssh_client.c */ -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ +/* + * Copyright 2003-2015 Aris Adamantiadis + * + * This file is part of the SSH Library + * + * You are free to copy this file, modify it in any way, consider it being public + * domain. This does not apply to the rest of the library though, but it is + * allowed to cut-and-paste working code from this file to any license of + * program. + * The goal is to show the API in action. It's not a reference on how terminal + * clients must be made or how a client should react. + */ #include "config.h" #include @@ -203,14 +204,14 @@ static void select_loop(ssh_session session,ssh_channel channel) /* stdin */ connector_in = ssh_connector_new(session); - ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDOUT); + ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDINOUT); ssh_connector_set_in_fd(connector_in, 0); ssh_event_add_connector(event, connector_in); /* stdout */ connector_out = ssh_connector_new(session); ssh_connector_set_out_fd(connector_out, 1); - ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDOUT); + ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDINOUT); ssh_event_add_connector(event, connector_out); /* stderr */ @@ -238,7 +239,6 @@ static void select_loop(ssh_session session,ssh_channel channel) ssh_connector_free(connector_err); ssh_event_free(event); - ssh_channel_free(channel); } static void shell(ssh_session session) @@ -246,7 +246,11 @@ static void shell(ssh_session session) ssh_channel channel; struct termios terminal_local; int interactive=isatty(0); + channel = ssh_channel_new(session); + if (channel == NULL) { + return; + } if (interactive) { tcgetattr(0, &terminal_local); @@ -255,6 +259,7 @@ static void shell(ssh_session session) if (ssh_channel_open_session(channel)) { printf("Error opening channel : %s\n", ssh_get_error(session)); + ssh_channel_free(channel); return; } chan = channel; @@ -265,6 +270,7 @@ static void shell(ssh_session session) if (ssh_channel_request_shell(channel)) { printf("Requesting shell : %s\n", ssh_get_error(session)); + ssh_channel_free(channel); return; } @@ -278,6 +284,7 @@ static void shell(ssh_session session) if (interactive) { do_cleanup(0); } + ssh_channel_free(channel); } static void batch_shell(ssh_session session) @@ -294,12 +301,18 @@ static void batch_shell(ssh_session session) } channel = ssh_channel_new(session); + if (channel == NULL) { + return; + } + ssh_channel_open_session(channel); if (ssh_channel_request_exec(channel, buffer)) { printf("Error executing '%s' : %s\n", buffer, ssh_get_error(session)); + ssh_channel_free(channel); return; } select_loop(session, channel); + ssh_channel_free(channel); } static int client(ssh_session session) diff --git a/examples/ssh_server_fork.c b/examples/ssh_server_fork.c index e346dd3..c9892d8 100644 --- a/examples/ssh_server_fork.c +++ b/examples/ssh_server_fork.c @@ -70,6 +70,8 @@ static void set_default_keys(ssh_bind sshbind, ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, KEYS_FOLDER "ssh_host_ecdsa_key"); } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, + KEYS_FOLDER "ssh_host_ed25519_key"); } #define DEF_STR_SIZE 1024 char authorizedkeys[DEF_STR_SIZE] = {0}; diff --git a/examples/sshd_direct-tcpip.c b/examples/sshd_direct-tcpip.c index 076933f..36a15a5 100644 --- a/examples/sshd_direct-tcpip.c +++ b/examples/sshd_direct-tcpip.c @@ -23,7 +23,6 @@ clients must be made or how a client should react. #include #include #include -#include #ifdef HAVE_ARGP_H #include @@ -34,6 +33,20 @@ clients must be made or how a client should react. #include #include +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) + +#ifndef __unused__ +# ifdef HAVE_UNUSED_ATTRIBUTE +# define __unused__ __attribute__((unused)) +# else /* HAVE_UNUSED_ATTRIBUTE */ +# define __unused__ +# endif /* HAVE_UNUSED_ATTRIBUTE */ +#endif /* __unused__ */ + +#ifndef UNUSED_PARAM +#define UNUSED_PARAM(param) param __unused__ +#endif /* UNUSED_PARAM */ + #ifndef KEYS_FOLDER #ifdef _WIN32 #define KEYS_FOLDER @@ -218,7 +231,9 @@ static void my_channel_close_function(ssh_session session, ssh_channel channel, struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata; (void)session; - _ssh_log(SSH_LOG_PROTOCOL, "=== my_channel_close_function", "Channel %d:%d closed by remote. State=%d", channel->local_channel, channel->remote_channel, channel->state); + _ssh_log(SSH_LOG_PROTOCOL, + "=== my_channel_close_function", + "Channel closed by remote."); stack_socket_close(session, event_fd_data); } @@ -227,7 +242,10 @@ static void my_channel_eof_function(ssh_session session, ssh_channel channel, vo struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata; (void)session; - _ssh_log(SSH_LOG_PROTOCOL, "=== my_channel_eof_function", "Got EOF on channel %d:%d. Shuting down write on socket (fd = %d).", channel->local_channel, channel->remote_channel, *event_fd_data->p_fd); + _ssh_log(SSH_LOG_PROTOCOL, + "=== my_channel_eof_function", + "Got EOF on channel. Shuting down write on socket (fd = %d).", + *event_fd_data->p_fd); stack_socket_close(session, event_fd_data); } @@ -236,7 +254,10 @@ static void my_channel_exit_status_function(ssh_session session, ssh_channel cha struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata; (void)session; - _ssh_log(SSH_LOG_PROTOCOL, "=== my_channel_exit_status_function", "Got exit status %d on channel %d:%d fd = %d.", exit_status, channel->local_channel, channel->remote_channel, *event_fd_data->p_fd); + _ssh_log(SSH_LOG_PROTOCOL, + "=== my_channel_exit_status_function", + "Got exit status %d on channel fd = %d.", + exit_status, *event_fd_data->p_fd); } static int my_channel_data_function(ssh_session session, @@ -253,7 +274,11 @@ static int my_channel_data_function(ssh_session session, fprintf(stderr, "Why we're here? Stacked = %d\n", event_fd_data->stacked); } - _ssh_log(SSH_LOG_PROTOCOL, "=== my_channel_data_function", "%d bytes waiting on channel %d:%d for reading. Fd = %d",len, channel->local_channel, channel->remote_channel, *event_fd_data->p_fd); + _ssh_log(SSH_LOG_PROTOCOL, + "=== my_channel_data_function", + "%d bytes waiting on channel for reading. Fd = %d", + len, + *event_fd_data->p_fd); if (len > 0) { i = send(*event_fd_data->p_fd, data, len, 0); } @@ -313,8 +338,10 @@ static int my_fd_data_function(UNUSED_PARAM(socket_t fd), blocking = ssh_is_blocking(session); ssh_set_blocking(session, 0); - _ssh_log(SSH_LOG_FUNCTIONS, "=== my_fd_data_function", "Trying to read from tcp socket fd = %d... (Channel %d:%d state=%d)", - *event_fd_data->p_fd, channel->local_channel, channel->remote_channel, channel->state); + _ssh_log(SSH_LOG_FUNCTIONS, + "=== my_fd_data_function", + "Trying to read from tcp socket fd = %d", + *event_fd_data->p_fd); #ifdef _WIN32 struct sockaddr from; int fromlen = sizeof(from); diff --git a/include/libssh/CMakeLists.txt b/include/libssh/CMakeLists.txt index 85ffa6f..acc966e 100644 --- a/include/libssh/CMakeLists.txt +++ b/include/libssh/CMakeLists.txt @@ -26,7 +26,7 @@ install( FILES ${libssh_HDRS} DESTINATION - ${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME} + ${CMAKE_INSTALL_INCLUDEDIR}/${APPLICATION_NAME} COMPONENT headers ) diff --git a/include/libssh/agent.h b/include/libssh/agent.h index 0142f57..d4eefbb 100644 --- a/include/libssh/agent.h +++ b/include/libssh/agent.h @@ -104,7 +104,7 @@ void ssh_agent_free(struct ssh_agent_struct *agent); */ int ssh_agent_is_running(struct ssh_session_struct *session); -int ssh_agent_get_ident_count(struct ssh_session_struct *session); +uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session); ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, char **comment); diff --git a/include/libssh/bignum.h b/include/libssh/bignum.h index 3f82bc6..726ed7b 100644 --- a/include/libssh/bignum.h +++ b/include/libssh/bignum.h @@ -27,7 +27,7 @@ bignum ssh_make_string_bn(ssh_string string); ssh_string ssh_make_bignum_string(bignum num); -void ssh_print_bignum(const char *which, const bignum num); +void ssh_print_bignum(const char *which, const_bignum num); #endif /* BIGNUM_H_ */ diff --git a/include/libssh/bind.h b/include/libssh/bind.h index 4ca2376..6b5f19d 100644 --- a/include/libssh/bind.h +++ b/include/libssh/bind.h @@ -22,6 +22,7 @@ #define BIND_H_ #include "libssh/priv.h" +#include "libssh/kex.h" #include "libssh/session.h" struct ssh_bind_struct { @@ -31,7 +32,7 @@ struct ssh_bind_struct { struct ssh_poll_handle_struct *poll; /* options */ - char *wanted_methods[10]; + char *wanted_methods[SSH_KEX_METHODS]; char *banner; char *ecdsakey; char *dsakey; diff --git a/include/libssh/channels.h b/include/libssh/channels.h index fc03965..bbabcfd 100644 --- a/include/libssh/channels.h +++ b/include/libssh/channels.h @@ -97,8 +97,9 @@ SSH_PACKET_CALLBACK(channel_rcv_close); SSH_PACKET_CALLBACK(channel_rcv_request); SSH_PACKET_CALLBACK(channel_rcv_data); -int channel_default_bufferize(ssh_channel channel, void *data, int len, - int is_stderr); +int channel_default_bufferize(ssh_channel channel, + void *data, size_t len, + bool is_stderr); int ssh_channel_flush(ssh_channel channel); uint32_t ssh_channel_new_id(ssh_session session); ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id); diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h index 034b4f2..ede7166 100644 --- a/include/libssh/crypto.h +++ b/include/libssh/crypto.h @@ -80,6 +80,8 @@ enum ssh_key_exchange_e { SSH_KEX_DH_GROUP16_SHA512, /* diffie-hellman-group18-sha512 */ SSH_KEX_DH_GROUP18_SHA512, + /* diffie-hellman-group14-sha256 */ + SSH_KEX_DH_GROUP14_SHA256, }; enum ssh_cipher_e { @@ -105,7 +107,7 @@ struct ssh_crypto_struct { bignum shared_secret; struct dh_ctx *dh_ctx; #ifdef WITH_GEX - size_t dh_pmin; int dh_pn; int dh_pmax; /* preferred group parameters */ + size_t dh_pmin; size_t dh_pn; size_t dh_pmax; /* preferred group parameters */ #endif /* WITH_GEX */ #ifdef HAVE_ECDH #ifdef HAVE_OPENSSL_ECC diff --git a/include/libssh/dh.h b/include/libssh/dh.h index 48d8c46..390b30d 100644 --- a/include/libssh/dh.h +++ b/include/libssh/dh.h @@ -48,6 +48,8 @@ int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer, int ssh_dh_compute_shared_secret(struct dh_ctx *ctx, int local, int remote, bignum *dest); +void ssh_dh_debug_crypto(struct ssh_crypto_struct *c); + /* common functions */ int ssh_dh_init(void); void ssh_dh_finalize(void); diff --git a/include/libssh/fe25519.h b/include/libssh/fe25519.h index e959912..438d85d 100644 --- a/include/libssh/fe25519.h +++ b/include/libssh/fe25519.h @@ -39,7 +39,7 @@ void fe25519_unpack(fe25519 *r, const unsigned char x[32]); void fe25519_pack(unsigned char r[32], const fe25519 *x); -int fe25519_iszero(const fe25519 *x); +uint32_t fe25519_iszero(const fe25519 *x); int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y); diff --git a/include/libssh/keys.h b/include/libssh/keys.h index f25283a..7b13861 100644 --- a/include/libssh/keys.h +++ b/include/libssh/keys.h @@ -28,13 +28,13 @@ struct ssh_public_key_struct { int type; const char *type_c; /* Don't free it ! it is static */ -#ifdef HAVE_LIBGCRYPT +#if defined(HAVE_LIBGCRYPT) gcry_sexp_t dsa_pub; gcry_sexp_t rsa_pub; -#elif HAVE_LIBCRYPTO +#elif defined(HAVE_LIBCRYPTO) DSA *dsa_pub; RSA *rsa_pub; -#elif HAVE_LIBMBEDCRYPTO +#elif defined(HAVE_LIBMBEDCRYPTO) mbedtls_pk_context *rsa_pub; void *dsa_pub; #endif @@ -42,13 +42,13 @@ struct ssh_public_key_struct { struct ssh_private_key_struct { int type; -#ifdef HAVE_LIBGCRYPT +#if defined(HAVE_LIBGCRYPT) gcry_sexp_t dsa_priv; gcry_sexp_t rsa_priv; -#elif defined HAVE_LIBCRYPTO +#elif defined(HAVE_LIBCRYPTO) DSA *dsa_priv; RSA *rsa_priv; -#elif HAVE_LIBMBEDCRYPTO +#elif defined(HAVE_LIBMBEDCRYPTO) mbedtls_pk_context *rsa_priv; void *dsa_priv; #endif diff --git a/include/libssh/knownhosts.h b/include/libssh/knownhosts.h index dcaa6c2..44e434c 100644 --- a/include/libssh/knownhosts.h +++ b/include/libssh/knownhosts.h @@ -23,6 +23,7 @@ #define SSH_KNOWNHOSTS_H_ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session); +char *ssh_known_hosts_get_algorithms_names(ssh_session session); enum ssh_known_hosts_e ssh_session_get_known_hosts_entry_file(ssh_session session, const char *filename, diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h index 541912b..4117942 100644 --- a/include/libssh/libcrypto.h +++ b/include/libssh/libcrypto.h @@ -96,9 +96,9 @@ typedef BN_CTX* bignum_CTX; #define bignum_add(dest, a, b) BN_add(dest, a, b) #define bignum_sub(dest, a, b) BN_sub(dest, a, b) #define bignum_mod(dest, a, b, ctx) BN_mod(dest, a, b, ctx) -#define bignum_num_bytes(num) BN_num_bytes(num) -#define bignum_num_bits(num) BN_num_bits(num) -#define bignum_is_bit_set(num,bit) BN_is_bit_set(num,bit) +#define bignum_num_bytes(num) (size_t)BN_num_bytes(num) +#define bignum_num_bits(num) (size_t)BN_num_bits(num) +#define bignum_is_bit_set(num,bit) BN_is_bit_set(num, (int)bit) #define bignum_bn2bin(num,len, ptr) BN_bn2bin(num, ptr) #define bignum_cmp(num1,num2) BN_cmp(num1,num2) #define bignum_rshift1(dest, src) BN_rshift1(dest, src) @@ -112,7 +112,11 @@ typedef BN_CTX* bignum_CTX; /* Returns true if the OpenSSL is operating in FIPS mode */ +#ifdef HAVE_OPENSSL_FIPS_MODE #define ssh_fips_mode() (FIPS_mode() != 0) +#else +#define ssh_fips_mode() false +#endif #endif /* HAVE_LIBCRYPTO */ diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h index 795d33e..347d851 100644 --- a/include/libssh/libgcrypt.h +++ b/include/libssh/libgcrypt.h @@ -78,7 +78,10 @@ int ssh_gcry_rand_range(bignum rnd, bignum max); #define bignum_bin2bn(data,datalen,dest) gcry_mpi_scan(dest,GCRYMPI_FMT_USG,data,datalen,NULL) #define bignum_bn2dec(num) ssh_gcry_bn2dec(num) #define bignum_dec2bn(num, data) ssh_gcry_dec2bn(data, num) -#define bignum_bn2hex(num,data) gcry_mpi_aprint(GCRYMPI_FMT_HEX,data,NULL,num) + +#define bignum_bn2hex(num, data) \ + gcry_mpi_aprint(GCRYMPI_FMT_HEX, data, NULL, (const gcry_mpi_t)num) + #define bignum_hex2bn(data, num) (gcry_mpi_scan(num,GCRYMPI_FMT_HEX,data,0,NULL)==0?1:0) #define bignum_rand(num,bits) 1,gcry_mpi_randomize(num,bits,GCRY_STRONG_RANDOM),gcry_mpi_set_bit(num,bits-1),gcry_mpi_set_bit(num,0) #define bignum_mod_exp(dest,generator,exp,modulo, ctx) 1,gcry_mpi_powm(dest,generator,exp,modulo) diff --git a/include/libssh/libmbedcrypto.h b/include/libssh/libmbedcrypto.h index 00d5ba8..fe53019 100644 --- a/include/libssh/libmbedcrypto.h +++ b/include/libssh/libmbedcrypto.h @@ -75,7 +75,7 @@ struct mbedtls_ecdsa_sig { bignum ssh_mbedcry_bn_new(void); void ssh_mbedcry_bn_free(bignum num); -unsigned char *ssh_mbedcry_bn2num(bignum num, int radix); +unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix); int ssh_mbedcry_rand(bignum rnd, int bits, int top, int bottom); int ssh_mbedcry_is_bit_set(bignum num, size_t pos); int ssh_mbedcry_rand_range(bignum dest, bignum max); diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index c5f9df6..7903013 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -79,7 +79,7 @@ /* libssh version */ #define LIBSSH_VERSION_MAJOR 0 #define LIBSSH_VERSION_MINOR 9 -#define LIBSSH_VERSION_MICRO 0 +#define LIBSSH_VERSION_MICRO 3 #define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \ LIBSSH_VERSION_MINOR, \ @@ -168,13 +168,13 @@ enum ssh_auth_e { }; /* auth flags */ -#define SSH_AUTH_METHOD_UNKNOWN 0 -#define SSH_AUTH_METHOD_NONE 0x0001 -#define SSH_AUTH_METHOD_PASSWORD 0x0002 -#define SSH_AUTH_METHOD_PUBLICKEY 0x0004 -#define SSH_AUTH_METHOD_HOSTBASED 0x0008 -#define SSH_AUTH_METHOD_INTERACTIVE 0x0010 -#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020 +#define SSH_AUTH_METHOD_UNKNOWN 0x0000u +#define SSH_AUTH_METHOD_NONE 0x0001u +#define SSH_AUTH_METHOD_PASSWORD 0x0002u +#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u +#define SSH_AUTH_METHOD_HOSTBASED 0x0008u +#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u +#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u /* messages */ enum ssh_requests_e { @@ -441,6 +441,7 @@ enum ssh_scp_request_types { enum ssh_connector_flags_e { /** Only the standard stream of the channel */ SSH_CONNECTOR_STDOUT = 1, + SSH_CONNECTOR_STDINOUT = 1, /** Only the exception stream of the channel */ SSH_CONNECTOR_STDERR = 2, /** Merge both standard and exception streams */ @@ -632,7 +633,13 @@ LIBSSH_API ssh_pcap_file ssh_pcap_file_new(void); LIBSSH_API int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename); /** - * @brief SSH authentication callback. + * @addtogroup libssh_auth + * + * @{ + */ + +/** + * @brief SSH authentication callback for password and publickey auth. * * @param prompt Prompt to be displayed. * @param buf Buffer to save the password. You should null-terminate it. @@ -647,6 +654,8 @@ LIBSSH_API int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename); typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata); +/** @} */ + LIBSSH_API ssh_key ssh_key_new(void); #define SSH_KEY_FREE(x) \ do { if ((x) != NULL) { ssh_key_free(x); x = NULL; } } while(0) diff --git a/include/libssh/messages.h b/include/libssh/messages.h index fd40b9f..04d041d 100644 --- a/include/libssh/messages.h +++ b/include/libssh/messages.h @@ -28,7 +28,7 @@ struct ssh_auth_request { int method; char *password; struct ssh_key_struct *pubkey; - char signature_state; + enum ssh_publickey_state_e signature_state; char kbdint_response; }; diff --git a/include/libssh/misc.h b/include/libssh/misc.h index 94da397..3cc3b11 100644 --- a/include/libssh/misc.h +++ b/include/libssh/misc.h @@ -26,6 +26,7 @@ char *ssh_get_user_home_dir(void); char *ssh_get_local_username(void); int ssh_file_readaccess_ok(const char *file); +int ssh_dir_writeable(const char *path); char *ssh_path_expand_tilde(const char *d); char *ssh_path_expand_escape(ssh_session session, const char *s); @@ -50,6 +51,12 @@ struct ssh_timestamp { long useconds; }; +enum ssh_quote_state_e { + NO_QUOTE, + SINGLE_QUOTE, + DOUBLE_QUOTE +}; + struct ssh_list *ssh_list_new(void); void ssh_list_free(struct ssh_list *list); struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list); @@ -83,4 +90,11 @@ int ssh_match_group(const char *group, const char *object); void uint64_inc(unsigned char *counter); +void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len); + +int ssh_mkdirs(const char *pathname, mode_t mode); + +int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len); +int ssh_newline_vis(const char *string, char *buf, size_t buf_len); + #endif /* MISC_H_ */ diff --git a/include/libssh/pki.h b/include/libssh/pki.h index 57736f8..9b10434 100644 --- a/include/libssh/pki.h +++ b/include/libssh/pki.h @@ -30,7 +30,15 @@ #endif #include "libssh/crypto.h" +#ifdef HAVE_OPENSSL_ED25519 +/* If using OpenSSL implementation, define the signature lenght which would be + * defined in libssh/ed25519.h otherwise */ +#define ED25519_SIG_LEN 64 +#else #include "libssh/ed25519.h" +#endif +/* This definition is used for both OpenSSL and internal implementations */ +#define ED25519_KEY_LEN 32 #define MAX_PUBKEY_SIZE 0x100000 /* 1M */ #define MAX_PRIVKEY_SIZE 0x400000 /* 4M */ @@ -61,8 +69,13 @@ struct ssh_key_struct { void *ecdsa; # endif /* HAVE_OPENSSL_EC_H */ #endif /* HAVE_LIBGCRYPT */ +#ifdef HAVE_OPENSSL_ED25519 + uint8_t *ed25519_pubkey; + uint8_t *ed25519_privkey; +#else ed25519_pubkey *ed25519_pubkey; ed25519_privkey *ed25519_privkey; +#endif void *cert; enum ssh_keytypes_e cert_type; }; @@ -79,7 +92,9 @@ struct ssh_signature_struct { ssh_string rsa_sig; struct mbedtls_ecdsa_sig ecdsa_sig; #endif /* HAVE_LIBGCRYPT */ +#ifndef HAVE_OPENSSL_ED25519 ed25519_signature *ed25519_sig; +#endif ssh_string raw_sig; }; @@ -96,6 +111,7 @@ enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name); enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type); enum ssh_digest_e ssh_key_type_to_hash(ssh_session session, enum ssh_keytypes_e type); +enum ssh_digest_e ssh_key_hash_from_name(const char *name); #define is_ecdsa_key_type(t) \ ((t) >= SSH_KEYTYPE_ECDSA_P256 && (t) <= SSH_KEYTYPE_ECDSA_P521) @@ -118,7 +134,7 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob, int ssh_pki_signature_verify(ssh_session session, ssh_signature sig, const ssh_key key, - unsigned char *digest, + const unsigned char *digest, size_t dlen); /* SSH Public Key Functions */ @@ -138,7 +154,8 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session, struct ssh_buffer_struct *buf, const ssh_key pubkey); ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, - const ssh_key privkey); + const ssh_key privkey, + const enum ssh_digest_e digest); /* Temporary functions, to be removed after migration to ssh_key */ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key); diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h index 39a22ca..d365a2d 100644 --- a/include/libssh/pki_priv.h +++ b/include/libssh/pki_priv.h @@ -124,11 +124,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, const ssh_string sig_blob, enum ssh_keytypes_e type, enum ssh_digest_e hash_type); -int pki_signature_verify(ssh_session session, - const ssh_signature sig, - const ssh_key key, - const unsigned char *input, - size_t input_len); /* SSH Signing Functions */ ssh_signature pki_do_sign(const ssh_key privkey, @@ -148,8 +143,8 @@ int pki_ed25519_key_cmp(const ssh_key k1, enum ssh_keycmp_e what); int pki_ed25519_key_dup(ssh_key new, const ssh_key key); int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key); -ssh_string pki_ed25519_sig_to_blob(ssh_signature sig); -int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob); +ssh_string pki_ed25519_signature_to_blob(ssh_signature sig); +int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob); int pki_privkey_build_ed25519(ssh_key key, ssh_string pubkey, ssh_string privkey); diff --git a/include/libssh/priv.h b/include/libssh/priv.h index ce74465..31405d6 100644 --- a/include/libssh/priv.h +++ b/include/libssh/priv.h @@ -221,7 +221,17 @@ int gettimeofday(struct timeval *__p, void *__t); struct ssh_common_struct; struct ssh_kex_struct; -int ssh_get_key_params(ssh_session session, ssh_key *privkey); +enum ssh_digest_e { + SSH_DIGEST_AUTO=0, + SSH_DIGEST_SHA1=1, + SSH_DIGEST_SHA256, + SSH_DIGEST_SHA384, + SSH_DIGEST_SHA512, +}; + +int ssh_get_key_params(ssh_session session, + ssh_key *privkey, + enum ssh_digest_e *digest); /* LOGGING */ void ssh_log_function(int verbosity, @@ -277,7 +287,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, /* in base64.c */ ssh_buffer base64_to_bin(const char *source); -unsigned char *bin_to_base64(const unsigned char *source, int len); +uint8_t *bin_to_base64(const uint8_t *source, size_t len); /* gzip.c */ int compress_buffer(ssh_session session,ssh_buffer buf); @@ -395,20 +405,20 @@ void explicit_bzero(void *s, size_t n); # endif /* HAVE_FALLTHROUGH_ATTRIBUTE */ #endif /* FALL_THROUGH */ -#ifndef __unused__ +#ifndef __attr_unused__ # ifdef HAVE_UNUSED_ATTRIBUTE -# define __unused__ __attribute__((unused)) +# define __attr_unused__ __attribute__((unused)) # else /* HAVE_UNUSED_ATTRIBUTE */ -# define __unused__ +# define __attr_unused__ # endif /* HAVE_UNUSED_ATTRIBUTE */ -#endif /* __unused__ */ +#endif /* __attr_unused__ */ #ifndef UNUSED_PARAM -#define UNUSED_PARAM(param) param __unused__ +#define UNUSED_PARAM(param) param __attr_unused__ #endif /* UNUSED_PARAM */ #ifndef UNUSED_VAR -#define UNUSED_VAR(var) __unused__ var +#define UNUSED_VAR(var) __attr_unused__ var #endif /* UNUSED_VAR */ void ssh_agent_state_free(void *data); diff --git a/include/libssh/session.h b/include/libssh/session.h index dfcb0cd..2225615 100644 --- a/include/libssh/session.h +++ b/include/libssh/session.h @@ -145,7 +145,7 @@ struct ssh_session_struct { /* where it was before being interrupted */ enum ssh_pending_call_e pending_call_state; enum ssh_session_state_e session_state; - int packet_state; + enum ssh_packet_state_e packet_state; enum ssh_dh_state_e dh_handshake_state; enum ssh_channel_request_state_e global_req_state; struct ssh_agent_state_struct *agent_state; @@ -188,6 +188,7 @@ struct ssh_session_struct { ssh_key ed25519_key; /* The type of host key wanted by client */ enum ssh_keytypes_e hostkey; + enum ssh_digest_e hostkey_digest; } srv; /* auths accepted by server */ @@ -212,7 +213,7 @@ struct ssh_session_struct { char *sshdir; char *knownhosts; char *global_knownhosts; - char *wanted_methods[10]; + char *wanted_methods[SSH_KEX_METHODS]; char *pubkey_accepted_types; char *ProxyCommand; char *custombanner; diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h index 774a4ba..8c14b21 100644 --- a/include/libssh/sftp.h +++ b/include/libssh/sftp.h @@ -201,13 +201,18 @@ struct sftp_statvfs_struct { }; /** - * @brief Start a new sftp session. + * @brief Creates a new sftp session. + * + * This function creates a new sftp session and allocates a new sftp channel + * with the server inside of the provided ssh session. This function call is + * usually followed by the sftp_init(), which initializes SFTP protocol itself. * * @param session The ssh session to use. * * @return A new sftp session or NULL on error. * * @see sftp_free() + * @see sftp_init() */ LIBSSH_API sftp_session sftp_new(ssh_session session); @@ -232,7 +237,10 @@ LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channe LIBSSH_API void sftp_free(sftp_session sftp); /** - * @brief Initialize the sftp session with the server. + * @brief Initialize the sftp protocol with the server. + * + * This function involves the SFTP protocol initialization (as described + * in the SFTP specification), including the version and extensions negotiation. * * @param sftp The sftp session to initialize. * @@ -862,13 +870,6 @@ LIBSSH_API int sftp_server_init(sftp_session sftp); LIBSSH_API void sftp_server_free(sftp_session sftp); #endif /* WITH_SERVER */ -/* this is not a public interface */ -#define SFTP_HANDLES 256 -sftp_packet sftp_packet_read(sftp_session sftp); -int sftp_packet_write(sftp_session sftp,uint8_t type, ssh_buffer payload); -void sftp_packet_free(sftp_packet packet); -int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr); -sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf,int expectname); /* sftpserver.c */ LIBSSH_API sftp_client_message sftp_get_client_message(sftp_session sftp); diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h new file mode 100644 index 0000000..8392519 --- /dev/null +++ b/include/libssh/sftp_priv.h @@ -0,0 +1,32 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SFTP_PRIV_H +#define SFTP_PRIV_H + +sftp_packet sftp_packet_read(sftp_session sftp); +ssize_t sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload); +void sftp_packet_free(sftp_packet packet); +int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr); +sftp_attributes sftp_parse_attr(sftp_session session, + ssh_buffer buf, + int expectname); + +#endif /* SFTP_PRIV_H */ diff --git a/include/libssh/socket.h b/include/libssh/socket.h index 5c296e0..5e345c6 100644 --- a/include/libssh/socket.h +++ b/include/libssh/socket.h @@ -63,6 +63,9 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks); int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s); struct ssh_poll_handle_struct * ssh_socket_get_poll_handle(ssh_socket s); -int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr); +int ssh_socket_connect(ssh_socket s, + const char *host, + uint16_t port, + const char *bind_addr); #endif /* SOCKET_H_ */ diff --git a/include/libssh/token.h b/include/libssh/token.h index 7b24418..9896fb0 100644 --- a/include/libssh/token.h +++ b/include/libssh/token.h @@ -38,7 +38,11 @@ void ssh_tokens_free(struct ssh_tokens_st *tokens); char *ssh_find_matching(const char *available_d, const char *preferred_d); - char *ssh_find_all_matching(const char *available_d, const char *preferred_d); + +char *ssh_remove_duplicates(const char *list); + +char *ssh_append_without_duplicates(const char *list, + const char *appended_list); #endif /* TOKEN_H_ */ diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h index 1c18f28..ba64939 100644 --- a/include/libssh/wrapper.h +++ b/include/libssh/wrapper.h @@ -29,14 +29,6 @@ #include "libssh/libgcrypt.h" #include "libssh/libmbedcrypto.h" -enum ssh_digest_e { - SSH_DIGEST_AUTO=0, - SSH_DIGEST_SHA1=1, - SSH_DIGEST_SHA256, - SSH_DIGEST_SHA384, - SSH_DIGEST_SHA512, -}; - enum ssh_kdf_digest { SSH_KDF_SHA1=1, SSH_KDF_SHA256, diff --git a/libssh.pc.cmake b/libssh.pc.cmake index 48fbf7f..b37cb3f 100644 --- a/libssh.pc.cmake +++ b/libssh.pc.cmake @@ -1,6 +1,6 @@ Name: ${PROJECT_NAME} Description: The SSH Library Version: ${PROJECT_VERSION} -Libs: -L${LIB_INSTALL_DIR} -lssh -Cflags: -I${INCLUDE_INSTALL_DIR} +Libs: -L${CMAKE_INSTALL_FULL_LIBDIR} -lssh +Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR} diff --git a/src/ABI/current b/src/ABI/current index 2871567..dc24a89 100644 --- a/src/ABI/current +++ b/src/ABI/current @@ -1 +1 @@ -4.8.1 \ No newline at end of file +4.8.5 \ No newline at end of file diff --git a/src/ABI/libssh-4.8.2.symbols b/src/ABI/libssh-4.8.2.symbols new file mode 100644 index 0000000..dce4add --- /dev/null +++ b/src/ABI/libssh-4.8.2.symbols @@ -0,0 +1,421 @@ +_ssh_log +buffer_free +buffer_get +buffer_get_len +buffer_new +channel_accept_x11 +channel_change_pty_size +channel_close +channel_forward_accept +channel_forward_cancel +channel_forward_listen +channel_free +channel_get_exit_status +channel_get_session +channel_is_closed +channel_is_eof +channel_is_open +channel_new +channel_open_forward +channel_open_session +channel_poll +channel_read +channel_read_buffer +channel_read_nonblocking +channel_request_env +channel_request_exec +channel_request_pty +channel_request_pty_size +channel_request_send_signal +channel_request_sftp +channel_request_shell +channel_request_subsystem +channel_request_x11 +channel_select +channel_send_eof +channel_set_blocking +channel_write +channel_write_stderr +privatekey_free +privatekey_from_file +publickey_free +publickey_from_file +publickey_from_privatekey +publickey_to_string +sftp_async_read +sftp_async_read_begin +sftp_attributes_free +sftp_canonicalize_path +sftp_chmod +sftp_chown +sftp_client_message_free +sftp_client_message_get_data +sftp_client_message_get_filename +sftp_client_message_get_flags +sftp_client_message_get_submessage +sftp_client_message_get_type +sftp_client_message_set_filename +sftp_close +sftp_closedir +sftp_dir_eof +sftp_extension_supported +sftp_extensions_get_count +sftp_extensions_get_data +sftp_extensions_get_name +sftp_file_set_blocking +sftp_file_set_nonblocking +sftp_free +sftp_fstat +sftp_fstatvfs +sftp_fsync +sftp_get_client_message +sftp_get_error +sftp_handle +sftp_handle_alloc +sftp_handle_remove +sftp_init +sftp_lstat +sftp_mkdir +sftp_new +sftp_new_channel +sftp_open +sftp_opendir +sftp_read +sftp_readdir +sftp_readlink +sftp_rename +sftp_reply_attr +sftp_reply_data +sftp_reply_handle +sftp_reply_name +sftp_reply_names +sftp_reply_names_add +sftp_reply_status +sftp_rewind +sftp_rmdir +sftp_seek +sftp_seek64 +sftp_send_client_message +sftp_server_free +sftp_server_init +sftp_server_new +sftp_server_version +sftp_setstat +sftp_stat +sftp_statvfs +sftp_statvfs_free +sftp_symlink +sftp_tell +sftp_tell64 +sftp_unlink +sftp_utimes +sftp_write +ssh_accept +ssh_add_channel_callbacks +ssh_auth_list +ssh_basename +ssh_bind_accept +ssh_bind_accept_fd +ssh_bind_fd_toaccept +ssh_bind_free +ssh_bind_get_fd +ssh_bind_listen +ssh_bind_new +ssh_bind_options_parse_config +ssh_bind_options_set +ssh_bind_set_blocking +ssh_bind_set_callbacks +ssh_bind_set_fd +ssh_blocking_flush +ssh_buffer_add_data +ssh_buffer_free +ssh_buffer_get +ssh_buffer_get_data +ssh_buffer_get_len +ssh_buffer_new +ssh_buffer_reinit +ssh_channel_accept_forward +ssh_channel_accept_x11 +ssh_channel_cancel_forward +ssh_channel_change_pty_size +ssh_channel_close +ssh_channel_free +ssh_channel_get_exit_status +ssh_channel_get_session +ssh_channel_is_closed +ssh_channel_is_eof +ssh_channel_is_open +ssh_channel_listen_forward +ssh_channel_new +ssh_channel_open_auth_agent +ssh_channel_open_forward +ssh_channel_open_forward_unix +ssh_channel_open_reverse_forward +ssh_channel_open_session +ssh_channel_open_x11 +ssh_channel_poll +ssh_channel_poll_timeout +ssh_channel_read +ssh_channel_read_nonblocking +ssh_channel_read_timeout +ssh_channel_request_auth_agent +ssh_channel_request_env +ssh_channel_request_exec +ssh_channel_request_pty +ssh_channel_request_pty_size +ssh_channel_request_send_break +ssh_channel_request_send_exit_signal +ssh_channel_request_send_exit_status +ssh_channel_request_send_signal +ssh_channel_request_sftp +ssh_channel_request_shell +ssh_channel_request_subsystem +ssh_channel_request_x11 +ssh_channel_select +ssh_channel_send_eof +ssh_channel_set_blocking +ssh_channel_set_counter +ssh_channel_window_size +ssh_channel_write +ssh_channel_write_stderr +ssh_clean_pubkey_hash +ssh_connect +ssh_connector_free +ssh_connector_new +ssh_connector_set_in_channel +ssh_connector_set_in_fd +ssh_connector_set_out_channel +ssh_connector_set_out_fd +ssh_copyright +ssh_dirname +ssh_disconnect +ssh_dump_knownhost +ssh_event_add_connector +ssh_event_add_fd +ssh_event_add_session +ssh_event_dopoll +ssh_event_free +ssh_event_new +ssh_event_remove_connector +ssh_event_remove_fd +ssh_event_remove_session +ssh_execute_message_callbacks +ssh_finalize +ssh_forward_accept +ssh_forward_cancel +ssh_forward_listen +ssh_free +ssh_get_cipher_in +ssh_get_cipher_out +ssh_get_clientbanner +ssh_get_disconnect_message +ssh_get_error +ssh_get_error_code +ssh_get_fd +ssh_get_fingerprint_hash +ssh_get_hexa +ssh_get_hmac_in +ssh_get_hmac_out +ssh_get_issue_banner +ssh_get_kex_algo +ssh_get_log_callback +ssh_get_log_level +ssh_get_log_userdata +ssh_get_openssh_version +ssh_get_poll_flags +ssh_get_pubkey +ssh_get_pubkey_hash +ssh_get_publickey +ssh_get_publickey_hash +ssh_get_random +ssh_get_server_publickey +ssh_get_serverbanner +ssh_get_status +ssh_get_version +ssh_getpass +ssh_gssapi_get_creds +ssh_gssapi_set_creds +ssh_handle_key_exchange +ssh_init +ssh_is_blocking +ssh_is_connected +ssh_is_server_known +ssh_key_cmp +ssh_key_free +ssh_key_is_private +ssh_key_is_public +ssh_key_new +ssh_key_type +ssh_key_type_from_name +ssh_key_type_to_char +ssh_known_hosts_parse_line +ssh_knownhosts_entry_free +ssh_log +ssh_message_auth_interactive_request +ssh_message_auth_kbdint_is_response +ssh_message_auth_password +ssh_message_auth_pubkey +ssh_message_auth_publickey +ssh_message_auth_publickey_state +ssh_message_auth_reply_pk_ok +ssh_message_auth_reply_pk_ok_simple +ssh_message_auth_reply_success +ssh_message_auth_set_methods +ssh_message_auth_user +ssh_message_channel_request_channel +ssh_message_channel_request_command +ssh_message_channel_request_env_name +ssh_message_channel_request_env_value +ssh_message_channel_request_open_destination +ssh_message_channel_request_open_destination_port +ssh_message_channel_request_open_originator +ssh_message_channel_request_open_originator_port +ssh_message_channel_request_open_reply_accept +ssh_message_channel_request_open_reply_accept_channel +ssh_message_channel_request_pty_height +ssh_message_channel_request_pty_pxheight +ssh_message_channel_request_pty_pxwidth +ssh_message_channel_request_pty_term +ssh_message_channel_request_pty_width +ssh_message_channel_request_reply_success +ssh_message_channel_request_subsystem +ssh_message_channel_request_x11_auth_cookie +ssh_message_channel_request_x11_auth_protocol +ssh_message_channel_request_x11_screen_number +ssh_message_channel_request_x11_single_connection +ssh_message_free +ssh_message_get +ssh_message_global_request_address +ssh_message_global_request_port +ssh_message_global_request_reply_success +ssh_message_reply_default +ssh_message_retrieve +ssh_message_service_reply_success +ssh_message_service_service +ssh_message_subtype +ssh_message_type +ssh_mkdir +ssh_new +ssh_options_copy +ssh_options_get +ssh_options_get_port +ssh_options_getopt +ssh_options_parse_config +ssh_options_set +ssh_pcap_file_close +ssh_pcap_file_free +ssh_pcap_file_new +ssh_pcap_file_open +ssh_pki_copy_cert_to_privkey +ssh_pki_export_privkey_base64 +ssh_pki_export_privkey_file +ssh_pki_export_privkey_to_pubkey +ssh_pki_export_pubkey_base64 +ssh_pki_export_pubkey_file +ssh_pki_generate +ssh_pki_import_cert_base64 +ssh_pki_import_cert_file +ssh_pki_import_privkey_base64 +ssh_pki_import_privkey_file +ssh_pki_import_pubkey_base64 +ssh_pki_import_pubkey_file +ssh_pki_key_ecdsa_name +ssh_print_hash +ssh_print_hexa +ssh_privatekey_type +ssh_publickey_to_file +ssh_remove_channel_callbacks +ssh_scp_accept_request +ssh_scp_close +ssh_scp_deny_request +ssh_scp_free +ssh_scp_init +ssh_scp_leave_directory +ssh_scp_new +ssh_scp_pull_request +ssh_scp_push_directory +ssh_scp_push_file +ssh_scp_push_file64 +ssh_scp_read +ssh_scp_request_get_filename +ssh_scp_request_get_permissions +ssh_scp_request_get_size +ssh_scp_request_get_size64 +ssh_scp_request_get_warning +ssh_scp_write +ssh_select +ssh_send_debug +ssh_send_ignore +ssh_send_keepalive +ssh_server_init_kex +ssh_service_request +ssh_session_export_known_hosts_entry +ssh_session_get_known_hosts_entry +ssh_session_has_known_hosts_entry +ssh_session_is_known_server +ssh_session_update_known_hosts +ssh_set_agent_channel +ssh_set_agent_socket +ssh_set_auth_methods +ssh_set_blocking +ssh_set_callbacks +ssh_set_channel_callbacks +ssh_set_counters +ssh_set_fd_except +ssh_set_fd_toread +ssh_set_fd_towrite +ssh_set_log_callback +ssh_set_log_level +ssh_set_log_userdata +ssh_set_message_callback +ssh_set_pcap_file +ssh_set_server_callbacks +ssh_silent_disconnect +ssh_string_burn +ssh_string_copy +ssh_string_data +ssh_string_fill +ssh_string_free +ssh_string_free_char +ssh_string_from_char +ssh_string_get_char +ssh_string_len +ssh_string_new +ssh_string_to_char +ssh_threads_get_default +ssh_threads_get_noop +ssh_threads_get_pthread +ssh_threads_set_callbacks +ssh_try_publickey_from_file +ssh_userauth_agent +ssh_userauth_agent_pubkey +ssh_userauth_autopubkey +ssh_userauth_gssapi +ssh_userauth_kbdint +ssh_userauth_kbdint_getanswer +ssh_userauth_kbdint_getinstruction +ssh_userauth_kbdint_getname +ssh_userauth_kbdint_getnanswers +ssh_userauth_kbdint_getnprompts +ssh_userauth_kbdint_getprompt +ssh_userauth_kbdint_setanswer +ssh_userauth_list +ssh_userauth_none +ssh_userauth_offer_pubkey +ssh_userauth_password +ssh_userauth_privatekey_file +ssh_userauth_pubkey +ssh_userauth_publickey +ssh_userauth_publickey_auto +ssh_userauth_try_publickey +ssh_version +ssh_write_knownhost +string_burn +string_copy +string_data +string_fill +string_free +string_from_char +string_len +string_new +string_to_char \ No newline at end of file diff --git a/src/ABI/libssh-4.8.3.symbols b/src/ABI/libssh-4.8.3.symbols new file mode 100644 index 0000000..dce4add --- /dev/null +++ b/src/ABI/libssh-4.8.3.symbols @@ -0,0 +1,421 @@ +_ssh_log +buffer_free +buffer_get +buffer_get_len +buffer_new +channel_accept_x11 +channel_change_pty_size +channel_close +channel_forward_accept +channel_forward_cancel +channel_forward_listen +channel_free +channel_get_exit_status +channel_get_session +channel_is_closed +channel_is_eof +channel_is_open +channel_new +channel_open_forward +channel_open_session +channel_poll +channel_read +channel_read_buffer +channel_read_nonblocking +channel_request_env +channel_request_exec +channel_request_pty +channel_request_pty_size +channel_request_send_signal +channel_request_sftp +channel_request_shell +channel_request_subsystem +channel_request_x11 +channel_select +channel_send_eof +channel_set_blocking +channel_write +channel_write_stderr +privatekey_free +privatekey_from_file +publickey_free +publickey_from_file +publickey_from_privatekey +publickey_to_string +sftp_async_read +sftp_async_read_begin +sftp_attributes_free +sftp_canonicalize_path +sftp_chmod +sftp_chown +sftp_client_message_free +sftp_client_message_get_data +sftp_client_message_get_filename +sftp_client_message_get_flags +sftp_client_message_get_submessage +sftp_client_message_get_type +sftp_client_message_set_filename +sftp_close +sftp_closedir +sftp_dir_eof +sftp_extension_supported +sftp_extensions_get_count +sftp_extensions_get_data +sftp_extensions_get_name +sftp_file_set_blocking +sftp_file_set_nonblocking +sftp_free +sftp_fstat +sftp_fstatvfs +sftp_fsync +sftp_get_client_message +sftp_get_error +sftp_handle +sftp_handle_alloc +sftp_handle_remove +sftp_init +sftp_lstat +sftp_mkdir +sftp_new +sftp_new_channel +sftp_open +sftp_opendir +sftp_read +sftp_readdir +sftp_readlink +sftp_rename +sftp_reply_attr +sftp_reply_data +sftp_reply_handle +sftp_reply_name +sftp_reply_names +sftp_reply_names_add +sftp_reply_status +sftp_rewind +sftp_rmdir +sftp_seek +sftp_seek64 +sftp_send_client_message +sftp_server_free +sftp_server_init +sftp_server_new +sftp_server_version +sftp_setstat +sftp_stat +sftp_statvfs +sftp_statvfs_free +sftp_symlink +sftp_tell +sftp_tell64 +sftp_unlink +sftp_utimes +sftp_write +ssh_accept +ssh_add_channel_callbacks +ssh_auth_list +ssh_basename +ssh_bind_accept +ssh_bind_accept_fd +ssh_bind_fd_toaccept +ssh_bind_free +ssh_bind_get_fd +ssh_bind_listen +ssh_bind_new +ssh_bind_options_parse_config +ssh_bind_options_set +ssh_bind_set_blocking +ssh_bind_set_callbacks +ssh_bind_set_fd +ssh_blocking_flush +ssh_buffer_add_data +ssh_buffer_free +ssh_buffer_get +ssh_buffer_get_data +ssh_buffer_get_len +ssh_buffer_new +ssh_buffer_reinit +ssh_channel_accept_forward +ssh_channel_accept_x11 +ssh_channel_cancel_forward +ssh_channel_change_pty_size +ssh_channel_close +ssh_channel_free +ssh_channel_get_exit_status +ssh_channel_get_session +ssh_channel_is_closed +ssh_channel_is_eof +ssh_channel_is_open +ssh_channel_listen_forward +ssh_channel_new +ssh_channel_open_auth_agent +ssh_channel_open_forward +ssh_channel_open_forward_unix +ssh_channel_open_reverse_forward +ssh_channel_open_session +ssh_channel_open_x11 +ssh_channel_poll +ssh_channel_poll_timeout +ssh_channel_read +ssh_channel_read_nonblocking +ssh_channel_read_timeout +ssh_channel_request_auth_agent +ssh_channel_request_env +ssh_channel_request_exec +ssh_channel_request_pty +ssh_channel_request_pty_size +ssh_channel_request_send_break +ssh_channel_request_send_exit_signal +ssh_channel_request_send_exit_status +ssh_channel_request_send_signal +ssh_channel_request_sftp +ssh_channel_request_shell +ssh_channel_request_subsystem +ssh_channel_request_x11 +ssh_channel_select +ssh_channel_send_eof +ssh_channel_set_blocking +ssh_channel_set_counter +ssh_channel_window_size +ssh_channel_write +ssh_channel_write_stderr +ssh_clean_pubkey_hash +ssh_connect +ssh_connector_free +ssh_connector_new +ssh_connector_set_in_channel +ssh_connector_set_in_fd +ssh_connector_set_out_channel +ssh_connector_set_out_fd +ssh_copyright +ssh_dirname +ssh_disconnect +ssh_dump_knownhost +ssh_event_add_connector +ssh_event_add_fd +ssh_event_add_session +ssh_event_dopoll +ssh_event_free +ssh_event_new +ssh_event_remove_connector +ssh_event_remove_fd +ssh_event_remove_session +ssh_execute_message_callbacks +ssh_finalize +ssh_forward_accept +ssh_forward_cancel +ssh_forward_listen +ssh_free +ssh_get_cipher_in +ssh_get_cipher_out +ssh_get_clientbanner +ssh_get_disconnect_message +ssh_get_error +ssh_get_error_code +ssh_get_fd +ssh_get_fingerprint_hash +ssh_get_hexa +ssh_get_hmac_in +ssh_get_hmac_out +ssh_get_issue_banner +ssh_get_kex_algo +ssh_get_log_callback +ssh_get_log_level +ssh_get_log_userdata +ssh_get_openssh_version +ssh_get_poll_flags +ssh_get_pubkey +ssh_get_pubkey_hash +ssh_get_publickey +ssh_get_publickey_hash +ssh_get_random +ssh_get_server_publickey +ssh_get_serverbanner +ssh_get_status +ssh_get_version +ssh_getpass +ssh_gssapi_get_creds +ssh_gssapi_set_creds +ssh_handle_key_exchange +ssh_init +ssh_is_blocking +ssh_is_connected +ssh_is_server_known +ssh_key_cmp +ssh_key_free +ssh_key_is_private +ssh_key_is_public +ssh_key_new +ssh_key_type +ssh_key_type_from_name +ssh_key_type_to_char +ssh_known_hosts_parse_line +ssh_knownhosts_entry_free +ssh_log +ssh_message_auth_interactive_request +ssh_message_auth_kbdint_is_response +ssh_message_auth_password +ssh_message_auth_pubkey +ssh_message_auth_publickey +ssh_message_auth_publickey_state +ssh_message_auth_reply_pk_ok +ssh_message_auth_reply_pk_ok_simple +ssh_message_auth_reply_success +ssh_message_auth_set_methods +ssh_message_auth_user +ssh_message_channel_request_channel +ssh_message_channel_request_command +ssh_message_channel_request_env_name +ssh_message_channel_request_env_value +ssh_message_channel_request_open_destination +ssh_message_channel_request_open_destination_port +ssh_message_channel_request_open_originator +ssh_message_channel_request_open_originator_port +ssh_message_channel_request_open_reply_accept +ssh_message_channel_request_open_reply_accept_channel +ssh_message_channel_request_pty_height +ssh_message_channel_request_pty_pxheight +ssh_message_channel_request_pty_pxwidth +ssh_message_channel_request_pty_term +ssh_message_channel_request_pty_width +ssh_message_channel_request_reply_success +ssh_message_channel_request_subsystem +ssh_message_channel_request_x11_auth_cookie +ssh_message_channel_request_x11_auth_protocol +ssh_message_channel_request_x11_screen_number +ssh_message_channel_request_x11_single_connection +ssh_message_free +ssh_message_get +ssh_message_global_request_address +ssh_message_global_request_port +ssh_message_global_request_reply_success +ssh_message_reply_default +ssh_message_retrieve +ssh_message_service_reply_success +ssh_message_service_service +ssh_message_subtype +ssh_message_type +ssh_mkdir +ssh_new +ssh_options_copy +ssh_options_get +ssh_options_get_port +ssh_options_getopt +ssh_options_parse_config +ssh_options_set +ssh_pcap_file_close +ssh_pcap_file_free +ssh_pcap_file_new +ssh_pcap_file_open +ssh_pki_copy_cert_to_privkey +ssh_pki_export_privkey_base64 +ssh_pki_export_privkey_file +ssh_pki_export_privkey_to_pubkey +ssh_pki_export_pubkey_base64 +ssh_pki_export_pubkey_file +ssh_pki_generate +ssh_pki_import_cert_base64 +ssh_pki_import_cert_file +ssh_pki_import_privkey_base64 +ssh_pki_import_privkey_file +ssh_pki_import_pubkey_base64 +ssh_pki_import_pubkey_file +ssh_pki_key_ecdsa_name +ssh_print_hash +ssh_print_hexa +ssh_privatekey_type +ssh_publickey_to_file +ssh_remove_channel_callbacks +ssh_scp_accept_request +ssh_scp_close +ssh_scp_deny_request +ssh_scp_free +ssh_scp_init +ssh_scp_leave_directory +ssh_scp_new +ssh_scp_pull_request +ssh_scp_push_directory +ssh_scp_push_file +ssh_scp_push_file64 +ssh_scp_read +ssh_scp_request_get_filename +ssh_scp_request_get_permissions +ssh_scp_request_get_size +ssh_scp_request_get_size64 +ssh_scp_request_get_warning +ssh_scp_write +ssh_select +ssh_send_debug +ssh_send_ignore +ssh_send_keepalive +ssh_server_init_kex +ssh_service_request +ssh_session_export_known_hosts_entry +ssh_session_get_known_hosts_entry +ssh_session_has_known_hosts_entry +ssh_session_is_known_server +ssh_session_update_known_hosts +ssh_set_agent_channel +ssh_set_agent_socket +ssh_set_auth_methods +ssh_set_blocking +ssh_set_callbacks +ssh_set_channel_callbacks +ssh_set_counters +ssh_set_fd_except +ssh_set_fd_toread +ssh_set_fd_towrite +ssh_set_log_callback +ssh_set_log_level +ssh_set_log_userdata +ssh_set_message_callback +ssh_set_pcap_file +ssh_set_server_callbacks +ssh_silent_disconnect +ssh_string_burn +ssh_string_copy +ssh_string_data +ssh_string_fill +ssh_string_free +ssh_string_free_char +ssh_string_from_char +ssh_string_get_char +ssh_string_len +ssh_string_new +ssh_string_to_char +ssh_threads_get_default +ssh_threads_get_noop +ssh_threads_get_pthread +ssh_threads_set_callbacks +ssh_try_publickey_from_file +ssh_userauth_agent +ssh_userauth_agent_pubkey +ssh_userauth_autopubkey +ssh_userauth_gssapi +ssh_userauth_kbdint +ssh_userauth_kbdint_getanswer +ssh_userauth_kbdint_getinstruction +ssh_userauth_kbdint_getname +ssh_userauth_kbdint_getnanswers +ssh_userauth_kbdint_getnprompts +ssh_userauth_kbdint_getprompt +ssh_userauth_kbdint_setanswer +ssh_userauth_list +ssh_userauth_none +ssh_userauth_offer_pubkey +ssh_userauth_password +ssh_userauth_privatekey_file +ssh_userauth_pubkey +ssh_userauth_publickey +ssh_userauth_publickey_auto +ssh_userauth_try_publickey +ssh_version +ssh_write_knownhost +string_burn +string_copy +string_data +string_fill +string_free +string_from_char +string_len +string_new +string_to_char \ No newline at end of file diff --git a/src/ABI/libssh-4.8.4.symbols b/src/ABI/libssh-4.8.4.symbols new file mode 100644 index 0000000..dce4add --- /dev/null +++ b/src/ABI/libssh-4.8.4.symbols @@ -0,0 +1,421 @@ +_ssh_log +buffer_free +buffer_get +buffer_get_len +buffer_new +channel_accept_x11 +channel_change_pty_size +channel_close +channel_forward_accept +channel_forward_cancel +channel_forward_listen +channel_free +channel_get_exit_status +channel_get_session +channel_is_closed +channel_is_eof +channel_is_open +channel_new +channel_open_forward +channel_open_session +channel_poll +channel_read +channel_read_buffer +channel_read_nonblocking +channel_request_env +channel_request_exec +channel_request_pty +channel_request_pty_size +channel_request_send_signal +channel_request_sftp +channel_request_shell +channel_request_subsystem +channel_request_x11 +channel_select +channel_send_eof +channel_set_blocking +channel_write +channel_write_stderr +privatekey_free +privatekey_from_file +publickey_free +publickey_from_file +publickey_from_privatekey +publickey_to_string +sftp_async_read +sftp_async_read_begin +sftp_attributes_free +sftp_canonicalize_path +sftp_chmod +sftp_chown +sftp_client_message_free +sftp_client_message_get_data +sftp_client_message_get_filename +sftp_client_message_get_flags +sftp_client_message_get_submessage +sftp_client_message_get_type +sftp_client_message_set_filename +sftp_close +sftp_closedir +sftp_dir_eof +sftp_extension_supported +sftp_extensions_get_count +sftp_extensions_get_data +sftp_extensions_get_name +sftp_file_set_blocking +sftp_file_set_nonblocking +sftp_free +sftp_fstat +sftp_fstatvfs +sftp_fsync +sftp_get_client_message +sftp_get_error +sftp_handle +sftp_handle_alloc +sftp_handle_remove +sftp_init +sftp_lstat +sftp_mkdir +sftp_new +sftp_new_channel +sftp_open +sftp_opendir +sftp_read +sftp_readdir +sftp_readlink +sftp_rename +sftp_reply_attr +sftp_reply_data +sftp_reply_handle +sftp_reply_name +sftp_reply_names +sftp_reply_names_add +sftp_reply_status +sftp_rewind +sftp_rmdir +sftp_seek +sftp_seek64 +sftp_send_client_message +sftp_server_free +sftp_server_init +sftp_server_new +sftp_server_version +sftp_setstat +sftp_stat +sftp_statvfs +sftp_statvfs_free +sftp_symlink +sftp_tell +sftp_tell64 +sftp_unlink +sftp_utimes +sftp_write +ssh_accept +ssh_add_channel_callbacks +ssh_auth_list +ssh_basename +ssh_bind_accept +ssh_bind_accept_fd +ssh_bind_fd_toaccept +ssh_bind_free +ssh_bind_get_fd +ssh_bind_listen +ssh_bind_new +ssh_bind_options_parse_config +ssh_bind_options_set +ssh_bind_set_blocking +ssh_bind_set_callbacks +ssh_bind_set_fd +ssh_blocking_flush +ssh_buffer_add_data +ssh_buffer_free +ssh_buffer_get +ssh_buffer_get_data +ssh_buffer_get_len +ssh_buffer_new +ssh_buffer_reinit +ssh_channel_accept_forward +ssh_channel_accept_x11 +ssh_channel_cancel_forward +ssh_channel_change_pty_size +ssh_channel_close +ssh_channel_free +ssh_channel_get_exit_status +ssh_channel_get_session +ssh_channel_is_closed +ssh_channel_is_eof +ssh_channel_is_open +ssh_channel_listen_forward +ssh_channel_new +ssh_channel_open_auth_agent +ssh_channel_open_forward +ssh_channel_open_forward_unix +ssh_channel_open_reverse_forward +ssh_channel_open_session +ssh_channel_open_x11 +ssh_channel_poll +ssh_channel_poll_timeout +ssh_channel_read +ssh_channel_read_nonblocking +ssh_channel_read_timeout +ssh_channel_request_auth_agent +ssh_channel_request_env +ssh_channel_request_exec +ssh_channel_request_pty +ssh_channel_request_pty_size +ssh_channel_request_send_break +ssh_channel_request_send_exit_signal +ssh_channel_request_send_exit_status +ssh_channel_request_send_signal +ssh_channel_request_sftp +ssh_channel_request_shell +ssh_channel_request_subsystem +ssh_channel_request_x11 +ssh_channel_select +ssh_channel_send_eof +ssh_channel_set_blocking +ssh_channel_set_counter +ssh_channel_window_size +ssh_channel_write +ssh_channel_write_stderr +ssh_clean_pubkey_hash +ssh_connect +ssh_connector_free +ssh_connector_new +ssh_connector_set_in_channel +ssh_connector_set_in_fd +ssh_connector_set_out_channel +ssh_connector_set_out_fd +ssh_copyright +ssh_dirname +ssh_disconnect +ssh_dump_knownhost +ssh_event_add_connector +ssh_event_add_fd +ssh_event_add_session +ssh_event_dopoll +ssh_event_free +ssh_event_new +ssh_event_remove_connector +ssh_event_remove_fd +ssh_event_remove_session +ssh_execute_message_callbacks +ssh_finalize +ssh_forward_accept +ssh_forward_cancel +ssh_forward_listen +ssh_free +ssh_get_cipher_in +ssh_get_cipher_out +ssh_get_clientbanner +ssh_get_disconnect_message +ssh_get_error +ssh_get_error_code +ssh_get_fd +ssh_get_fingerprint_hash +ssh_get_hexa +ssh_get_hmac_in +ssh_get_hmac_out +ssh_get_issue_banner +ssh_get_kex_algo +ssh_get_log_callback +ssh_get_log_level +ssh_get_log_userdata +ssh_get_openssh_version +ssh_get_poll_flags +ssh_get_pubkey +ssh_get_pubkey_hash +ssh_get_publickey +ssh_get_publickey_hash +ssh_get_random +ssh_get_server_publickey +ssh_get_serverbanner +ssh_get_status +ssh_get_version +ssh_getpass +ssh_gssapi_get_creds +ssh_gssapi_set_creds +ssh_handle_key_exchange +ssh_init +ssh_is_blocking +ssh_is_connected +ssh_is_server_known +ssh_key_cmp +ssh_key_free +ssh_key_is_private +ssh_key_is_public +ssh_key_new +ssh_key_type +ssh_key_type_from_name +ssh_key_type_to_char +ssh_known_hosts_parse_line +ssh_knownhosts_entry_free +ssh_log +ssh_message_auth_interactive_request +ssh_message_auth_kbdint_is_response +ssh_message_auth_password +ssh_message_auth_pubkey +ssh_message_auth_publickey +ssh_message_auth_publickey_state +ssh_message_auth_reply_pk_ok +ssh_message_auth_reply_pk_ok_simple +ssh_message_auth_reply_success +ssh_message_auth_set_methods +ssh_message_auth_user +ssh_message_channel_request_channel +ssh_message_channel_request_command +ssh_message_channel_request_env_name +ssh_message_channel_request_env_value +ssh_message_channel_request_open_destination +ssh_message_channel_request_open_destination_port +ssh_message_channel_request_open_originator +ssh_message_channel_request_open_originator_port +ssh_message_channel_request_open_reply_accept +ssh_message_channel_request_open_reply_accept_channel +ssh_message_channel_request_pty_height +ssh_message_channel_request_pty_pxheight +ssh_message_channel_request_pty_pxwidth +ssh_message_channel_request_pty_term +ssh_message_channel_request_pty_width +ssh_message_channel_request_reply_success +ssh_message_channel_request_subsystem +ssh_message_channel_request_x11_auth_cookie +ssh_message_channel_request_x11_auth_protocol +ssh_message_channel_request_x11_screen_number +ssh_message_channel_request_x11_single_connection +ssh_message_free +ssh_message_get +ssh_message_global_request_address +ssh_message_global_request_port +ssh_message_global_request_reply_success +ssh_message_reply_default +ssh_message_retrieve +ssh_message_service_reply_success +ssh_message_service_service +ssh_message_subtype +ssh_message_type +ssh_mkdir +ssh_new +ssh_options_copy +ssh_options_get +ssh_options_get_port +ssh_options_getopt +ssh_options_parse_config +ssh_options_set +ssh_pcap_file_close +ssh_pcap_file_free +ssh_pcap_file_new +ssh_pcap_file_open +ssh_pki_copy_cert_to_privkey +ssh_pki_export_privkey_base64 +ssh_pki_export_privkey_file +ssh_pki_export_privkey_to_pubkey +ssh_pki_export_pubkey_base64 +ssh_pki_export_pubkey_file +ssh_pki_generate +ssh_pki_import_cert_base64 +ssh_pki_import_cert_file +ssh_pki_import_privkey_base64 +ssh_pki_import_privkey_file +ssh_pki_import_pubkey_base64 +ssh_pki_import_pubkey_file +ssh_pki_key_ecdsa_name +ssh_print_hash +ssh_print_hexa +ssh_privatekey_type +ssh_publickey_to_file +ssh_remove_channel_callbacks +ssh_scp_accept_request +ssh_scp_close +ssh_scp_deny_request +ssh_scp_free +ssh_scp_init +ssh_scp_leave_directory +ssh_scp_new +ssh_scp_pull_request +ssh_scp_push_directory +ssh_scp_push_file +ssh_scp_push_file64 +ssh_scp_read +ssh_scp_request_get_filename +ssh_scp_request_get_permissions +ssh_scp_request_get_size +ssh_scp_request_get_size64 +ssh_scp_request_get_warning +ssh_scp_write +ssh_select +ssh_send_debug +ssh_send_ignore +ssh_send_keepalive +ssh_server_init_kex +ssh_service_request +ssh_session_export_known_hosts_entry +ssh_session_get_known_hosts_entry +ssh_session_has_known_hosts_entry +ssh_session_is_known_server +ssh_session_update_known_hosts +ssh_set_agent_channel +ssh_set_agent_socket +ssh_set_auth_methods +ssh_set_blocking +ssh_set_callbacks +ssh_set_channel_callbacks +ssh_set_counters +ssh_set_fd_except +ssh_set_fd_toread +ssh_set_fd_towrite +ssh_set_log_callback +ssh_set_log_level +ssh_set_log_userdata +ssh_set_message_callback +ssh_set_pcap_file +ssh_set_server_callbacks +ssh_silent_disconnect +ssh_string_burn +ssh_string_copy +ssh_string_data +ssh_string_fill +ssh_string_free +ssh_string_free_char +ssh_string_from_char +ssh_string_get_char +ssh_string_len +ssh_string_new +ssh_string_to_char +ssh_threads_get_default +ssh_threads_get_noop +ssh_threads_get_pthread +ssh_threads_set_callbacks +ssh_try_publickey_from_file +ssh_userauth_agent +ssh_userauth_agent_pubkey +ssh_userauth_autopubkey +ssh_userauth_gssapi +ssh_userauth_kbdint +ssh_userauth_kbdint_getanswer +ssh_userauth_kbdint_getinstruction +ssh_userauth_kbdint_getname +ssh_userauth_kbdint_getnanswers +ssh_userauth_kbdint_getnprompts +ssh_userauth_kbdint_getprompt +ssh_userauth_kbdint_setanswer +ssh_userauth_list +ssh_userauth_none +ssh_userauth_offer_pubkey +ssh_userauth_password +ssh_userauth_privatekey_file +ssh_userauth_pubkey +ssh_userauth_publickey +ssh_userauth_publickey_auto +ssh_userauth_try_publickey +ssh_version +ssh_write_knownhost +string_burn +string_copy +string_data +string_fill +string_free +string_from_char +string_len +string_new +string_to_char \ No newline at end of file diff --git a/src/ABI/libssh-4.8.5.symbols b/src/ABI/libssh-4.8.5.symbols new file mode 100644 index 0000000..dce4add --- /dev/null +++ b/src/ABI/libssh-4.8.5.symbols @@ -0,0 +1,421 @@ +_ssh_log +buffer_free +buffer_get +buffer_get_len +buffer_new +channel_accept_x11 +channel_change_pty_size +channel_close +channel_forward_accept +channel_forward_cancel +channel_forward_listen +channel_free +channel_get_exit_status +channel_get_session +channel_is_closed +channel_is_eof +channel_is_open +channel_new +channel_open_forward +channel_open_session +channel_poll +channel_read +channel_read_buffer +channel_read_nonblocking +channel_request_env +channel_request_exec +channel_request_pty +channel_request_pty_size +channel_request_send_signal +channel_request_sftp +channel_request_shell +channel_request_subsystem +channel_request_x11 +channel_select +channel_send_eof +channel_set_blocking +channel_write +channel_write_stderr +privatekey_free +privatekey_from_file +publickey_free +publickey_from_file +publickey_from_privatekey +publickey_to_string +sftp_async_read +sftp_async_read_begin +sftp_attributes_free +sftp_canonicalize_path +sftp_chmod +sftp_chown +sftp_client_message_free +sftp_client_message_get_data +sftp_client_message_get_filename +sftp_client_message_get_flags +sftp_client_message_get_submessage +sftp_client_message_get_type +sftp_client_message_set_filename +sftp_close +sftp_closedir +sftp_dir_eof +sftp_extension_supported +sftp_extensions_get_count +sftp_extensions_get_data +sftp_extensions_get_name +sftp_file_set_blocking +sftp_file_set_nonblocking +sftp_free +sftp_fstat +sftp_fstatvfs +sftp_fsync +sftp_get_client_message +sftp_get_error +sftp_handle +sftp_handle_alloc +sftp_handle_remove +sftp_init +sftp_lstat +sftp_mkdir +sftp_new +sftp_new_channel +sftp_open +sftp_opendir +sftp_read +sftp_readdir +sftp_readlink +sftp_rename +sftp_reply_attr +sftp_reply_data +sftp_reply_handle +sftp_reply_name +sftp_reply_names +sftp_reply_names_add +sftp_reply_status +sftp_rewind +sftp_rmdir +sftp_seek +sftp_seek64 +sftp_send_client_message +sftp_server_free +sftp_server_init +sftp_server_new +sftp_server_version +sftp_setstat +sftp_stat +sftp_statvfs +sftp_statvfs_free +sftp_symlink +sftp_tell +sftp_tell64 +sftp_unlink +sftp_utimes +sftp_write +ssh_accept +ssh_add_channel_callbacks +ssh_auth_list +ssh_basename +ssh_bind_accept +ssh_bind_accept_fd +ssh_bind_fd_toaccept +ssh_bind_free +ssh_bind_get_fd +ssh_bind_listen +ssh_bind_new +ssh_bind_options_parse_config +ssh_bind_options_set +ssh_bind_set_blocking +ssh_bind_set_callbacks +ssh_bind_set_fd +ssh_blocking_flush +ssh_buffer_add_data +ssh_buffer_free +ssh_buffer_get +ssh_buffer_get_data +ssh_buffer_get_len +ssh_buffer_new +ssh_buffer_reinit +ssh_channel_accept_forward +ssh_channel_accept_x11 +ssh_channel_cancel_forward +ssh_channel_change_pty_size +ssh_channel_close +ssh_channel_free +ssh_channel_get_exit_status +ssh_channel_get_session +ssh_channel_is_closed +ssh_channel_is_eof +ssh_channel_is_open +ssh_channel_listen_forward +ssh_channel_new +ssh_channel_open_auth_agent +ssh_channel_open_forward +ssh_channel_open_forward_unix +ssh_channel_open_reverse_forward +ssh_channel_open_session +ssh_channel_open_x11 +ssh_channel_poll +ssh_channel_poll_timeout +ssh_channel_read +ssh_channel_read_nonblocking +ssh_channel_read_timeout +ssh_channel_request_auth_agent +ssh_channel_request_env +ssh_channel_request_exec +ssh_channel_request_pty +ssh_channel_request_pty_size +ssh_channel_request_send_break +ssh_channel_request_send_exit_signal +ssh_channel_request_send_exit_status +ssh_channel_request_send_signal +ssh_channel_request_sftp +ssh_channel_request_shell +ssh_channel_request_subsystem +ssh_channel_request_x11 +ssh_channel_select +ssh_channel_send_eof +ssh_channel_set_blocking +ssh_channel_set_counter +ssh_channel_window_size +ssh_channel_write +ssh_channel_write_stderr +ssh_clean_pubkey_hash +ssh_connect +ssh_connector_free +ssh_connector_new +ssh_connector_set_in_channel +ssh_connector_set_in_fd +ssh_connector_set_out_channel +ssh_connector_set_out_fd +ssh_copyright +ssh_dirname +ssh_disconnect +ssh_dump_knownhost +ssh_event_add_connector +ssh_event_add_fd +ssh_event_add_session +ssh_event_dopoll +ssh_event_free +ssh_event_new +ssh_event_remove_connector +ssh_event_remove_fd +ssh_event_remove_session +ssh_execute_message_callbacks +ssh_finalize +ssh_forward_accept +ssh_forward_cancel +ssh_forward_listen +ssh_free +ssh_get_cipher_in +ssh_get_cipher_out +ssh_get_clientbanner +ssh_get_disconnect_message +ssh_get_error +ssh_get_error_code +ssh_get_fd +ssh_get_fingerprint_hash +ssh_get_hexa +ssh_get_hmac_in +ssh_get_hmac_out +ssh_get_issue_banner +ssh_get_kex_algo +ssh_get_log_callback +ssh_get_log_level +ssh_get_log_userdata +ssh_get_openssh_version +ssh_get_poll_flags +ssh_get_pubkey +ssh_get_pubkey_hash +ssh_get_publickey +ssh_get_publickey_hash +ssh_get_random +ssh_get_server_publickey +ssh_get_serverbanner +ssh_get_status +ssh_get_version +ssh_getpass +ssh_gssapi_get_creds +ssh_gssapi_set_creds +ssh_handle_key_exchange +ssh_init +ssh_is_blocking +ssh_is_connected +ssh_is_server_known +ssh_key_cmp +ssh_key_free +ssh_key_is_private +ssh_key_is_public +ssh_key_new +ssh_key_type +ssh_key_type_from_name +ssh_key_type_to_char +ssh_known_hosts_parse_line +ssh_knownhosts_entry_free +ssh_log +ssh_message_auth_interactive_request +ssh_message_auth_kbdint_is_response +ssh_message_auth_password +ssh_message_auth_pubkey +ssh_message_auth_publickey +ssh_message_auth_publickey_state +ssh_message_auth_reply_pk_ok +ssh_message_auth_reply_pk_ok_simple +ssh_message_auth_reply_success +ssh_message_auth_set_methods +ssh_message_auth_user +ssh_message_channel_request_channel +ssh_message_channel_request_command +ssh_message_channel_request_env_name +ssh_message_channel_request_env_value +ssh_message_channel_request_open_destination +ssh_message_channel_request_open_destination_port +ssh_message_channel_request_open_originator +ssh_message_channel_request_open_originator_port +ssh_message_channel_request_open_reply_accept +ssh_message_channel_request_open_reply_accept_channel +ssh_message_channel_request_pty_height +ssh_message_channel_request_pty_pxheight +ssh_message_channel_request_pty_pxwidth +ssh_message_channel_request_pty_term +ssh_message_channel_request_pty_width +ssh_message_channel_request_reply_success +ssh_message_channel_request_subsystem +ssh_message_channel_request_x11_auth_cookie +ssh_message_channel_request_x11_auth_protocol +ssh_message_channel_request_x11_screen_number +ssh_message_channel_request_x11_single_connection +ssh_message_free +ssh_message_get +ssh_message_global_request_address +ssh_message_global_request_port +ssh_message_global_request_reply_success +ssh_message_reply_default +ssh_message_retrieve +ssh_message_service_reply_success +ssh_message_service_service +ssh_message_subtype +ssh_message_type +ssh_mkdir +ssh_new +ssh_options_copy +ssh_options_get +ssh_options_get_port +ssh_options_getopt +ssh_options_parse_config +ssh_options_set +ssh_pcap_file_close +ssh_pcap_file_free +ssh_pcap_file_new +ssh_pcap_file_open +ssh_pki_copy_cert_to_privkey +ssh_pki_export_privkey_base64 +ssh_pki_export_privkey_file +ssh_pki_export_privkey_to_pubkey +ssh_pki_export_pubkey_base64 +ssh_pki_export_pubkey_file +ssh_pki_generate +ssh_pki_import_cert_base64 +ssh_pki_import_cert_file +ssh_pki_import_privkey_base64 +ssh_pki_import_privkey_file +ssh_pki_import_pubkey_base64 +ssh_pki_import_pubkey_file +ssh_pki_key_ecdsa_name +ssh_print_hash +ssh_print_hexa +ssh_privatekey_type +ssh_publickey_to_file +ssh_remove_channel_callbacks +ssh_scp_accept_request +ssh_scp_close +ssh_scp_deny_request +ssh_scp_free +ssh_scp_init +ssh_scp_leave_directory +ssh_scp_new +ssh_scp_pull_request +ssh_scp_push_directory +ssh_scp_push_file +ssh_scp_push_file64 +ssh_scp_read +ssh_scp_request_get_filename +ssh_scp_request_get_permissions +ssh_scp_request_get_size +ssh_scp_request_get_size64 +ssh_scp_request_get_warning +ssh_scp_write +ssh_select +ssh_send_debug +ssh_send_ignore +ssh_send_keepalive +ssh_server_init_kex +ssh_service_request +ssh_session_export_known_hosts_entry +ssh_session_get_known_hosts_entry +ssh_session_has_known_hosts_entry +ssh_session_is_known_server +ssh_session_update_known_hosts +ssh_set_agent_channel +ssh_set_agent_socket +ssh_set_auth_methods +ssh_set_blocking +ssh_set_callbacks +ssh_set_channel_callbacks +ssh_set_counters +ssh_set_fd_except +ssh_set_fd_toread +ssh_set_fd_towrite +ssh_set_log_callback +ssh_set_log_level +ssh_set_log_userdata +ssh_set_message_callback +ssh_set_pcap_file +ssh_set_server_callbacks +ssh_silent_disconnect +ssh_string_burn +ssh_string_copy +ssh_string_data +ssh_string_fill +ssh_string_free +ssh_string_free_char +ssh_string_from_char +ssh_string_get_char +ssh_string_len +ssh_string_new +ssh_string_to_char +ssh_threads_get_default +ssh_threads_get_noop +ssh_threads_get_pthread +ssh_threads_set_callbacks +ssh_try_publickey_from_file +ssh_userauth_agent +ssh_userauth_agent_pubkey +ssh_userauth_autopubkey +ssh_userauth_gssapi +ssh_userauth_kbdint +ssh_userauth_kbdint_getanswer +ssh_userauth_kbdint_getinstruction +ssh_userauth_kbdint_getname +ssh_userauth_kbdint_getnanswers +ssh_userauth_kbdint_getnprompts +ssh_userauth_kbdint_getprompt +ssh_userauth_kbdint_setanswer +ssh_userauth_list +ssh_userauth_none +ssh_userauth_offer_pubkey +ssh_userauth_password +ssh_userauth_privatekey_file +ssh_userauth_pubkey +ssh_userauth_publickey +ssh_userauth_publickey_auto +ssh_userauth_try_publickey +ssh_version +ssh_write_knownhost +string_burn +string_copy +string_data +string_fill +string_free +string_from_char +string_len +string_new +string_to_char \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c7f198..532cc14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,4 @@ -set(LIBSSH_PUBLIC_INCLUDE_DIRS - ${libssh_SOURCE_DIR}/include - CACHE INTERNAL "libssh public include directories" -) +set(LIBSSH_PUBLIC_INCLUDE_DIRS ${libssh_SOURCE_DIR}/include) set(LIBSSH_PRIVATE_INCLUDE_DIRS ${libssh_BINARY_DIR} @@ -18,13 +15,6 @@ if (WIN32) ) endif (WIN32) -if (HAVE_LIBSOCKET) - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - socket - ) -endif (HAVE_LIBSOCKET) - if (OPENSSL_CRYPTO_LIBRARY) set(LIBSSH_PRIVATE_INCLUDE_DIRS ${LIBSSH_PRIVATE_INCLUDE_DIRS} @@ -95,15 +85,12 @@ if (WITH_NACL AND NACL_FOUND) ) endif (WITH_NACL AND NACL_FOUND) -set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - CACHE INTERNAL "libssh link libraries" -) - -set(LIBSSH_SHARED_LIBRARY - ssh_shared - CACHE INTERNAL "libssh shared library" -) +if (MINGW AND Threads_FOUND) + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + Threads::Threads + ) +endif() if (BUILD_STATIC_LIB) set(LIBSSH_STATIC_LIBRARY @@ -146,7 +133,6 @@ set(libssh_SRCS pcap.c pki.c pki_container_openssh.c - pki_ed25519.c poll.c session.c scp.c @@ -157,14 +143,11 @@ set(libssh_SRCS external/bcrypt_pbkdf.c external/blowfish.c external/chacha.c - external/ed25519.c - external/fe25519.c - external/ge25519.c external/poly1305.c - external/sc25519.c chachapoly.c config_parser.c token.c + pki_ed25519_common.c ) if (DEFAULT_C_NO_DEPRECATION_FLAGS) @@ -201,6 +184,11 @@ if (WITH_GCRYPT) pki_gcrypt.c ecdh_gcrypt.c dh_key.c + pki_ed25519.c + external/ed25519.c + external/fe25519.c + external/ge25519.c + external/sc25519.c ) elseif (WITH_MBEDTLS) set(libssh_SRCS @@ -211,6 +199,11 @@ elseif (WITH_MBEDTLS) pki_mbedcrypto.c ecdh_mbedcrypto.c dh_key.c + pki_ed25519.c + external/ed25519.c + external/fe25519.c + external/ge25519.c + external/sc25519.c ) else (WITH_GCRYPT) set(libssh_SRCS @@ -221,6 +214,16 @@ else (WITH_GCRYPT) libcrypto.c dh_crypto.c ) + if (NOT HAVE_OPENSSL_ED25519) + set(libssh_SRCS + ${libssh_SRCS} + pki_ed25519.c + external/ed25519.c + external/fe25519.c + external/ge25519.c + external/sc25519.c + ) + endif (NOT HAVE_OPENSSL_ED25519) if(OPENSSL_VERSION VERSION_LESS "1.1.0") set(libssh_SRCS ${libssh_SRCS} libcrypto-compat.c) endif() @@ -271,17 +274,14 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) endif (WITH_GSSAPI AND GSSAPI_FOUND) if (NOT WITH_NACL) - set(libssh_SRCS - ${libssh_SRCS} - external/curve25519_ref.c - ) + if (NOT HAVE_OPENSSL_ED25519) + set(libssh_SRCS + ${libssh_SRCS} + external/curve25519_ref.c + ) + endif (NOT HAVE_OPENSSL_ED25519) endif (NOT WITH_NACL) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${LIBSSH_PRIVATE_INCLUDE_DIRS} -) - # Set the path to the default map file set(MAP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.map") @@ -313,13 +313,27 @@ if (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT AND ABIMAP_FOUND) ) endif (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT AND ABIMAP_FOUND) -add_library(${LIBSSH_SHARED_LIBRARY} SHARED ${libssh_SRCS}) -target_compile_options(${LIBSSH_SHARED_LIBRARY} +# This gets built as a static library, if -DBUILD_SHARED_LIBS=OFF is passed to +# cmake. +add_library(ssh ${libssh_SRCS}) +target_compile_options(ssh PRIVATE ${DEFAULT_C_COMPILE_FLAGS} -D_GNU_SOURCE) +target_include_directories(ssh + PUBLIC + $ + $ + PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS}) -target_link_libraries(${LIBSSH_SHARED_LIBRARY} ${LIBSSH_LINK_LIBRARIES}) +target_link_libraries(ssh + PRIVATE ${LIBSSH_LINK_LIBRARIES}) + +if (WIN32 AND NOT BUILD_SHARED_LIBS) + set_target_properties(ssh PROPERTIES COMPILE_FLAGS "-DLIBSSH_STATIC") +endif () + +add_library(ssh::ssh ALIAS ssh) if (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT) if (ABIMAP_FOUND) @@ -327,56 +341,63 @@ if (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT) set(MAP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_dev.map") endif (ABIMAP_FOUND) - set_target_properties(${LIBSSH_SHARED_LIBRARY} + set_target_properties(ssh PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${MAP_PATH}\"") endif (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT) -set_target_properties( - ${LIBSSH_SHARED_LIBRARY} +set_target_properties(ssh PROPERTIES VERSION ${LIBRARY_VERSION} SOVERSION ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh DEFINE_SYMBOL LIBSSH_EXPORTS ) if (WITH_VISIBILITY_HIDDEN) - set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") + set_target_properties(ssh PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") endif (WITH_VISIBILITY_HIDDEN) if (MINGW) - set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES LINK_FLAGS "-Wl,--enable-stdcall-fixup") + set_target_properties(ssh PROPERTIES LINK_FLAGS "-Wl,--enable-stdcall-fixup") endif () -install( - TARGETS - ${LIBSSH_SHARED_LIBRARY} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - COMPONENT libraries -) +install(TARGETS ssh + EXPORT libssh-config + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT libraries) + +install(EXPORT libssh-config + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) if (BUILD_STATIC_LIB) - add_library(${LIBSSH_STATIC_LIBRARY} STATIC ${libssh_SRCS}) - target_compile_options(${LIBSSH_STATIC_LIBRARY} + add_library(ssh-static STATIC ${libssh_SRCS}) + target_compile_options(ssh-static PRIVATE ${DEFAULT_C_COMPILE_FLAGS} -D_GNU_SOURCE) + target_include_directories(ssh-static + PUBLIC + $ + $ + PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS}) + target_link_libraries(ssh-static + PUBLIC ${LIBSSH_LINK_LIBRARIES}) + add_library(ssh::static ALIAS ssh-static) + if (MSVC) set(OUTPUT_SUFFIX static) else (MSVC) set(OUTPUT_SUFFIX ) endif (MSVC) set_target_properties( - ${LIBSSH_STATIC_LIBRARY} + ssh-static PROPERTIES VERSION ${LIBRARY_VERSION} @@ -390,21 +411,12 @@ if (BUILD_STATIC_LIB) if (WIN32) set_target_properties( - ${LIBSSH_STATIC_LIBRARY} + ssh-static PROPERTIES COMPILE_FLAGS "-DLIBSSH_STATIC" ) endif (WIN32) - - if (WITH_STATIC_LIB) - install(TARGETS - ${LIBSSH_STATIC_LIBRARY} - DESTINATION - ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} - COMPONENT - libraries) - endif (WITH_STATIC_LIB) endif (BUILD_STATIC_LIB) message(STATUS "Threads_FOUND=${Threads_FOUND}") diff --git a/src/agent.c b/src/agent.c index c34f3fc..62b0093 100644 --- a/src/agent.c +++ b/src/agent.c @@ -196,7 +196,7 @@ void ssh_agent_close(struct ssh_agent_struct *agent) { void ssh_agent_free(ssh_agent agent) { if (agent) { if (agent->ident) { - ssh_buffer_free(agent->ident); + SSH_BUFFER_FREE(agent->ident); } if (agent->sock) { ssh_agent_close(agent); @@ -307,90 +307,91 @@ static int agent_talk(struct ssh_session_struct *session, return 0; } -int ssh_agent_get_ident_count(struct ssh_session_struct *session) { - ssh_buffer request = NULL; - ssh_buffer reply = NULL; - unsigned int type = 0; - uint32_t count = 0; - int rc; - - /* send message to the agent requesting the list of identities */ - request = ssh_buffer_new(); - if (request == NULL) { - ssh_set_error_oom(session); - return -1; - } - if (ssh_buffer_add_u8(request, SSH2_AGENTC_REQUEST_IDENTITIES) < 0) { - ssh_set_error_oom(session); - ssh_buffer_free(request); - return -1; - } +uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session) +{ + ssh_buffer request = NULL; + ssh_buffer reply = NULL; + unsigned int type = 0; + uint32_t count = 0; + int rc; - reply = ssh_buffer_new(); - if (reply == NULL) { - ssh_buffer_free(request); - ssh_set_error(session, SSH_FATAL, "Not enough space"); - return -1; - } + /* send message to the agent requesting the list of identities */ + request = ssh_buffer_new(); + if (request == NULL) { + ssh_set_error_oom(session); + return 0; + } + if (ssh_buffer_add_u8(request, SSH2_AGENTC_REQUEST_IDENTITIES) < 0) { + ssh_set_error_oom(session); + SSH_BUFFER_FREE(request); + return 0; + } - if (agent_talk(session, request, reply) < 0) { - ssh_buffer_free(request); - ssh_buffer_free(reply); - return 0; - } - ssh_buffer_free(request); + reply = ssh_buffer_new(); + if (reply == NULL) { + SSH_BUFFER_FREE(request); + ssh_set_error(session, SSH_FATAL, "Not enough space"); + return 0; + } - /* get message type and verify the answer */ - rc = ssh_buffer_get_u8(reply, (uint8_t *) &type); - if (rc != sizeof(uint8_t)) { - ssh_set_error(session, SSH_FATAL, - "Bad authentication reply size: %d", rc); - ssh_buffer_free(reply); - return -1; - } + if (agent_talk(session, request, reply) < 0) { + SSH_BUFFER_FREE(request); + SSH_BUFFER_FREE(reply); + return 0; + } + SSH_BUFFER_FREE(request); + + /* get message type and verify the answer */ + rc = ssh_buffer_get_u8(reply, (uint8_t *) &type); + if (rc != sizeof(uint8_t)) { + ssh_set_error(session, SSH_FATAL, + "Bad authentication reply size: %d", rc); + SSH_BUFFER_FREE(reply); + return 0; + } #ifdef WORDS_BIGENDIAN - type = bswap_32(type); + type = bswap_32(type); #endif - SSH_LOG(SSH_LOG_WARN, - "Answer type: %d, expected answer: %d", - type, SSH2_AGENT_IDENTITIES_ANSWER); + SSH_LOG(SSH_LOG_WARN, + "Answer type: %d, expected answer: %d", + type, SSH2_AGENT_IDENTITIES_ANSWER); - if (agent_failed(type)) { - ssh_buffer_free(reply); - return 0; - } else if (type != SSH2_AGENT_IDENTITIES_ANSWER) { - ssh_set_error(session, SSH_FATAL, - "Bad authentication reply message type: %u", type); - ssh_buffer_free(reply); - return -1; - } + if (agent_failed(type)) { + SSH_BUFFER_FREE(reply); + return 0; + } else if (type != SSH2_AGENT_IDENTITIES_ANSWER) { + ssh_set_error(session, SSH_FATAL, + "Bad authentication reply message type: %u", type); + SSH_BUFFER_FREE(reply); + return 0; + } - rc = ssh_buffer_get_u32(reply, &count); - if (rc != 4) { - ssh_set_error(session, - SSH_FATAL, - "Failed to read count"); - ssh_buffer_free(reply); - return -1; - } - session->agent->count = ntohl(count); - SSH_LOG(SSH_LOG_DEBUG, "Agent count: %d", - session->agent->count); - if (session->agent->count > 1024) { - ssh_set_error(session, SSH_FATAL, - "Too many identities in authentication reply: %d", - session->agent->count); - ssh_buffer_free(reply); - return -1; - } + rc = ssh_buffer_get_u32(reply, &count); + if (rc != 4) { + ssh_set_error(session, + SSH_FATAL, + "Failed to read count"); + SSH_BUFFER_FREE(reply); + return 0; + } + session->agent->count = ntohl(count); + SSH_LOG(SSH_LOG_DEBUG, "Agent count: %d", + session->agent->count); + if (session->agent->count > 1024) { + ssh_set_error(session, SSH_FATAL, + "Too many identities in authentication reply: %d", + session->agent->count); + SSH_BUFFER_FREE(reply); + return 0; + } - if (session->agent->ident) { - ssh_buffer_reinit(session->agent->ident); - } - session->agent->ident = reply; + if (session->agent->ident) { + ssh_buffer_reinit(session->agent->ident); + } + session->agent->ident = reply; - return session->agent->count; + return session->agent->count; } /* caller has to free commment */ @@ -424,7 +425,7 @@ ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, /* get the comment */ tmp = ssh_buffer_get_ssh_string(session->agent->ident); if (tmp == NULL) { - ssh_string_free(blob); + SSH_STRING_FREE(blob); return NULL; } @@ -432,12 +433,12 @@ ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, if (comment) { *comment = ssh_string_to_char(tmp); } else { - ssh_string_free(blob); - ssh_string_free(tmp); + SSH_STRING_FREE(blob); + SSH_STRING_FREE(tmp); return NULL; } - ssh_string_free(tmp); + SSH_STRING_FREE(tmp); /* get key from blob */ rc = ssh_pki_import_pubkey_blob(blob, &key); @@ -445,7 +446,7 @@ ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, /* Try again as a cert. */ rc = ssh_pki_import_cert_blob(blob, &key); } - ssh_string_free(blob); + SSH_STRING_FREE(blob); if (rc == SSH_ERROR) { return NULL; } @@ -491,13 +492,13 @@ ssh_string ssh_agent_sign_data(ssh_session session, /* create request */ if (ssh_buffer_add_u8(request, SSH2_AGENTC_SIGN_REQUEST) < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } rc = ssh_pki_export_pubkey_blob(pubkey, &key_blob); if (rc < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } @@ -512,26 +513,26 @@ ssh_string ssh_agent_sign_data(ssh_session session, sizeof(uint32_t) * 2 + ssh_string_len(key_blob)); if (rc < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } /* adds len + blob */ rc = ssh_buffer_add_ssh_string(request, key_blob); - ssh_string_free(key_blob); + SSH_STRING_FREE(key_blob); if (rc < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } /* Add data */ dlen = ssh_buffer_get_len(data); if (ssh_buffer_add_u32(request, htonl(dlen)) < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } if (ssh_buffer_add_data(request, ssh_buffer_get(data), dlen) < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } @@ -544,27 +545,27 @@ ssh_string ssh_agent_sign_data(ssh_session session, } } if (ssh_buffer_add_u32(request, htonl(flags)) < 0) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } reply = ssh_buffer_new(); if (reply == NULL) { - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); return NULL; } /* send the request */ if (agent_talk(session, request, reply) < 0) { - ssh_buffer_free(request); - ssh_buffer_free(reply); + SSH_BUFFER_FREE(request); + SSH_BUFFER_FREE(reply); return NULL; } - ssh_buffer_free(request); + SSH_BUFFER_FREE(request); /* check if reply is valid */ if (ssh_buffer_get_u8(reply, (uint8_t *) &type) != sizeof(uint8_t)) { - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); return NULL; } #ifdef WORDS_BIGENDIAN @@ -573,19 +574,19 @@ ssh_string ssh_agent_sign_data(ssh_session session, if (agent_failed(type)) { SSH_LOG(SSH_LOG_WARN, "Agent reports failure in signing the key"); - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); return NULL; } else if (type != SSH2_AGENT_SIGN_RESPONSE) { ssh_set_error(session, SSH_FATAL, "Bad authentication response: %u", type); - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); return NULL; } sig_blob = ssh_buffer_get_ssh_string(reply); - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); return sig_blob; } diff --git a/src/auth.c b/src/auth.c index 5eabeb8..3a5f0ef 100644 --- a/src/auth.c +++ b/src/auth.c @@ -25,6 +25,7 @@ #include "config.h" #include +#include #ifndef _WIN32 #include @@ -204,7 +205,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_banner) { SSH_LOG(SSH_LOG_DEBUG, "Received SSH_USERAUTH_BANNER packet"); if (session->banner != NULL) - ssh_string_free(session->banner); + SSH_STRING_FREE(session->banner); session->banner = banner; } @@ -557,7 +558,7 @@ int ssh_userauth_try_publickey(ssh_session session, goto fail; } - ssh_string_free(pubkey_s); + SSH_STRING_FREE(pubkey_s); session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY; session->auth.state = SSH_AUTH_STATE_PUBKEY_OFFER_SENT; @@ -575,7 +576,7 @@ pending: return rc; fail: - ssh_string_free(pubkey_s); + SSH_STRING_FREE(pubkey_s); ssh_set_error_oom(session); ssh_buffer_reinit(session->out_buffer); @@ -680,7 +681,7 @@ int ssh_userauth_publickey(ssh_session session, if (rc < 0) { goto fail; } - ssh_string_free(str); + SSH_STRING_FREE(str); /* Get the hash type to be used in the signature based on the key type */ hash_type = ssh_key_type_to_hash(session, privkey->type); @@ -692,7 +693,7 @@ int ssh_userauth_publickey(ssh_session session, } rc = ssh_buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); + SSH_STRING_FREE(str); str = NULL; if (rc < 0) { goto fail; @@ -714,7 +715,7 @@ pending: return rc; fail: - ssh_string_free(str); + SSH_STRING_FREE(str); ssh_set_error_oom(session); ssh_buffer_reinit(session->out_buffer); @@ -840,7 +841,7 @@ void ssh_agent_state_free(void *data) { struct ssh_agent_state_struct *state = data; if (state) { - ssh_string_free_char(state->comment); + SSH_STRING_FREE_CHAR(state->comment); ssh_key_free(state->pubkey); free (state); } @@ -918,7 +919,7 @@ int ssh_userauth_agent(ssh_session session, } else if (rc != SSH_AUTH_SUCCESS) { SSH_LOG(SSH_LOG_DEBUG, "Public key of %s refused by server", state->comment); - ssh_string_free_char(state->comment); + SSH_STRING_FREE_CHAR(state->comment); state->comment = NULL; ssh_key_free(state->pubkey); state->pubkey = ssh_agent_get_next_ident(session, &state->comment); @@ -934,7 +935,7 @@ int ssh_userauth_agent(ssh_session session, rc = ssh_userauth_agent_publickey(session, username, state->pubkey); if (rc == SSH_AUTH_AGAIN) return rc; - ssh_string_free_char(state->comment); + SSH_STRING_FREE_CHAR(state->comment); state->comment = NULL; if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_PARTIAL) { ssh_agent_state_free (session->agent_state); @@ -1030,6 +1031,9 @@ int ssh_userauth_publickey_auto(ssh_session session, ssh_set_error_oom(session); return SSH_AUTH_ERROR; } + + /* Set state explicitly */ + session->auth.auto_state->state = SSH_AUTH_AUTO_STATE_NONE; } state = session->auth.auto_state; if (state->state == SSH_AUTH_AUTO_STATE_NONE) { @@ -1112,7 +1116,9 @@ int ssh_userauth_publickey_auto(ssh_session session, "Public key authentication error for %s", privkey_file); ssh_key_free(state->privkey); + state->privkey = NULL; ssh_key_free(state->pubkey); + state->pubkey = NULL; SAFE_FREE(session->auth.auto_state); return rc; } else if (rc == SSH_AUTH_AGAIN) { @@ -1178,6 +1184,9 @@ int ssh_userauth_publickey_auto(ssh_session session, return rc; } + ssh_key_free(state->privkey); + ssh_key_free(state->pubkey); + SSH_LOG(SSH_LOG_WARN, "The server accepted the public key but refused the signature"); state->it = state->it->next; @@ -1329,7 +1338,7 @@ ssh_kbdint ssh_kbdint_new(void) { void ssh_kbdint_free(ssh_kbdint kbd) { - int i, n; + size_t i, n; if (kbd == NULL) { return; @@ -1365,7 +1374,7 @@ void ssh_kbdint_free(ssh_kbdint kbd) { } void ssh_kbdint_clean(ssh_kbdint kbd) { - int i, n; + size_t i, n; if (kbd == NULL) { return; @@ -1554,7 +1563,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ); /* We don't care about tmp */ - ssh_string_free(tmp); + SSH_STRING_FREE(tmp); if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg"); @@ -1778,7 +1787,7 @@ const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, } if (echo) { - *echo = session->kbdint->echo[i]; + *echo = (char)session->kbdint->echo[i]; } return session->kbdint->prompts[i]; diff --git a/src/base64.c b/src/base64.c index 372dc65..4148f49 100644 --- a/src/base64.c +++ b/src/base64.c @@ -29,7 +29,8 @@ #include "libssh/priv.h" #include "libssh/buffer.h" -static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +static +const uint8_t alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; @@ -167,19 +168,19 @@ ssh_buffer base64_to_bin(const char *source) { error: SAFE_FREE(base64); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } -#define BLOCK(letter, n) do {ptr = strchr(alphabet, source[n]); \ +#define BLOCK(letter, n) do {ptr = strchr((const char *)alphabet, source[n]); \ if(!ptr) return -1; \ - i = ptr - alphabet; \ + i = ptr - (const char *)alphabet; \ SET_##letter(*block, i); \ } while(0) /* Returns 0 if ok, -1 if not (ie invalid char into the stuff) */ static int to_block4(unsigned long *block, const char *source, int num) { - char *ptr; + const char *ptr = NULL; unsigned int i; *block = 0; @@ -234,29 +235,32 @@ static int get_equals(char *string) { } /* thanks sysk for debugging my mess :) */ +static void _bin_to_base64(uint8_t *dest, + const uint8_t source[3], + size_t len) +{ #define BITS(n) ((1 << (n)) - 1) -static void _bin_to_base64(unsigned char *dest, const unsigned char source[3], - int len) { - switch (len) { - case 1: - dest[0] = alphabet[(source[0] >> 2)]; - dest[1] = alphabet[((source[0] & BITS(2)) << 4)]; - dest[2] = '='; - dest[3] = '='; - break; - case 2: - dest[0] = alphabet[source[0] >> 2]; - dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; - dest[2] = alphabet[(source[1] & BITS(4)) << 2]; - dest[3] = '='; - break; - case 3: - dest[0] = alphabet[(source[0] >> 2)]; - dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; - dest[2] = alphabet[ (source[2] >> 6) | (source[1] & BITS(4)) << 2]; - dest[3] = alphabet[source[2] & BITS(6)]; - break; - } + switch (len) { + case 1: + dest[0] = alphabet[(source[0] >> 2)]; + dest[1] = alphabet[((source[0] & BITS(2)) << 4)]; + dest[2] = '='; + dest[3] = '='; + break; + case 2: + dest[0] = alphabet[source[0] >> 2]; + dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; + dest[2] = alphabet[(source[1] & BITS(4)) << 2]; + dest[3] = '='; + break; + case 3: + dest[0] = alphabet[(source[0] >> 2)]; + dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; + dest[2] = alphabet[(source[2] >> 6) | (source[1] & BITS(4)) << 2]; + dest[3] = alphabet[source[2] & BITS(6)]; + break; + } +#undef BITS } /** @@ -266,25 +270,29 @@ static void _bin_to_base64(unsigned char *dest, const unsigned char source[3], * * @returns the converted string */ -unsigned char *bin_to_base64(const unsigned char *source, int len) { - unsigned char *base64; - unsigned char *ptr; - int flen = len + (3 - (len % 3)); /* round to upper 3 multiple */ - flen = (4 * flen) / 3 + 1; - - base64 = malloc(flen); - if (base64 == NULL) { - return NULL; - } - ptr = base64; - - while(len > 0){ - _bin_to_base64(ptr, source, len > 3 ? 3 : len); - ptr += 4; - source += 3; - len -= 3; - } - ptr[0] = '\0'; +uint8_t *bin_to_base64(const uint8_t *source, size_t len) +{ + uint8_t *base64 = NULL; + uint8_t *ptr = NULL; + size_t flen = len + (3 - (len % 3)); /* round to upper 3 multiple */ + flen = (4 * flen) / 3 + 1; + + base64 = malloc(flen); + if (base64 == NULL) { + return NULL; + } + ptr = base64; + + while(len > 0){ + _bin_to_base64(ptr, source, len > 3 ? 3 : len); + ptr += 4; + if (len < 3) { + break; + } + source += 3; + len -= 3; + } + ptr[0] = '\0'; - return base64; + return base64; } diff --git a/src/bignum.c b/src/bignum.c index fdd2d81..ef8de31 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -29,9 +29,9 @@ ssh_string ssh_make_bignum_string(bignum num) { ssh_string ptr = NULL; - int pad = 0; - unsigned int len = bignum_num_bytes(num); - unsigned int bits = bignum_num_bits(num); + size_t pad = 0; + size_t len = bignum_num_bytes(num); + size_t bits = bignum_num_bits(num); if (len == 0) { return NULL; @@ -43,7 +43,9 @@ ssh_string ssh_make_bignum_string(bignum num) { } #ifdef DEBUG_CRYPTO - fprintf(stderr, "%d bits, %d bytes, %d padding\n", bits, len, pad); + SSH_LOG(SSH_LOG_TRACE, + "%zu bits, %zu bytes, %zu padding\n", + bits, len, pad); #endif /* DEBUG_CRYPTO */ ptr = ssh_string_new(len + pad); @@ -67,7 +69,8 @@ bignum ssh_make_string_bn(ssh_string string) size_t len = ssh_string_len(string); #ifdef DEBUG_CRYPTO - fprintf(stderr, "Importing a %zu bits, %zu bytes object ...\n", + SSH_LOG(SSH_LOG_TRACE, + "Importing a %zu bits, %zu bytes object ...\n", len * 8, len); #endif /* DEBUG_CRYPTO */ @@ -77,7 +80,7 @@ bignum ssh_make_string_bn(ssh_string string) } /* prints the bignum on stderr */ -void ssh_print_bignum(const char *name, const bignum num) +void ssh_print_bignum(const char *name, const_bignum num) { unsigned char *hex = NULL; if (num != NULL) { diff --git a/src/bind.c b/src/bind.c index 8b45ef8..fa8df9e 100644 --- a/src/bind.c +++ b/src/bind.c @@ -411,7 +411,7 @@ void ssh_bind_free(ssh_bind sshbind){ ssh_key_free(sshbind->ed25519); sshbind->ed25519 = NULL; - for (i = 0; i < 10; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { if (sshbind->wanted_methods[i]) { SAFE_FREE(sshbind->wanted_methods[i]); } @@ -442,7 +442,7 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ session->server = 1; /* Copy options from bind to session */ - for (i = 0; i < 10; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { if (sshbind->wanted_methods[i]) { session->opts.wanted_methods[i] = strdup(sshbind->wanted_methods[i]); if (session->opts.wanted_methods[i] == NULL) { diff --git a/src/buffer.c b/src/buffer.c index 1f38ae6..a2e6246 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1119,6 +1119,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, goto cleanup; } + rc = SSH_ERROR; switch (*p) { case 'b': o.byte = va_arg(ap, uint8_t *); @@ -1128,27 +1129,32 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, case 'w': o.word = va_arg(ap, uint16_t *); rlen = ssh_buffer_get_data(buffer, o.word, sizeof(uint16_t)); - *o.word = ntohs(*o.word); - rc = rlen==2 ? SSH_OK : SSH_ERROR; + if (rlen == 2) { + *o.word = ntohs(*o.word); + rc = SSH_OK; + } break; case 'd': o.dword = va_arg(ap, uint32_t *); rlen = ssh_buffer_get_u32(buffer, o.dword); - *o.dword = ntohl(*o.dword); - rc = rlen==4 ? SSH_OK : SSH_ERROR; + if (rlen == 4) { + *o.dword = ntohl(*o.dword); + rc = SSH_OK; + } break; case 'q': o.qword = va_arg(ap, uint64_t*); rlen = ssh_buffer_get_u64(buffer, o.qword); - *o.qword = ntohll(*o.qword); - rc = rlen==8 ? SSH_OK : SSH_ERROR; + if (rlen == 8) { + *o.qword = ntohll(*o.qword); + rc = SSH_OK; + } break; case 'B': o.bignum = va_arg(ap, bignum *); *o.bignum = NULL; tmp_string = ssh_buffer_get_ssh_string(buffer); if (tmp_string == NULL) { - rc = SSH_ERROR; break; } *o.bignum = ssh_make_string_bn(tmp_string); @@ -1167,14 +1173,12 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, o.cstring = va_arg(ap, char **); *o.cstring = NULL; - rc = ssh_buffer_get_u32(buffer, &u32len); - if (rc != 4){ - rc = SSH_ERROR; + rlen = ssh_buffer_get_u32(buffer, &u32len); + if (rlen != 4){ break; } len = ntohl(u32len); if (len > max_len - 1) { - rc = SSH_ERROR; break; } @@ -1230,7 +1234,6 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, break; default: SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p); - rc = SSH_ERROR; } if (rc != SSH_OK) { break; diff --git a/src/chachapoly.c b/src/chachapoly.c index caa694c..820e7f6 100644 --- a/src/chachapoly.c +++ b/src/chachapoly.c @@ -109,11 +109,11 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, out_packet->payload, len - sizeof(uint32_t)); - /* ssh_print_hexa("poly1305_ctx", poly1305_ctx, sizeof(poly1305_ctx)); */ + /* ssh_log_hexdump("poly1305_ctx", poly1305_ctx, sizeof(poly1305_ctx)); */ /* step 4, compute the MAC */ poly1305_auth(tag, (uint8_t *)out_packet, len, poly1305_ctx); - /* ssh_print_hexa("poly1305 src", (uint8_t *)out_packet, len); - ssh_print_hexa("poly1305 tag", tag, POLY1305_TAGLEN); */ + /* ssh_log_hexdump("poly1305 src", (uint8_t *)out_packet, len); + ssh_log_hexdump("poly1305 tag", tag, POLY1305_TAGLEN); */ } static int chacha20_poly1305_aead_decrypt_length( @@ -159,17 +159,17 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher, poly1305_ctx, POLY1305_KEYLEN); #if 0 - ssh_print_hexa("poly1305_ctx", poly1305_ctx, sizeof(poly1305_ctx)); + ssh_log_hexdump("poly1305_ctx", poly1305_ctx, sizeof(poly1305_ctx)); #endif poly1305_auth(tag, (uint8_t *)complete_packet, encrypted_size + sizeof(uint32_t), poly1305_ctx); #if 0 - ssh_print_hexa("poly1305 src", + ssh_log_hexdump("poly1305 src", (uint8_t*)complete_packet, encrypted_size + 4); - ssh_print_hexa("poly1305 tag", tag, POLY1305_TAGLEN); - ssh_print_hexa("received tag", mac, POLY1305_TAGLEN); + ssh_log_hexdump("poly1305 tag", tag, POLY1305_TAGLEN); + ssh_log_hexdump("received tag", mac, POLY1305_TAGLEN); #endif cmp = memcmp(tag, mac, POLY1305_TAGLEN); diff --git a/src/channels.c b/src/channels.c index 71d39c6..05dcf8f 100644 --- a/src/channels.c +++ b/src/channels.c @@ -106,7 +106,7 @@ ssh_channel ssh_channel_new(ssh_session session) channel->stderr_buffer = ssh_buffer_new(); if (channel->stderr_buffer == NULL) { ssh_set_error_oom(session); - ssh_buffer_free(channel->stdout_buffer); + SSH_BUFFER_FREE(channel->stdout_buffer); SAFE_FREE(channel); return NULL; } @@ -121,6 +121,10 @@ ssh_channel ssh_channel_new(ssh_session session) ssh_list_prepend(session->channels, channel); + /* Set states explicitly */ + channel->state = SSH_CHANNEL_STATE_NOT_OPEN; + channel->request_state = SSH_CHANNEL_REQ_STATE_NONE; + return channel; } @@ -277,74 +281,89 @@ static int ssh_channel_open_termination(void *c){ * * @return SSH_OK if successful; SSH_ERROR otherwise. */ -static int channel_open(ssh_channel channel, const char *type, int window, - int maxpacket, ssh_buffer payload) { - ssh_session session = channel->session; - int err=SSH_ERROR; - int rc; +static int +channel_open(ssh_channel channel, + const char *type, + uint32_t window, + uint32_t maxpacket, + ssh_buffer payload) +{ + ssh_session session = channel->session; + int err = SSH_ERROR; + int rc; - switch(channel->state){ - case SSH_CHANNEL_STATE_NOT_OPEN: - break; - case SSH_CHANNEL_STATE_OPENING: - goto pending; - case SSH_CHANNEL_STATE_OPEN: - case SSH_CHANNEL_STATE_CLOSED: - case SSH_CHANNEL_STATE_OPEN_DENIED: - goto end; - default: - ssh_set_error(session,SSH_FATAL,"Bad state in channel_open: %d",channel->state); - } - channel->local_channel = ssh_channel_new_id(session); - channel->local_maxpacket = maxpacket; - channel->local_window = window; + switch (channel->state) { + case SSH_CHANNEL_STATE_NOT_OPEN: + break; + case SSH_CHANNEL_STATE_OPENING: + goto pending; + case SSH_CHANNEL_STATE_OPEN: + case SSH_CHANNEL_STATE_CLOSED: + case SSH_CHANNEL_STATE_OPEN_DENIED: + goto end; + default: + ssh_set_error(session, SSH_FATAL, "Bad state in channel_open: %d", + channel->state); + } - SSH_LOG(SSH_LOG_PROTOCOL, - "Creating a channel %d with %d window and %d max packet", - channel->local_channel, window, maxpacket); + channel->local_channel = ssh_channel_new_id(session); + channel->local_maxpacket = maxpacket; + channel->local_window = window; - rc = ssh_buffer_pack(session->out_buffer, - "bsddd", - SSH2_MSG_CHANNEL_OPEN, - type, - channel->local_channel, - channel->local_window, - channel->local_maxpacket); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - return err; - } + SSH_LOG(SSH_LOG_PROTOCOL, + "Creating a channel %d with %d window and %d max packet", + channel->local_channel, window, maxpacket); - if (payload != NULL) { - if (ssh_buffer_add_buffer(session->out_buffer, payload) < 0) { - ssh_set_error_oom(session); + rc = ssh_buffer_pack(session->out_buffer, + "bsddd", + SSH2_MSG_CHANNEL_OPEN, + type, + channel->local_channel, + channel->local_window, + channel->local_maxpacket); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + return err; + } + + if (payload != NULL) { + if (ssh_buffer_add_buffer(session->out_buffer, payload) < 0) { + ssh_set_error_oom(session); - return err; + return err; + } + } + channel->state = SSH_CHANNEL_STATE_OPENING; + if (ssh_packet_send(session) == SSH_ERROR) { + return err; } - } - channel->state = SSH_CHANNEL_STATE_OPENING; - if (ssh_packet_send(session) == SSH_ERROR) { - return err; - } + SSH_LOG(SSH_LOG_PACKET, + "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", + type, channel->local_channel); - SSH_LOG(SSH_LOG_PACKET, - "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", - type, channel->local_channel); pending: - /* wait until channel is opened by server */ - err = ssh_handle_packets_termination(session, - SSH_TIMEOUT_DEFAULT, - ssh_channel_open_termination, - channel); - - if (session->session_state == SSH_SESSION_STATE_ERROR) - err = SSH_ERROR; + /* wait until channel is opened by server */ + err = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_open_termination, + channel); + + if (session->session_state == SSH_SESSION_STATE_ERROR) { + err = SSH_ERROR; + } + end: - if(channel->state == SSH_CHANNEL_STATE_OPEN) - err=SSH_OK; + /* This needs to pass the SSH_AGAIN from the above, + * but needs to catch failed channel states */ + if (channel->state == SSH_CHANNEL_STATE_OPEN) { + err = SSH_OK; + } else if (err != SSH_AGAIN) { + /* Messages were handled correctly, but he channel state is invalid */ + err = SSH_ERROR; + } - return err; + return err; } /* return channel with corresponding local id, or NULL if not found */ @@ -373,7 +392,10 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { * @param minimumsize The minimum acceptable size for the new window. * @return SSH_OK if successful; SSH_ERROR otherwise. */ -static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) { +static int grow_window(ssh_session session, + ssh_channel channel, + uint32_t minimumsize) +{ uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; int rc; @@ -538,7 +560,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ if (channel_default_bufferize(channel, ssh_string_data(str), len, is_stderr) < 0) { - ssh_string_free(str); + SSH_STRING_FREE(str); return SSH_PACKET_USED; } @@ -554,7 +576,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ channel->local_window, channel->remote_window); - ssh_string_free(str); + SSH_STRING_FREE(str); if (is_stderr) { buf = channel->stderr_buffer; @@ -822,8 +844,10 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { * * FIXME is the window changed? */ -int channel_default_bufferize(ssh_channel channel, void *data, int len, - int is_stderr) { +int channel_default_bufferize(ssh_channel channel, + void *data, size_t len, + bool is_stderr) +{ ssh_session session; if(channel == NULL) { @@ -838,8 +862,10 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, } SSH_LOG(SSH_LOG_PACKET, - "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); - if (is_stderr == 0) { + "placing %zu bytes into channel buffer (%s)", + len, + is_stderr ? "stderr" : "stdout"); + if (!is_stderr) { /* stdout */ if (channel->stdout_buffer == NULL) { channel->stdout_buffer = ssh_buffer_new(); @@ -851,7 +877,7 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, if (ssh_buffer_add_data(channel->stdout_buffer, data, len) < 0) { ssh_set_error_oom(session); - ssh_buffer_free(channel->stdout_buffer); + SSH_BUFFER_FREE(channel->stdout_buffer); channel->stdout_buffer = NULL; return -1; } @@ -867,7 +893,7 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, if (ssh_buffer_add_data(channel->stderr_buffer, data, len) < 0) { ssh_set_error_oom(session); - ssh_buffer_free(channel->stderr_buffer); + SSH_BUFFER_FREE(channel->stderr_buffer); channel->stderr_buffer = NULL; return -1; } @@ -998,8 +1024,8 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, payload); error: - ssh_buffer_free(payload); - ssh_string_free(str); + SSH_BUFFER_FREE(payload); + SSH_STRING_FREE(str); return rc; } @@ -1081,8 +1107,8 @@ int ssh_channel_open_forward_unix(ssh_channel channel, payload); error: - ssh_buffer_free(payload); - ssh_string_free(str); + SSH_BUFFER_FREE(payload); + SSH_STRING_FREE(str); return rc; } @@ -1815,7 +1841,7 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, pending: rc = channel_request(channel, "pty-req", buffer, 1); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -1875,7 +1901,7 @@ int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { rc = channel_request(channel, "window-change", buffer, 0); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -1944,11 +1970,23 @@ int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) { pending: rc = channel_request(channel, "subsystem", buffer, 1); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } +/** + * @brief Request sftp subsystem on the channel + * + * @param[in] channel The channel to request the sftp subsystem. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @note You should use sftp_new() which does this for you. + */ int ssh_channel_request_sftp( ssh_channel channel){ if(channel == NULL) { return SSH_ERROR; @@ -2048,7 +2086,7 @@ pending: rc = channel_request(channel, "x11-req", buffer, 1); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2361,7 +2399,7 @@ pending: } error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2433,7 +2471,7 @@ pending: rc = ssh_global_request(session, "cancel-tcpip-forward", buffer, 1); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2491,7 +2529,7 @@ int ssh_channel_request_env(ssh_channel channel, const char *name, const char *v pending: rc = channel_request(channel, "env", buffer,1); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2513,12 +2551,12 @@ error: * * Example: @code - rc = channel_request_exec(channel, "ps aux"); + rc = ssh_channel_request_exec(channel, "ps aux"); if (rc > 0) { return -1; } - while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { + while ((rc = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) { return -1; } @@ -2560,7 +2598,7 @@ int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { pending: rc = channel_request(channel, "exec", buffer, 1); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2623,7 +2661,7 @@ int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { rc = channel_request(channel, "signal", buffer, 0); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2666,7 +2704,7 @@ int ssh_channel_request_send_break(ssh_channel channel, uint32_t length) { rc = channel_request(channel, "break", buffer, 0); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -2942,42 +2980,45 @@ int ssh_channel_read_timeout(ssh_channel channel, * * @see ssh_channel_is_eof() */ -int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, - int is_stderr) { - ssh_session session; - int to_read; - int rc; - int blocking; +int ssh_channel_read_nonblocking(ssh_channel channel, + void *dest, + uint32_t count, + int is_stderr) +{ + ssh_session session; + ssize_t to_read; + int rc; + int blocking; - if(channel == NULL) { - return SSH_ERROR; - } - if(dest == NULL) { - ssh_set_error_invalid(channel->session); - return SSH_ERROR; - } + if(channel == NULL) { + return SSH_ERROR; + } + if(dest == NULL) { + ssh_set_error_invalid(channel->session); + return SSH_ERROR; + } - session = channel->session; + session = channel->session; - to_read = ssh_channel_poll(channel, is_stderr); + to_read = ssh_channel_poll(channel, is_stderr); - if (to_read <= 0) { - if (session->session_state == SSH_SESSION_STATE_ERROR){ - return SSH_ERROR; - } + if (to_read <= 0) { + if (session->session_state == SSH_SESSION_STATE_ERROR){ + return SSH_ERROR; + } - return to_read; /* may be an error code */ - } + return to_read; /* may be an error code */ + } - if (to_read > (int)count) { - to_read = (int)count; - } - blocking = ssh_is_blocking(session); - ssh_set_blocking(session, 0); - rc = ssh_channel_read(channel, dest, to_read, is_stderr); - ssh_set_blocking(session,blocking); + if ((size_t)to_read > count) { + to_read = (ssize_t)count; + } + blocking = ssh_is_blocking(session); + ssh_set_blocking(session, 0); + rc = ssh_channel_read(channel, dest, (uint32_t)to_read, is_stderr); + ssh_set_blocking(session,blocking); - return rc; + return rc; } /** @@ -3046,38 +3087,51 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){ * * @see ssh_channel_is_eof() */ -int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ - ssh_session session; - ssh_buffer stdbuf; - struct ssh_channel_read_termination_struct ctx; - int rc; +int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr) +{ + ssh_session session; + ssh_buffer stdbuf; + struct ssh_channel_read_termination_struct ctx; + size_t len; + int rc; - if(channel == NULL) { - return SSH_ERROR; - } + if(channel == NULL) { + return SSH_ERROR; + } - session = channel->session; - stdbuf = channel->stdout_buffer; + session = channel->session; + stdbuf = channel->stdout_buffer; - if (is_stderr) { - stdbuf = channel->stderr_buffer; - } - ctx.buffer = stdbuf; - ctx.channel = channel; - ctx.count = 1; - rc = ssh_handle_packets_termination(channel->session, timeout, - ssh_channel_read_termination, &ctx); - if(rc ==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ - rc = SSH_ERROR; - goto end; - } - rc = ssh_buffer_get_len(stdbuf); - if(rc > 0) - goto end; - if (channel->remote_eof) - rc = SSH_EOF; -end: - return rc; + if (is_stderr) { + stdbuf = channel->stderr_buffer; + } + ctx.buffer = stdbuf; + ctx.channel = channel; + ctx.count = 1; + rc = ssh_handle_packets_termination(channel->session, + timeout, + ssh_channel_read_termination, + &ctx); + if (rc == SSH_ERROR || + session->session_state == SSH_SESSION_STATE_ERROR) { + rc = SSH_ERROR; + goto out; + } + len = ssh_buffer_get_len(stdbuf); + if (len > 0) { + if (len > INT_MAX) { + rc = SSH_ERROR; + } else { + rc = (int)len; + } + goto out; + } + if (channel->remote_eof) { + rc = SSH_EOF; + } + +out: + return rc; } /** @@ -3196,8 +3250,9 @@ static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans, } /* Just count number of pointers in the array */ -static int count_ptrs(ssh_channel *ptrs) { - int c; +static size_t count_ptrs(ssh_channel *ptrs) +{ + size_t c; for (c = 0; ptrs[c] != NULL; c++) ; @@ -3451,7 +3506,7 @@ pending: payload); error: - ssh_buffer_free(payload); + SSH_BUFFER_FREE(payload); return rc; } @@ -3513,7 +3568,7 @@ pending: payload); error: - ssh_buffer_free(payload); + SSH_BUFFER_FREE(payload); return rc; } @@ -3554,7 +3609,7 @@ int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) { rc = channel_request(channel, "exit-status", buffer, 0); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -3609,7 +3664,7 @@ int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig, rc = channel_request(channel, "exit-signal", buffer, 0); error: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } diff --git a/src/client.c b/src/client.c index a1b587f..ae170b9 100644 --- a/src/client.c +++ b/src/client.c @@ -252,6 +252,7 @@ static int dh_handshake(ssh_session session) { switch(session->next_crypto->kex_type){ case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP14_SHA1: + case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP18_SHA512: rc = ssh_client_dh_init(session); diff --git a/src/config.c b/src/config.c index d26d44a..a08e41b 100644 --- a/src/config.c +++ b/src/config.c @@ -274,10 +274,8 @@ static int ssh_config_match(char *value, const char *pattern, bool negate) { int ok, result = 0; - char *lowervalue; - lowervalue = (value) ? ssh_lowercase(value) : NULL; - ok = match_pattern_list(lowervalue, pattern, strlen(pattern), 0); + ok = match_pattern_list(value, pattern, strlen(pattern), 0); if (ok <= 0 && negate == true) { result = 1; } else if (ok > 0 && negate == false) { @@ -286,7 +284,6 @@ ssh_config_match(char *value, const char *pattern, bool negate) SSH_LOG(SSH_LOG_TRACE, "%s '%s' against pattern '%s'%s (ok=%d)", result == 1 ? "Matched" : "Not matched", value, pattern, negate == true ? " (negated)" : "", ok); - SAFE_FREE(lowervalue); return result; } @@ -397,6 +394,11 @@ ssh_config_parse_line(ssh_session session, long l; int64_t ll; + /* Ignore empty lines */ + if (line == NULL || *line == '\0') { + return 0; + } + x = s = strdup(line); if (s == NULL) { ssh_set_error_oom(session); @@ -450,6 +452,7 @@ ssh_config_parse_line(ssh_session session, int result = 1; size_t args = 0; enum ssh_config_match_e opt; + char *localuser = NULL; *parsing = 0; do { @@ -515,8 +518,29 @@ ssh_config_parse_line(ssh_session session, result = 0; break; - case MATCH_ORIGINALHOST: case MATCH_LOCALUSER: + /* Here we match only one argument */ + p = ssh_config_get_str_tok(&s, NULL); + if (p == NULL || p[0] == '\0') { + ssh_set_error(session, SSH_FATAL, + "line %d: ERROR - Match user keyword " + "requires argument", count); + SAFE_FREE(x); + return -1; + } + localuser = ssh_get_local_username(); + if (localuser == NULL) { + SSH_LOG(SSH_LOG_WARN, "line %d: Can not get local username " + "for conditional matching.", count); + SAFE_FREE(x); + return -1; + } + result &= ssh_config_match(localuser, p, negate); + SAFE_FREE(localuser); + args++; + break; + + case MATCH_ORIGINALHOST: /* Skip one argument */ p = ssh_config_get_str_tok(&s, NULL); if (p == NULL || p[0] == '\0') { diff --git a/src/connect.c b/src/connect.c index 124b9f5..252e2c6 100644 --- a/src/connect.c +++ b/src/connect.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/src/curve25519.c b/src/curve25519.c index 92f603d..c13b360 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -39,6 +39,10 @@ #include "libssh/pki.h" #include "libssh/bignum.h" +#ifdef HAVE_OPENSSL_X25519 +#include +#endif + static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply); static ssh_packet_callback dh_client_callbacks[] = { @@ -52,58 +56,216 @@ static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = { .user = NULL }; +static int ssh_curve25519_init(ssh_session session) +{ + int rc; +#ifdef HAVE_OPENSSL_X25519 + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + size_t pubkey_len = CURVE25519_PUBKEY_SIZE; + size_t pkey_len = CURVE25519_PRIVKEY_SIZE; + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); + if (pctx == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 context: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + + rc = EVP_PKEY_keygen_init(pctx); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 keygen: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_CTX_free(pctx); + return SSH_ERROR; + } + + rc = EVP_PKEY_keygen(pctx, &pkey); + EVP_PKEY_CTX_free(pctx); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to generate X25519 keys: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + + if (session->server) { + rc = EVP_PKEY_get_raw_public_key(pkey, + session->next_crypto->curve25519_server_pubkey, + &pubkey_len); + } else { + rc = EVP_PKEY_get_raw_public_key(pkey, + session->next_crypto->curve25519_client_pubkey, + &pubkey_len); + } + + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get X25519 raw public key: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_free(pkey); + return SSH_ERROR; + } + + rc = EVP_PKEY_get_raw_private_key(pkey, + session->next_crypto->curve25519_privkey, + &pkey_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get X25519 raw private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_free(pkey); + return SSH_ERROR; + } + + EVP_PKEY_free(pkey); +#else + rc = ssh_get_random(session->next_crypto->curve25519_privkey, + CURVE25519_PRIVKEY_SIZE, 1); + if (rc != 1) { + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; + } + + if (session->server) { + crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey, + session->next_crypto->curve25519_privkey); + } else { + crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey, + session->next_crypto->curve25519_privkey); + } +#endif /* HAVE_OPENSSL_X25519 */ + + return SSH_OK; +} + /** @internal * @brief Starts curve25519-sha256@libssh.org / curve25519-sha256 key exchange */ -int ssh_client_curve25519_init(ssh_session session){ - int rc; - int ok; +int ssh_client_curve25519_init(ssh_session session) +{ + int rc; - ok = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); - if (!ok) { - ssh_set_error(session, SSH_FATAL, "PRNG error"); - return SSH_ERROR; - } + rc = ssh_curve25519_init(session); + if (rc != SSH_OK) { + return rc; + } - crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey, - session->next_crypto->curve25519_privkey); - - rc = ssh_buffer_pack(session->out_buffer, - "bdP", - SSH2_MSG_KEX_ECDH_INIT, - CURVE25519_PUBKEY_SIZE, - (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - /* register the packet callbacks */ - ssh_packet_set_callbacks(session, &ssh_curve25519_client_callbacks); - session->dh_handshake_state = DH_STATE_INIT_SENT; - rc = ssh_packet_send(session); + rc = ssh_buffer_pack(session->out_buffer, + "bdP", + SSH2_MSG_KEX_ECDH_INIT, + CURVE25519_PUBKEY_SIZE, + (size_t)CURVE25519_PUBKEY_SIZE, + session->next_crypto->curve25519_client_pubkey); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + return SSH_ERROR; + } - return rc; + /* register the packet callbacks */ + ssh_packet_set_callbacks(session, &ssh_curve25519_client_callbacks); + session->dh_handshake_state = DH_STATE_INIT_SENT; + rc = ssh_packet_send(session); + + return rc; } -static int ssh_curve25519_build_k(ssh_session session) { - ssh_curve25519_pubkey k; +static int ssh_curve25519_build_k(ssh_session session) +{ + ssh_curve25519_pubkey k; + +#ifdef HAVE_OPENSSL_X25519 + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL, *pubkey = NULL; + size_t shared_key_len = sizeof(k); + int rc, ret = SSH_ERROR; + + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, + session->next_crypto->curve25519_privkey, + CURVE25519_PRIVKEY_SIZE); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create X25519 EVP_PKEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } - if (session->server) - crypto_scalarmult(k, session->next_crypto->curve25519_privkey, - session->next_crypto->curve25519_client_pubkey); - else - crypto_scalarmult(k, session->next_crypto->curve25519_privkey, - session->next_crypto->curve25519_server_pubkey); + pctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pctx == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 context: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } - bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret); - if (session->next_crypto->shared_secret == NULL) { - return SSH_ERROR; - } + rc = EVP_PKEY_derive_init(pctx); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 key derivation: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + if (session->server) { + pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, + session->next_crypto->curve25519_client_pubkey, + CURVE25519_PUBKEY_SIZE); + } else { + pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, + session->next_crypto->curve25519_server_pubkey, + CURVE25519_PUBKEY_SIZE); + } + if (pubkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create X25519 public key EVP_PKEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + rc = EVP_PKEY_derive_set_peer(pctx, pubkey); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to set peer X25519 public key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + rc = EVP_PKEY_derive(pctx, k, &shared_key_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to derive X25519 shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + ret = SSH_OK; +out: + EVP_PKEY_free(pkey); + EVP_PKEY_free(pubkey); + EVP_PKEY_CTX_free(pctx); + if (ret == SSH_ERROR) { + return ret; + } +#else + if (session->server) { + crypto_scalarmult(k, session->next_crypto->curve25519_privkey, + session->next_crypto->curve25519_client_pubkey); + } else { + crypto_scalarmult(k, session->next_crypto->curve25519_privkey, + session->next_crypto->curve25519_server_pubkey); + } +#endif /* HAVE_OPENSSL_X25519 */ + + bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret); + if (session->next_crypto->shared_secret == NULL) { + return SSH_ERROR; + } #ifdef DEBUG_CRYPTO - ssh_print_hexa("Session server cookie", + ssh_log_hexdump("Session server cookie", session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", + ssh_log_hexdump("Session client cookie", session->next_crypto->client_kex.cookie, 16); ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret); #endif @@ -132,7 +294,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){ } rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != 0) { ssh_set_error(session, SSH_FATAL, @@ -148,11 +310,11 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){ if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", (int)ssh_string_len(q_s_string)); - ssh_string_free(q_s_string); + SSH_STRING_FREE(q_s_string); goto error; } memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); - ssh_string_free(q_s_string); + SSH_STRING_FREE(q_s_string); signature = ssh_buffer_get_ssh_string(packet); if (signature == NULL) { @@ -221,8 +383,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ /* SSH host keys (rsa,dsa,ecdsa) */ ssh_key privkey; + enum ssh_digest_e digest = SSH_DIGEST_AUTO; ssh_string sig_blob = NULL; - int ok; int rc; (void)type; (void)user; @@ -240,24 +402,21 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ SSH_FATAL, "Incorrect size for server Curve25519 public key: %zu", ssh_string_len(q_c_string)); - ssh_string_free(q_c_string); + SSH_STRING_FREE(q_c_string); goto error; } memcpy(session->next_crypto->curve25519_client_pubkey, - ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); - ssh_string_free(q_c_string); + ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); + SSH_STRING_FREE(q_c_string); /* Build server's keypair */ - ok = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); - if (!ok) { - ssh_set_error(session, SSH_FATAL, "PRNG error"); + rc = ssh_curve25519_init(session); + if (rc != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Failed to generate curve25519 keys"); goto error; } - crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey, - session->next_crypto->curve25519_privkey); - rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_REPLY); if (rc < 0) { ssh_set_error_oom(session); @@ -272,7 +431,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ } /* privkey is not allocated */ - rc = ssh_get_key_params(session, &privkey); + rc = ssh_get_key_params(session, &privkey, &digest); if (rc == SSH_ERROR) { goto error; } @@ -292,7 +451,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ /* add host's public key */ rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_blob); - ssh_string_free(server_pubkey_blob); + SSH_STRING_FREE(server_pubkey_blob); if (rc < 0) { ssh_set_error_oom(session); goto error; @@ -309,20 +468,20 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ CURVE25519_PUBKEY_SIZE); rc = ssh_buffer_add_ssh_string(session->out_buffer, q_s_string); - ssh_string_free(q_s_string); + SSH_STRING_FREE(q_s_string); if (rc < 0) { ssh_set_error_oom(session); goto error; } /* add signature blob */ - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey, digest); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); goto error; } rc = ssh_buffer_add_ssh_string(session->out_buffer, sig_blob); - ssh_string_free(sig_blob); + SSH_STRING_FREE(sig_blob); if (rc < 0) { ssh_set_error_oom(session); goto error; diff --git a/src/dh-gex.c b/src/dh-gex.c index b20bf2b..9bf0546 100644 --- a/src/dh-gex.c +++ b/src/dh-gex.c @@ -107,7 +107,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group) int blen; bignum pmin1 = NULL, one = NULL; bignum_CTX ctx = bignum_ctx_new(); - bignum modulus, generator; + bignum modulus = NULL, generator = NULL; const_bignum pubkey; (void) type; (void) user; @@ -179,14 +179,18 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group) bignum_ctx_free(ctx); ctx = NULL; - /* all checks passed, set parameters */ + /* all checks passed, set parameters (the BNs are copied in openssl backend) */ rc = ssh_dh_set_parameters(session->next_crypto->dh_ctx, modulus, generator); if (rc != SSH_OK) { - bignum_safe_free(modulus); - bignum_safe_free(generator); goto error; } +#ifdef HAVE_LIBCRYPTO + bignum_safe_free(modulus); + bignum_safe_free(generator); +#endif + modulus = NULL; + generator = NULL; /* compute and send DH public parameter */ rc = ssh_dh_keypair_gen_keys(session->next_crypto->dh_ctx, @@ -221,6 +225,8 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group) return SSH_PACKET_USED; error: + bignum_safe_free(modulus); + bignum_safe_free(generator); bignum_safe_free(one); bignum_safe_free(pmin1); if(!bignum_ctx_invalid(ctx)) { @@ -259,7 +265,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply) } rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != 0) { goto error; } @@ -267,6 +273,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply) rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx, DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR, &session->next_crypto->shared_secret); + ssh_dh_debug_crypto(session->next_crypto); if (rc == SSH_ERROR) { ssh_set_error(session, SSH_FATAL, "Could not generate shared secret"); goto error; @@ -359,8 +366,13 @@ static bool dhgroup_better_size(uint32_t pmin, */ static bool invn_chance(int n) { - uint32_t nounce; - ssh_get_random(&nounce, sizeof(nounce), 0); + uint32_t nounce = 0; + int ok; + + ok = ssh_get_random(&nounce, sizeof(nounce), 0); + if (!ok) { + return false; + } return (nounce % n) == 0; } @@ -641,8 +653,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request) generator); #ifdef HAVE_LIBCRYPTO - bignum_safe_free(generator); - bignum_safe_free(modulus); + bignum_safe_free(generator); + bignum_safe_free(modulus); #endif if (rc != SSH_OK) { diff --git a/src/dh.c b/src/dh.c index 6c69813..05903da 100644 --- a/src/dh.c +++ b/src/dh.c @@ -365,7 +365,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){ goto error; } rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != 0) { goto error; } @@ -373,6 +373,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){ rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx, DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR, &session->next_crypto->shared_secret); + ssh_dh_debug_crypto(session->next_crypto); if (rc == SSH_ERROR){ ssh_set_error(session, SSH_FATAL, "Could not generate shared secret"); goto error; @@ -430,6 +431,7 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet) { struct ssh_crypto_struct *crypto = session->next_crypto; ssh_key privkey = NULL; + enum ssh_digest_e digest = SSH_DIGEST_AUTO; ssh_string sig_blob = NULL; ssh_string pubkey_blob = NULL; bignum client_pubkey; @@ -455,13 +457,14 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet) goto error; } - rc = ssh_get_key_params(session, &privkey); + rc = ssh_get_key_params(session, &privkey, &digest); if (rc != SSH_OK) { goto error; } rc = ssh_dh_compute_shared_secret(crypto->dh_ctx, DH_SERVER_KEYPAIR, DH_CLIENT_KEYPAIR, &crypto->shared_secret); + ssh_dh_debug_crypto(crypto); if (rc == SSH_ERROR) { ssh_set_error(session, SSH_FATAL, "Could not generate shared secret"); goto error; @@ -471,7 +474,7 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet) ssh_set_error(session, SSH_FATAL, "Could not create a session id"); goto error; } - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey, digest); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); goto error; @@ -479,6 +482,7 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet) switch (crypto->kex_type){ case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP14_SHA1: + case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP18_SHA512: packet_type = SSH2_MSG_KEXDH_REPLY; @@ -693,13 +697,16 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len) /** * @brief Get a hash as a human-readable hex- or base64-string. * - * This gets an allocated fingerprint hash. It is a hex strings if the given - * hash is a md5 sum. If it is a SHA sum, it will return an unpadded base64 - * strings. Either way, the output is prepended by the hash-type. + * This gets an allocated fingerprint hash. If it is a SHA sum, it will + * return an unpadded base64 strings. If it is a MD5 sum, it will return hex + * string. Either way, the output is prepended by the hash-type. * - * @param type Which sort of hash is given. + * @warning Do NOT use MD5 or SHA1! Those hash functions are being deprecated. * - * @param hash What should be converted to a base64 string. + * @param type Which sort of hash is given, use + * SSH_PUBLICKEY_HASH_SHA256 or better. + * + * @param hash The hash to be converted to fingerprint. * * @param len Length of the buffer to convert. * @@ -766,13 +773,13 @@ char *ssh_get_fingerprint_hash(enum ssh_publickey_hash_type type, /** * @brief Print a hash as a human-readable hex- or base64-string. * - * This function prints hex strings if the given hash is a md5 sum. - * But prints unpadded base64 strings for sha sums. - * Either way, the output is prepended by the hash-type. + * This prints an unpadded base64 strings for SHA sums and hex strings for MD5 + * sum. Either way, the output is prepended by the hash-type. * - * @param type Which sort of hash is given. + * @param type Which sort of hash is given. Use + * SSH_PUBLICKEY_HASH_SHA256 or better. * - * @param hash What should be converted to a base64 string. + * @param hash The hash to be converted to fingerprint. * * @param len Length of the buffer to convert. * diff --git a/src/dh_crypto.c b/src/dh_crypto.c index 56a76fa..3b3495c 100644 --- a/src/dh_crypto.c +++ b/src/dh_crypto.c @@ -41,6 +41,27 @@ struct dh_ctx { DH *keypair[2]; }; +void ssh_dh_debug_crypto(struct ssh_crypto_struct *c) +{ +#ifdef DEBUG_CRYPTO + const_bignum x = NULL, y = NULL, e = NULL, f = NULL; + + ssh_dh_keypair_get_keys(c->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e); + ssh_dh_keypair_get_keys(c->dh_ctx, DH_SERVER_KEYPAIR, &y, &f); + ssh_print_bignum("x", x); + ssh_print_bignum("y", y); + ssh_print_bignum("e", e); + ssh_print_bignum("f", f); + + ssh_log_hexdump("Session server cookie", c->server_kex.cookie, 16); + ssh_log_hexdump("Session client cookie", c->client_kex.cookie, 16); + ssh_print_bignum("k", c->shared_secret); + +#else + (void)c; /* UNUSED_PARAM */ +#endif +} + int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer, const_bignum *priv, const_bignum *pub) { @@ -96,12 +117,14 @@ int ssh_dh_get_parameters(struct dh_ctx *ctx, int ssh_dh_set_parameters(struct dh_ctx *ctx, const bignum modulus, const bignum generator) { + size_t i; int rc; if ((ctx == NULL) || (modulus == NULL) || (generator == NULL)) { return SSH_ERROR; } - for (int i = 0; i < 2; i++) { + + for (i = 0; i < 2; i++) { bignum p = NULL; bignum g = NULL; @@ -156,6 +179,7 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto) rc = ssh_dh_set_parameters(ctx, ssh_dh_group1, ssh_dh_generator); break; case SSH_KEX_DH_GROUP14_SHA1: + case SSH_KEX_DH_GROUP14_SHA256: rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator); break; case SSH_KEX_DH_GROUP16_SHA512: diff --git a/src/dh_key.c b/src/dh_key.c index cc69dfe..bda54b1 100644 --- a/src/dh_key.c +++ b/src/dh_key.c @@ -60,6 +60,28 @@ struct dh_ctx { bignum modulus; }; +void ssh_dh_debug_crypto(struct ssh_crypto_struct *c) +{ +#ifdef DEBUG_CRYPTO + const_bignum x = NULL, y = NULL, e = NULL, f = NULL; + + ssh_dh_keypair_get_keys(c->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e); + ssh_dh_keypair_get_keys(c->dh_ctx, DH_SERVER_KEYPAIR, &y, &f); + ssh_print_bignum("p", c->dh_ctx->modulus); + ssh_print_bignum("g", c->dh_ctx->generator); + ssh_print_bignum("x", x); + ssh_print_bignum("y", y); + ssh_print_bignum("e", e); + ssh_print_bignum("f", f); + + ssh_log_hexdump("Session server cookie", c->server_kex.cookie, 16); + ssh_log_hexdump("Session client cookie", c->client_kex.cookie, 16); + ssh_print_bignum("k", c->shared_secret); +#else + (void)c; /* UNUSED_PARAM */ +#endif +} + static void ssh_dh_free_modulus(struct dh_ctx *ctx) { if ((ctx->modulus != ssh_dh_group1) && @@ -225,6 +247,7 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto) rc = ssh_dh_set_parameters(ctx, ssh_dh_group1, ssh_dh_generator); break; case SSH_KEX_DH_GROUP14_SHA1: + case SSH_KEX_DH_GROUP14_SHA256: rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator); break; case SSH_KEX_DH_GROUP16_SHA512: @@ -263,30 +286,6 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto) crypto->dh_ctx = NULL; } -#ifdef DEBUG_CRYPTO -static void ssh_dh_debug(ssh_session session) -{ - struct ssh_crypto_struct *crypto = session->next_crypto; - const_bignum x, y, e, f; - ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e); - ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR, &y, &f); - ssh_print_bignum("p", crypto->dh_ctx->modulus); - ssh_print_bignum("g", crypto->dh_ctx->generator); - ssh_print_bignum("x", x); - ssh_print_bignum("y", y); - ssh_print_bignum("e", e); - ssh_print_bignum("f", f); - - ssh_print_hexa("Session server cookie", - session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", - session->next_crypto->client_kex.cookie, 16); - ssh_print_bignum("k", session->next_crypto->shared_secret); -} -#else -#define ssh_dh_debug(session) -#endif - /** @internal * @brief generates a secret DH parameter of at least DH_SECURITY_BITS * security as well as the corresponding public key. @@ -370,7 +369,6 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote, done: bignum_ctx_free(ctx); - ssh_dh_debug(session); if (rc != 1) { return SSH_ERROR; } diff --git a/src/ecdh.c b/src/ecdh.c index 1be1d92..a4c07cc 100644 --- a/src/ecdh.c +++ b/src/ecdh.c @@ -63,7 +63,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){ } rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != 0) { goto error; } diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c index 86e4551..a1de27f 100644 --- a/src/ecdh_crypto.c +++ b/src/ecdh_crypto.c @@ -101,7 +101,7 @@ int ssh_client_ecdh_init(ssh_session session){ rc = ssh_buffer_add_ssh_string(session->out_buffer,client_pubkey); if (rc < 0) { EC_KEY_free(key); - ssh_string_free(client_pubkey); + SSH_STRING_FREE(client_pubkey); return SSH_ERROR; } @@ -181,9 +181,9 @@ int ecdh_build_k(ssh_session session) { session->next_crypto->ecdh_privkey = NULL; #ifdef DEBUG_CRYPTO - ssh_print_hexa("Session server cookie", + ssh_log_hexdump("Session server cookie", session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", + ssh_log_hexdump("Session client cookie", session->next_crypto->client_kex.cookie, 16); ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret); #endif @@ -206,6 +206,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ bignum_CTX ctx; /* SSH host keys (rsa,dsa,ecdsa) */ ssh_key privkey; + enum ssh_digest_e digest = SSH_DIGEST_AUTO; ssh_string sig_blob = NULL; ssh_string pubkey_blob = NULL; int curve; @@ -277,7 +278,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ } /* privkey is not allocated */ - rc = ssh_get_key_params(session, &privkey); + rc = ssh_get_key_params(session, &privkey, &digest); if (rc == SSH_ERROR) { goto error; } @@ -288,7 +289,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ goto error; } - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey, digest); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); goto error; @@ -297,7 +298,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ rc = ssh_dh_get_next_server_publickey_blob(session, &pubkey_blob); if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Could not export server public key"); - ssh_string_free(sig_blob); + SSH_STRING_FREE(sig_blob); return SSH_ERROR; } @@ -308,8 +309,8 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ q_s_string, /* ecdh public key */ sig_blob); /* signature blob */ - ssh_string_free(sig_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(sig_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != SSH_OK) { ssh_set_error_oom(session); diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c index 473846b..d9c41bf 100644 --- a/src/ecdh_gcrypt.c +++ b/src/ecdh_gcrypt.c @@ -115,7 +115,7 @@ int ssh_client_ecdh_init(ssh_session session) out: gcry_sexp_release(param); gcry_sexp_release(key); - ssh_string_free(client_pubkey); + SSH_STRING_FREE(client_pubkey); return rc; } @@ -215,13 +215,13 @@ int ecdh_build_k(ssh_session session) k_len = 133; } else { ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); goto out; } if (ssh_string_len(s) != k_len) { ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); goto out; } @@ -231,7 +231,7 @@ int ecdh_build_k(ssh_session session) k_len / 2, NULL); ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); if (err) { goto out; } @@ -242,9 +242,9 @@ int ecdh_build_k(ssh_session session) session->next_crypto->ecdh_privkey = NULL; #ifdef DEBUG_CRYPTO - ssh_print_hexa("Session server cookie", + ssh_log_hexdump("Session server cookie", session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", + ssh_log_hexdump("Session client cookie", session->next_crypto->client_kex.cookie, 16); ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret); #endif @@ -254,7 +254,7 @@ int ecdh_build_k(ssh_session session) gcry_sexp_release(data); gcry_sexp_release(result); ssh_string_burn(privkey); - ssh_string_free(privkey); + SSH_STRING_FREE(privkey); return rc; } @@ -273,6 +273,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ gcry_sexp_t key = NULL; /* SSH host keys (rsa,dsa,ecdsa) */ ssh_key privkey; + enum ssh_digest_e digest = SSH_DIGEST_AUTO; ssh_string sig_blob = NULL; ssh_string pubkey_blob = NULL; int rc = SSH_ERROR; @@ -325,7 +326,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ } /* privkey is not allocated */ - rc = ssh_get_key_params(session, &privkey); + rc = ssh_get_key_params(session, &privkey, &digest); if (rc != SSH_OK) { goto out; } @@ -336,7 +337,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ goto out; } - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey, digest); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); rc = SSH_ERROR; @@ -346,7 +347,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ rc = ssh_dh_get_next_server_publickey_blob(session, &pubkey_blob); if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Could not export server public key"); - ssh_string_free(sig_blob); + SSH_STRING_FREE(sig_blob); goto out; } @@ -357,8 +358,8 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ q_s_string, /* ecdh public key */ sig_blob); /* signature blob */ - ssh_string_free(sig_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(sig_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != SSH_OK) { ssh_set_error_oom(session); diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c index 2c165aa..718f152 100644 --- a/src/ecdh_mbedcrypto.c +++ b/src/ecdh_mbedcrypto.c @@ -113,7 +113,7 @@ int ssh_client_ecdh_init(ssh_session session) out: mbedtls_ecp_group_free(&grp); - ssh_string_free(client_pubkey); + SSH_STRING_FREE(client_pubkey); return rc; } @@ -188,6 +188,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ ssh_string q_s_string = NULL; mbedtls_ecp_group grp; ssh_key privkey = NULL; + enum ssh_digest_e digest = SSH_DIGEST_AUTO; ssh_string sig_blob = NULL; ssh_string pubkey_blob = NULL; int rc; @@ -250,7 +251,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ } /* privkey is not allocated */ - rc = ssh_get_key_params(session, &privkey); + rc = ssh_get_key_params(session, &privkey, &digest); if (rc == SSH_ERROR) { rc = SSH_ERROR; goto out; @@ -263,7 +264,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ goto out; } - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey, digest); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); rc = SSH_ERROR; @@ -273,7 +274,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ rc = ssh_dh_get_next_server_publickey_blob(session, &pubkey_blob); if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Could not export server public key"); - ssh_string_free(sig_blob); + SSH_STRING_FREE(sig_blob); goto out; } @@ -283,8 +284,8 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ q_s_string, /* ecdh public key */ sig_blob); /* signature blob */ - ssh_string_free(sig_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(sig_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != SSH_OK) { ssh_set_error_oom(session); diff --git a/src/external/fe25519.c b/src/external/fe25519.c index 2b0b673..a7f26c9 100644 --- a/src/external/fe25519.c +++ b/src/external/fe25519.c @@ -120,10 +120,10 @@ void fe25519_pack(unsigned char r[32], const fe25519 *x) } } -int fe25519_iszero(const fe25519 *x) +uint32_t fe25519_iszero(const fe25519 *x) { int i; - int r; + uint32_t r; fe25519 t = *x; fe25519_freeze(&t); diff --git a/src/external/sc25519.c b/src/external/sc25519.c index 5f198d5..5da91f7 100644 --- a/src/external/sc25519.c +++ b/src/external/sc25519.c @@ -223,7 +223,7 @@ int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y) void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y) { - int i, carry; + uint32_t i, carry; for (i = 0; i < 32; i++) { r->v[i] = x->v[i] + y->v[i]; @@ -253,7 +253,7 @@ void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y) void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y) { - int i,j,carry; + uint32_t i,j,carry; uint32_t t[64]; for (i = 0; i < 64; i++) { diff --git a/src/gssapi.c b/src/gssapi.c index 5c3fb61..488df58 100644 --- a/src/gssapi.c +++ b/src/gssapi.c @@ -208,7 +208,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n return SSH_ERROR; session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN; rc = ssh_gssapi_send_response(session, oid_s); - ssh_string_free(oid_s); + SSH_STRING_FREE(oid_s); return rc; } else { return ssh_auth_reply_default(session,0); @@ -235,6 +235,10 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n for (i=0 ; i< n_oid ; ++i){ unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); size_t len = ssh_string_len(oids[i]); + + if (oid_s == NULL) { + continue; + } if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){ SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID"); continue; @@ -293,6 +297,10 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n for (i=0 ; i< n_oid ; ++i){ unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); size_t len = ssh_string_len(oids[i]); + + if (oid_s == NULL) { + continue; + } if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){ SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID"); continue; @@ -384,7 +392,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ return SSH_PACKET_USED; } ssh_packet_send(session); - ssh_string_free(out_token); + SSH_STRING_FREE(out_token); } else { session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC; } @@ -403,7 +411,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ "accepting token", maj_stat, min_stat); - ssh_string_free(token); + SSH_STRING_FREE(token); if (client_name != GSS_C_NO_NAME){ session->gssapi->client_name = client_name; session->gssapi->canonic_user = ssh_gssapi_name_to_char(client_name); @@ -444,13 +452,17 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session) ssh_buffer mic_buffer = NULL; int rc; + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); + if (crypto == NULL) { + return NULL; + } + mic_buffer = ssh_buffer_new(); if (mic_buffer == NULL) { ssh_set_error_oom(session); return NULL; } - crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); rc = ssh_buffer_pack(mic_buffer, "dPbsss", crypto->digest_len, @@ -461,7 +473,7 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session) "gssapi-with-mic"); if (rc != SSH_OK) { ssh_set_error_oom(session); - ssh_buffer_free(mic_buffer); + SSH_BUFFER_FREE(mic_buffer); return NULL; } @@ -545,10 +557,10 @@ error: end: ssh_gssapi_free(session); if (mic_buffer != NULL) { - ssh_buffer_free(mic_buffer); + SSH_BUFFER_FREE(mic_buffer); } if (mic_token != NULL) { - ssh_string_free(mic_token); + SSH_STRING_FREE(mic_token); } return SSH_PACKET_USED; @@ -700,13 +712,13 @@ end: * later. */ int ssh_gssapi_auth_mic(ssh_session session){ - int i; + size_t i; gss_OID_set selected; /* oid selected for authentication */ - ssh_string *oids; + ssh_string *oids = NULL; int rc; - int n_oids = 0; + size_t n_oids = 0; OM_uint32 maj_stat, min_stat; - char name_buf[256]; + char name_buf[256] = {0}; gss_buffer_desc hostname; const char *gss_host = session->opts.host; @@ -750,7 +762,7 @@ int ssh_gssapi_auth_mic(ssh_session session){ } n_oids = selected->count; - SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d oids", n_oids); + SSH_LOG(SSH_LOG_PROTOCOL, "Sending %zu oids", n_oids); oids = calloc(n_oids, sizeof(ssh_string)); if (oids == NULL) { @@ -760,6 +772,11 @@ int ssh_gssapi_auth_mic(ssh_session session){ for (i=0; ielements[i].length + 2); + if (oids[i] == NULL) { + ssh_set_error_oom(session); + rc = SSH_ERROR; + goto out; + } ((unsigned char *)oids[i]->data)[0] = SSH_OID_TAG; ((unsigned char *)oids[i]->data)[1] = selected->elements[i].length; memcpy((unsigned char *)oids[i]->data + 2, selected->elements[i].elements, @@ -767,8 +784,10 @@ int ssh_gssapi_auth_mic(ssh_session session){ } rc = ssh_gssapi_send_auth_mic(session, oids, n_oids); + +out: for (i = 0; i < n_oids; i++) { - ssh_string_free(oids[i]); + SSH_STRING_FREE(oids[i]); } free(oids); if (rc != SSH_ERROR) { @@ -778,13 +797,13 @@ int ssh_gssapi_auth_mic(ssh_session session){ return SSH_AUTH_ERROR; } -static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s){ - gss_OID ret; +static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s) +{ + gss_OID ret = NULL; unsigned char *data = ssh_string_data(oid_s); size_t len = ssh_string_len(oid_s); - ret = malloc(sizeof(gss_OID_desc)); - if (ret == NULL) { + if (data == NULL) { return NULL; } @@ -792,10 +811,17 @@ static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s){ SAFE_FREE(ret); return NULL; } + if (data[0] != SSH_OID_TAG || data[1] != len - 2) { SAFE_FREE(ret); return NULL; } + + ret = malloc(sizeof(gss_OID_desc)); + if (ret == NULL) { + return NULL; + } + ret->elements = malloc(len - 2); if (ret->elements == NULL) { SAFE_FREE(ret); @@ -828,7 +854,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){ goto error; } session->gssapi->client.oid = ssh_gssapi_oid_from_string(oid_s); - ssh_string_free(oid_s); + SSH_STRING_FREE(oid_s); if (!session->gssapi->client.oid) { ssh_set_error(session, SSH_FATAL, "Invalid OID"); goto error; @@ -896,7 +922,7 @@ static int ssh_gssapi_send_mic(ssh_session session){ maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT, &mic_buf, &mic_token_buf); if (GSS_ERROR(maj_stat)){ - ssh_buffer_free(mic_buffer); + SSH_BUFFER_FREE(mic_buffer); ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "generating MIC", maj_stat, @@ -910,7 +936,7 @@ static int ssh_gssapi_send_mic(ssh_session session){ mic_token_buf.length, (size_t)mic_token_buf.length, mic_token_buf.value); if (rc != SSH_OK) { - ssh_buffer_free(mic_buffer); + SSH_BUFFER_FREE(mic_buffer); ssh_set_error_oom(session); return SSH_ERROR; } @@ -958,7 +984,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){ "accepting token", maj_stat, min_stat); - ssh_string_free(token); + SSH_STRING_FREE(token); if (GSS_ERROR(maj_stat)){ ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "Gssapi error", diff --git a/src/gzip.c b/src/gzip.c index 2de576f..f981de9 100644 --- a/src/gzip.c +++ b/src/gzip.c @@ -90,14 +90,14 @@ static ssh_buffer gzip_compress(ssh_session session, ssh_buffer source, int leve zout->avail_out = BLOCKSIZE; status = deflate(zout, Z_PARTIAL_FLUSH); if (status != Z_OK) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); ssh_set_error(session, SSH_FATAL, "status %d deflating zlib packet", status); return NULL; } len = BLOCKSIZE - zout->avail_out; if (ssh_buffer_add_data(dest, out_buf, len) < 0) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return NULL; } zout->next_out = out_buf; @@ -115,16 +115,16 @@ int compress_buffer(ssh_session session, ssh_buffer buf) { } if (ssh_buffer_reinit(buf) < 0) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return -1; } if (ssh_buffer_add_data(buf, ssh_buffer_get(dest), ssh_buffer_get_len(dest)) < 0) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return -1; } - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return 0; } @@ -162,6 +162,10 @@ static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t int status; crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto == NULL) { + return NULL; + } + zin = crypto->compress_in_ctx; if (zin == NULL) { zin = crypto->compress_in_ctx = initdecompress(session); @@ -185,18 +189,18 @@ static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t if (status != Z_OK && status != Z_BUF_ERROR) { ssh_set_error(session, SSH_FATAL, "status %d inflating zlib packet", status); - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return NULL; } len = BLOCKSIZE - zin->avail_out; if (ssh_buffer_add_data(dest,out_buf,len) < 0) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return NULL; } if (ssh_buffer_get_len(dest) > maxlen){ /* Size of packet exceeded, avoid a denial of service attack */ - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return NULL; } zin->next_out = out_buf; @@ -214,15 +218,15 @@ int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){ } if (ssh_buffer_reinit(buf) < 0) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return -1; } if (ssh_buffer_add_data(buf, ssh_buffer_get(dest), ssh_buffer_get_len(dest)) < 0) { - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return -1; } - ssh_buffer_free(dest); + SSH_BUFFER_FREE(dest); return 0; } diff --git a/src/init.c b/src/init.c index a8dfe03..9e70bf6 100644 --- a/src/init.c +++ b/src/init.c @@ -106,7 +106,6 @@ _ret: * * This functions is automatically called when the library is loaded. * - * @returns 0 on success, -1 if an error occured. */ void libssh_constructor(void) { @@ -188,8 +187,6 @@ _ret: * * This function is automatically called when the library is unloaded. * - * @returns SSH_OK on success, SSH_ERROR if an error occurred. - * */ void libssh_destructor(void) { diff --git a/src/kex.c b/src/kex.c index 6ea5e8b..80b6e8a 100644 --- a/src/kex.c +++ b/src/kex.c @@ -154,13 +154,12 @@ ECDH \ "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512," \ GEX_SHA256 \ + "diffie-hellman-group14-sha256," \ "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" #define KEY_EXCHANGE_SUPPORTED \ GEX_SHA1 \ KEY_EXCHANGE -#define KEX_METHODS_SIZE 10 - /* RFC 8308 */ #define KEX_EXTENSION_CLIENT "ext-info-c" @@ -185,6 +184,7 @@ "ecdh-sha2-nistp384,"\ "ecdh-sha2-nistp521,"\ "diffie-hellman-group-exchange-sha256,"\ + "diffie-hellman-group14-sha256,"\ "diffie-hellman-group16-sha512,"\ "diffie-hellman-group18-sha512" @@ -257,7 +257,7 @@ static const char *ssh_kex_descriptions[] = { const char *ssh_kex_get_default_methods(uint32_t algo) { - if (algo >= KEX_METHODS_SIZE) { + if (algo >= SSH_KEX_METHODS) { return NULL; } @@ -266,7 +266,7 @@ const char *ssh_kex_get_default_methods(uint32_t algo) const char *ssh_kex_get_supported_method(uint32_t algo) { - if (algo >= KEX_METHODS_SIZE) { + if (algo >= SSH_KEX_METHODS) { return NULL; } @@ -274,7 +274,7 @@ const char *ssh_kex_get_supported_method(uint32_t algo) } const char *ssh_kex_get_description(uint32_t algo) { - if (algo >= KEX_METHODS_SIZE) { + if (algo >= SSH_KEX_METHODS) { return NULL; } @@ -282,7 +282,7 @@ const char *ssh_kex_get_description(uint32_t algo) { } const char *ssh_kex_get_fips_methods(uint32_t algo) { - if (algo >= KEX_METHODS_SIZE) { + if (algo >= SSH_KEX_METHODS) { return NULL; } @@ -333,9 +333,10 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) int i, ok; int server_kex = session->server; ssh_string str = NULL; - char *strings[KEX_METHODS_SIZE] = {0}; + char *strings[SSH_KEX_METHODS] = {0}; char *rsa_sig_ext = NULL; int rc = SSH_ERROR; + size_t len; uint8_t first_kex_packet_follows = 0; uint32_t kexinit_reserved = 0; @@ -351,32 +352,32 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) } if (server_kex) { - rc = ssh_buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16); - if (rc != 16) { + len = ssh_buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16); + if (len != 16) { ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); goto error; } - rc = ssh_hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie); - if (rc < 0) { + ok = ssh_hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie); + if (ok < 0) { ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); goto error; } } else { - rc = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16); - if (rc != 16) { + len = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16); + if (len != 16) { ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); goto error; } - rc = ssh_hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie); - if (rc < 0) { + ok = ssh_hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie); + if (ok < 0) { ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); goto error; } } - for (i = 0; i < KEX_METHODS_SIZE; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { str = ssh_buffer_get_ssh_string(packet); if (str == NULL) { goto error; @@ -393,7 +394,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) ssh_set_error_oom(session); goto error; } - ssh_string_free(str); + SSH_STRING_FREE(str); str = NULL; } @@ -528,7 +529,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) return SSH_PACKET_USED; error: - ssh_string_free(str); + SSH_STRING_FREE(str); for (i = 0; i < SSH_KEX_METHODS; i++) { if (server_kex) { session->next_crypto->client_kex.methods[i] = NULL; @@ -547,7 +548,7 @@ void ssh_list_kex(struct ssh_kex_struct *kex) { int i = 0; #ifdef DEBUG_CRYPTO - ssh_print_hexa("session cookie", kex->cookie, 16); + ssh_log_hexdump("session cookie", kex->cookie, 16); #endif for(i = 0; i < SSH_KEX_METHODS; i++) { @@ -561,103 +562,94 @@ void ssh_list_kex(struct ssh_kex_struct *kex) { /** * @internal + * * @brief selects the hostkey mechanisms to be chosen for the key exchange, - * as some hostkey mechanisms may be present in known_hosts file and preferred + * as some hostkey mechanisms may be present in known_hosts files. + * * @returns a cstring containing a comma-separated list of hostkey methods. * NULL if no method matches */ char *ssh_client_select_hostkeys(ssh_session session) { - char methods_buffer[128]={0}; - char tail_buffer[128]={0}; + const char *wanted = NULL; + char *wanted_without_certs = NULL; + char *known_hosts_algorithms = NULL; + char *known_hosts_ordered = NULL; char *new_hostkeys = NULL; - static const char *preferred_hostkeys[] = { - "ssh-ed25519", - "ecdsa-sha2-nistp521", - "ecdsa-sha2-nistp384", - "ecdsa-sha2-nistp256", - "rsa-sha2-512", - "rsa-sha2-256", - "ssh-rsa", -#ifdef HAVE_DSA - "ssh-dss", -#endif - NULL - }; - struct ssh_list *algo_list = NULL; - struct ssh_iterator *it = NULL; - size_t algo_count; - int needcomma = 0; - size_t i, len; - - algo_list = ssh_known_hosts_get_algorithms(session); - if (algo_list == NULL) { - return NULL; + char *fips_hostkeys = NULL; + + wanted = session->opts.wanted_methods[SSH_HOSTKEYS]; + if (wanted == NULL) { + if (ssh_fips_mode()) { + wanted = ssh_kex_get_fips_methods(SSH_HOSTKEYS); + } else { + wanted = ssh_kex_get_default_methods(SSH_HOSTKEYS); + } } - algo_count = ssh_list_count(algo_list); - if (algo_count == 0) { - ssh_list_free(algo_list); + /* This removes the certificate types, unsupported for now */ + wanted_without_certs = ssh_find_all_matching(HOSTKEYS, wanted); + if (wanted_without_certs == NULL) { + SSH_LOG(SSH_LOG_WARNING, + "List of allowed host key algorithms is empty or contains only " + "unsupported algorithms"); return NULL; } - for (i = 0; preferred_hostkeys[i] != NULL; ++i) { - bool found = false; - /* This is a signature type: We list also the SHA2 extensions */ - enum ssh_keytypes_e base_preferred = - ssh_key_type_from_signature_name(preferred_hostkeys[i]); - - for (it = ssh_list_get_iterator(algo_list); - it != NULL; - it = it->next) { - const char *algo = ssh_iterator_value(const char *, it); - /* This is always key type so we do not have to care for the - * SHA2 extension */ - enum ssh_keytypes_e base_algo = ssh_key_type_from_name(algo); - - if (base_preferred == base_algo) { - /* Matching the keys already verified it is a known type */ - if (needcomma) { - strncat(methods_buffer, - ",", - sizeof(methods_buffer) - strlen(methods_buffer) - 1); - } - strncat(methods_buffer, - preferred_hostkeys[i], - sizeof(methods_buffer) - strlen(methods_buffer) - 1); - needcomma = 1; - found = true; - } - } - /* Collect the rest of the algorithms in other buffer, that will - * follow the preferred buffer. This will signalize all the algorithms - * we are willing to accept. - */ - if (!found) { - snprintf(tail_buffer + strlen(tail_buffer), - sizeof(tail_buffer) - strlen(tail_buffer), - ",%s", preferred_hostkeys[i]); - } + SSH_LOG(SSH_LOG_DEBUG, + "Order of wanted host keys: \"%s\"", + wanted_without_certs); + + known_hosts_algorithms = ssh_known_hosts_get_algorithms_names(session); + if (known_hosts_algorithms == NULL) { + SSH_LOG(SSH_LOG_DEBUG, + "No key found in known_hosts; " + "changing host key method to \"%s\"", + wanted_without_certs); + + return wanted_without_certs; } - ssh_list_free(algo_list); - if (strlen(methods_buffer) == 0) { + SSH_LOG(SSH_LOG_DEBUG, + "Algorithms found in known_hosts files: \"%s\"", + known_hosts_algorithms); + + /* Filter and order the keys from known_hosts according to wanted list */ + known_hosts_ordered = ssh_find_all_matching(known_hosts_algorithms, + wanted_without_certs); + SAFE_FREE(known_hosts_algorithms); + if (known_hosts_ordered == NULL) { SSH_LOG(SSH_LOG_DEBUG, - "No supported kex method for existing key in known_hosts file"); - return NULL; + "No key found in known_hosts is allowed; " + "changing host key method to \"%s\"", + wanted_without_certs); + + return wanted_without_certs; } - /* Append the supported list to the preferred. - * The length is maximum 128 + 128 + 1, which will not overflow - */ - len = strlen(methods_buffer) + strlen(tail_buffer) + 1; - new_hostkeys = malloc(len); + /* Append the other supported keys after the preferred ones + * This function tolerates NULL pointers in parameters */ + new_hostkeys = ssh_append_without_duplicates(known_hosts_ordered, + wanted_without_certs); + SAFE_FREE(known_hosts_ordered); + SAFE_FREE(wanted_without_certs); if (new_hostkeys == NULL) { ssh_set_error_oom(session); return NULL; } - snprintf(new_hostkeys, len, - "%s%s", methods_buffer, tail_buffer); + + if (ssh_fips_mode()) { + /* Filter out algorithms not allowed in FIPS mode */ + fips_hostkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, new_hostkeys); + SAFE_FREE(new_hostkeys); + if (fips_hostkeys == NULL) { + SSH_LOG(SSH_LOG_WARNING, + "None of the wanted host keys or keys in known_hosts files " + "is allowed in FIPS mode."); + return NULL; + } + new_hostkeys = fips_hostkeys; + } SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", @@ -672,7 +664,7 @@ char *ssh_client_select_hostkeys(ssh_session session) */ int ssh_set_client_kex(ssh_session session) { - struct ssh_kex_struct *client= &session->next_crypto->client_kex; + struct ssh_kex_struct *client = &session->next_crypto->client_kex; const char *wanted; char *kex = NULL; char *kex_tmp = NULL; @@ -686,15 +678,23 @@ int ssh_set_client_kex(ssh_session session) return SSH_ERROR; } - memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **)); - /* first check if we have specific host key methods */ - if (session->opts.wanted_methods[SSH_HOSTKEYS] == NULL) { - /* Only if no override */ - session->opts.wanted_methods[SSH_HOSTKEYS] = - ssh_client_select_hostkeys(session); - } + memset(client->methods, 0, SSH_KEX_METHODS * sizeof(char **)); + + /* Set the list of allowed algorithms in order of preference, if it hadn't + * been set yet. */ + for (i = 0; i < SSH_KEX_METHODS; i++) { + if (i == SSH_HOSTKEYS) { + /* Set the hostkeys in the following order: + * - First: keys present in known_hosts files ordered by preference + * - Next: other wanted algorithms ordered by preference */ + client->methods[i] = ssh_client_select_hostkeys(session); + if (client->methods[i] == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + continue; + } - for (i = 0; i < KEX_METHODS_SIZE; i++) { wanted = session->opts.wanted_methods[i]; if (wanted == NULL) { if (ssh_fips_mode()) { @@ -751,7 +751,7 @@ int ssh_kex_select_methods (ssh_session session){ ext_start[0] = '\0'; } - for (i = 0; i < KEX_METHODS_SIZE; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]); if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){ ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]", @@ -766,6 +766,8 @@ int ssh_kex_select_methods (ssh_session session){ session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){ session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha256") == 0){ + session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA256; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group16-sha512") == 0){ session->next_crypto->kex_type=SSH_KEX_DH_GROUP16_SHA512; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group18-sha512") == 0){ @@ -824,7 +826,7 @@ int ssh_send_kex(ssh_session session, int server_kex) { ssh_list_kex(kex); - for (i = 0; i < KEX_METHODS_SIZE; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { str = ssh_string_from_char(kex->methods[i]); if (str == NULL) { goto error; @@ -836,7 +838,7 @@ int ssh_send_kex(ssh_session session, int server_kex) { if (ssh_buffer_add_ssh_string(session->out_buffer, str) < 0) { goto error; } - ssh_string_free(str); + SSH_STRING_FREE(str); str = NULL; } @@ -857,7 +859,7 @@ int ssh_send_kex(ssh_session session, int server_kex) { error: ssh_buffer_reinit(session->out_buffer); ssh_buffer_reinit(session->out_hashbuf); - ssh_string_free(str); + SSH_STRING_FREE(str); return -1; } @@ -1020,7 +1022,7 @@ int ssh_make_sessionid(ssh_session session) ssh_buffer_get_len(server_hash), ssh_buffer_get(server_hash), server_pubkey_blob); - ssh_string_free(server_pubkey_blob); + SSH_STRING_FREE(server_pubkey_blob); if(rc != SSH_OK){ goto error; } @@ -1028,6 +1030,7 @@ int ssh_make_sessionid(ssh_session session) switch(session->next_crypto->kex_type) { case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP14_SHA1: + case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP18_SHA512: rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx, @@ -1120,7 +1123,7 @@ int ssh_make_sessionid(ssh_session session) } #ifdef DEBUG_CRYPTO - ssh_print_hexa("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf)); + ssh_log_hexdump("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf)); #endif switch (session->next_crypto->kex_type) { @@ -1139,6 +1142,7 @@ int ssh_make_sessionid(ssh_session session) sha1(ssh_buffer_get(buf), ssh_buffer_get_len(buf), session->next_crypto->secret_hash); break; + case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_ECDH_SHA2_NISTP256: case SSH_KEX_CURVE25519_SHA256: case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: @@ -1196,20 +1200,20 @@ int ssh_make_sessionid(ssh_session session) } #ifdef DEBUG_CRYPTO printf("Session hash: \n"); - ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); - ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len); + ssh_log_hexdump("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); + ssh_log_hexdump("session id", session->next_crypto->session_id, session->next_crypto->digest_len); #endif rc = SSH_OK; error: - ssh_buffer_free(buf); - ssh_buffer_free(client_hash); - ssh_buffer_free(server_hash); + SSH_BUFFER_FREE(buf); + SSH_BUFFER_FREE(client_hash); + SSH_BUFFER_FREE(server_hash); session->in_hashbuf = NULL; session->out_hashbuf = NULL; - ssh_string_free(num); + SSH_STRING_FREE(num); return rc; } @@ -1384,22 +1388,22 @@ int ssh_generate_session_keys(ssh_session session) } #ifdef DEBUG_CRYPTO - ssh_print_hexa("Client to Server IV", IV_cli_to_srv, IV_len); - ssh_print_hexa("Server to Client IV", IV_srv_to_cli, IV_len); - ssh_print_hexa("Client to Server Encryption Key", enckey_cli_to_srv, + ssh_log_hexdump("Client to Server IV", IV_cli_to_srv, IV_len); + ssh_log_hexdump("Server to Client IV", IV_srv_to_cli, IV_len); + ssh_log_hexdump("Client to Server Encryption Key", enckey_cli_to_srv, enckey_cli_to_srv_len); - ssh_print_hexa("Server to Client Encryption Key", enckey_srv_to_cli, + ssh_log_hexdump("Server to Client Encryption Key", enckey_srv_to_cli, enckey_srv_to_cli_len); - ssh_print_hexa("Client to Server Integrity Key", intkey_cli_to_srv, + ssh_log_hexdump("Client to Server Integrity Key", intkey_cli_to_srv, intkey_cli_to_srv_len); - ssh_print_hexa("Server to Client Integrity Key", intkey_srv_to_cli, + ssh_log_hexdump("Server to Client Integrity Key", intkey_srv_to_cli, intkey_srv_to_cli_len); #endif rc = 0; error: ssh_string_burn(k_string); - ssh_string_free(k_string); + SSH_STRING_FREE(k_string); if (rc != 0) { free(IV_cli_to_srv); free(IV_srv_to_cli); diff --git a/src/known_hosts.c b/src/known_hosts.c index 8052090..ec6da30 100644 --- a/src/known_hosts.c +++ b/src/known_hosts.c @@ -405,8 +405,12 @@ int ssh_is_server_known(ssh_session session) if ((ret == SSH_SERVER_NOT_KNOWN) && (session->opts.StrictHostKeyChecking == 0)) { - ssh_write_knownhost(session); - ret = SSH_SERVER_KNOWN_OK; + int rv = ssh_session_update_known_hosts(session); + if (rv != SSH_OK) { + ret = SSH_SERVER_ERROR; + } else { + ret = SSH_SERVER_KNOWN_OK; + } } SAFE_FREE(host); @@ -492,10 +496,12 @@ char * ssh_dump_knownhost(ssh_session session) { * @deprecated Please use ssh_session_update_known_hosts() * @brief This function is deprecated */ -int ssh_write_knownhost(ssh_session session) { +int ssh_write_knownhost(ssh_session session) +{ FILE *file; - char *buffer; + char *buffer = NULL; char *dir; + int rc; if (session->opts.knownhosts == NULL) { if (ssh_options_apply(session) < 0) { @@ -504,33 +510,45 @@ int ssh_write_knownhost(ssh_session session) { } } - /* Check if directory exists and create it if not */ - dir = ssh_dirname(session->opts.knownhosts); - if (dir == NULL) { - ssh_set_error(session, SSH_FATAL, "%s", strerror(errno)); - return SSH_ERROR; - } + errno = 0; + file = fopen(session->opts.knownhosts, "a"); + if (file == NULL) { + if (errno == ENOENT) { + dir = ssh_dirname(session->opts.knownhosts); + if (dir == NULL) { + ssh_set_error(session, SSH_FATAL, "%s", strerror(errno)); + return SSH_ERROR; + } - if (!ssh_file_readaccess_ok(dir)) { - if (ssh_mkdir(dir, 0700) < 0) { - ssh_set_error(session, SSH_FATAL, - "Cannot create %s directory.", dir); + rc = ssh_mkdirs(dir, 0700); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "Cannot create %s directory: %s", + dir, strerror(errno)); + SAFE_FREE(dir); + return SSH_ERROR; + } SAFE_FREE(dir); + + errno = 0; + file = fopen(session->opts.knownhosts, "a"); + if (file == NULL) { + ssh_set_error(session, SSH_FATAL, + "Couldn't open known_hosts file %s" + " for appending: %s", + session->opts.knownhosts, strerror(errno)); + return SSH_ERROR; + } + } else { + ssh_set_error(session, SSH_FATAL, + "Couldn't open known_hosts file %s for appending: %s", + session->opts.knownhosts, strerror(errno)); return SSH_ERROR; } } - SAFE_FREE(dir); - - file = fopen(session->opts.knownhosts, "a"); - if (file == NULL) { - ssh_set_error(session, SSH_FATAL, - "Couldn't open known_hosts file %s for appending: %s", - session->opts.knownhosts, strerror(errno)); - return SSH_ERROR; - } - buffer = ssh_dump_knownhost(session); - if (buffer == NULL) { + rc = ssh_session_export_known_hosts_entry(session, &buffer); + if (rc != SSH_OK) { fclose(file); return SSH_ERROR; } diff --git a/src/knownhosts.c b/src/knownhosts.c index 8a4a8ba..fed75f9 100644 --- a/src/knownhosts.c +++ b/src/knownhosts.c @@ -42,6 +42,7 @@ #include "libssh/pki.h" #include "libssh/dh.h" #include "libssh/knownhosts.h" +#include "libssh/token.h" /** * @addtogroup libssh_session @@ -128,8 +129,8 @@ static int match_hashed_hostname(const char *host, const char *hashed_host) error: free(hashed); - ssh_buffer_free(salt); - ssh_buffer_free(hash); + SSH_BUFFER_FREE(salt); + SSH_BUFFER_FREE(hash); return match; } @@ -452,6 +453,146 @@ error: } /** + * @internal + * + * @brief Returns a static string containing a list of the signature types the + * given key type can generate. + * + * @returns A static cstring containing the signature types the key is able to + * generate separated by commas; NULL in case of error + */ +static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e type) +{ + switch (type) { + case SSH_KEYTYPE_RSA: + return "rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + case SSH_KEYTYPE_ED25519: + return "ssh-ed25519"; +#ifdef HAVE_DSA + case SSH_KEYTYPE_DSS: + return "ssh-dss"; +#endif +#ifdef HAVE_ECDH + case SSH_KEYTYPE_ECDSA_P256: + return "ecdsa-sha2-nistp256"; + case SSH_KEYTYPE_ECDSA_P384: + return "ecdsa-sha2-nistp384"; + case SSH_KEYTYPE_ECDSA_P521: + return "ecdsa-sha2-nistp521"; +#endif + case SSH_KEYTYPE_UNKNOWN: + default: + SSH_LOG(SSH_LOG_WARN, "The given type %d is not a base private key type " + "or is unsupported", type); + return NULL; + } +} + +/** + * @internal + * @brief Get the host keys algorithms identifiers from the known_hosts files + * + * This expands the signatures types that can be generated from the keys types + * present in the known_hosts files + * + * @param[in] session The ssh session to use. + * + * @return A newly allocated cstring containing a list of signature algorithms + * that can be generated by the host using the keys listed in the known_hosts + * files, NULL on error. + */ +char *ssh_known_hosts_get_algorithms_names(ssh_session session) +{ + char methods_buffer[256 + 1] = {0}; + struct ssh_list *entry_list = NULL; + struct ssh_iterator *it = NULL; + char *host_port = NULL; + size_t count; + bool needcomma = false; + char *names; + + int rc; + + if (session->opts.knownhosts == NULL || + session->opts.global_knownhosts == NULL) { + if (ssh_options_apply(session) < 0) { + ssh_set_error(session, + SSH_REQUEST_DENIED, + "Can't find a known_hosts file"); + + return NULL; + } + } + + host_port = ssh_session_get_host_port(session); + if (host_port == NULL) { + return NULL; + } + + rc = ssh_known_hosts_read_entries(host_port, + session->opts.knownhosts, + &entry_list); + if (rc != 0) { + SAFE_FREE(host_port); + ssh_list_free(entry_list); + return NULL; + } + + rc = ssh_known_hosts_read_entries(host_port, + session->opts.global_knownhosts, + &entry_list); + SAFE_FREE(host_port); + if (rc != 0) { + ssh_list_free(entry_list); + return NULL; + } + + if (entry_list == NULL) { + return NULL; + } + + count = ssh_list_count(entry_list); + if (count == 0) { + ssh_list_free(entry_list); + return NULL; + } + + for (it = ssh_list_get_iterator(entry_list); + it != NULL; + it = ssh_list_get_iterator(entry_list)) + { + struct ssh_knownhosts_entry *entry = NULL; + const char *algo = NULL; + + entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it); + algo = ssh_known_host_sigs_from_hostkey_type(entry->publickey->type); + if (algo == NULL) { + continue; + } + + if (needcomma) { + strncat(methods_buffer, + ",", + sizeof(methods_buffer) - strlen(methods_buffer) - 1); + } + + strncat(methods_buffer, + algo, + sizeof(methods_buffer) - strlen(methods_buffer) - 1); + needcomma = true; + + ssh_knownhosts_entry_free(entry); + ssh_list_remove(entry_list, it); + } + + ssh_list_free(entry_list); + + names = ssh_remove_duplicates(methods_buffer); + + return names; +} + +/** * @brief Parse a line from a known_hosts entry into a structure * * This parses an known_hosts entry into a structure with the key in a libssh @@ -691,7 +832,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) return SSH_KNOWN_HOSTS_ERROR; } - if (session->opts.knownhosts != NULL) { + if (known_hosts_found) { rc = ssh_known_hosts_read_entries(host_port, session->opts.knownhosts, &entry_list); @@ -702,17 +843,19 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) } } - if (session->opts.global_knownhosts != NULL) { + if (global_known_hosts_found) { rc = ssh_known_hosts_read_entries(host_port, session->opts.global_knownhosts, &entry_list); - SAFE_FREE(host_port); if (rc != 0) { + SAFE_FREE(host_port); ssh_list_free(entry_list); return SSH_KNOWN_HOSTS_ERROR; } } + SAFE_FREE(host_port); + if (ssh_list_count(entry_list) == 0) { ssh_list_free(entry_list); return SSH_KNOWN_HOSTS_UNKNOWN; @@ -836,34 +979,41 @@ int ssh_session_update_known_hosts(ssh_session session) } } - /* Check if directory exists and create it if not */ - dir = ssh_dirname(session->opts.knownhosts); - if (dir == NULL) { - ssh_set_error(session, SSH_FATAL, "%s", strerror(errno)); - return SSH_ERROR; - } - - rc = ssh_file_readaccess_ok(dir); - if (rc == 0) { - rc = ssh_mkdir(dir, 0700); - } else { - rc = 0; - } - - if (rc != 0) { - ssh_set_error(session, SSH_FATAL, - "Cannot create %s directory.", dir); - SAFE_FREE(dir); - return SSH_ERROR; - } - SAFE_FREE(dir); - + errno = 0; fp = fopen(session->opts.knownhosts, "a"); if (fp == NULL) { - ssh_set_error(session, SSH_FATAL, - "Couldn't open known_hosts file %s for appending: %s", - session->opts.knownhosts, strerror(errno)); - return SSH_ERROR; + if (errno == ENOENT) { + dir = ssh_dirname(session->opts.knownhosts); + if (dir == NULL) { + ssh_set_error(session, SSH_FATAL, "%s", strerror(errno)); + return SSH_ERROR; + } + + rc = ssh_mkdirs(dir, 0700); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "Cannot create %s directory: %s", + dir, strerror(errno)); + SAFE_FREE(dir); + return SSH_ERROR; + } + SAFE_FREE(dir); + + errno = 0; + fp = fopen(session->opts.knownhosts, "a"); + if (fp == NULL) { + ssh_set_error(session, SSH_FATAL, + "Couldn't open known_hosts file %s" + " for appending: %s", + session->opts.knownhosts, strerror(errno)); + return SSH_ERROR; + } + } else { + ssh_set_error(session, SSH_FATAL, + "Couldn't open known_hosts file %s for appending: %s", + session->opts.knownhosts, strerror(errno)); + return SSH_ERROR; + } } rc = ssh_session_export_known_hosts_entry(session, &entry); diff --git a/src/legacy.c b/src/legacy.c index b92ee66..8ae3d92 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -353,7 +353,7 @@ void publickey_free(ssh_public_key key) { case SSH_KEYTYPE_DSS: #ifdef HAVE_LIBGCRYPT gcry_sexp_release(key->dsa_pub); -#elif HAVE_LIBCRYPTO +#elif defined HAVE_LIBCRYPTO DSA_free(key->dsa_pub); #endif break; diff --git a/src/libcrypto.c b/src/libcrypto.c index 24ef07d..eefc98d 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c @@ -686,8 +686,12 @@ static int aes_ctr_set_key(struct ssh_cipher_struct *cipher, void *key, return SSH_OK; } -static void aes_ctr_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len) { +static void +aes_ctr_encrypt(struct ssh_cipher_struct *cipher, + void *in, + void *out, + size_t len) +{ unsigned char tmp_buffer[AES_BLOCK_SIZE]; unsigned int num=0; /* Some things are special with ctr128 : @@ -704,8 +708,12 @@ static void aes_ctr_encrypt(struct ssh_cipher_struct *cipher, void *in, void *ou } static void aes_ctr_cleanup(struct ssh_cipher_struct *cipher){ - explicit_bzero(cipher->aes_key, sizeof(*cipher->aes_key)); - SAFE_FREE(cipher->aes_key); + if (cipher != NULL) { + if (cipher->aes_key != NULL) { + explicit_bzero(cipher->aes_key, sizeof(*cipher->aes_key)); + } + SAFE_FREE(cipher->aes_key); + } } #endif /* HAVE_OPENSSL_EVP_AES_CTR */ diff --git a/src/libgcrypt.c b/src/libgcrypt.c index df19239..8fbf215 100644 --- a/src/libgcrypt.c +++ b/src/libgcrypt.c @@ -328,6 +328,10 @@ static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { return -1; } break; + default: + SSH_LOG(SSH_LOG_WARNING, "Unksupported key length %u.", cipher->keysize); + SAFE_FREE(cipher->key); + return -1; } if (gcry_cipher_setkey(cipher->key[0], key, cipher->keysize / 8)) { SAFE_FREE(cipher->key); @@ -738,7 +742,7 @@ ssh_string ssh_sexp_extract_mpi(const gcry_sexp_t sexp, err = gcry_mpi_print(outformat, ssh_string_data(result), size, NULL, mpi); if (err != 0) { ssh_string_burn(result); - ssh_string_free(result); + SSH_STRING_FREE(result); result = NULL; goto fail; } diff --git a/src/match.c b/src/match.c index 06587b9..1a60d73 100644 --- a/src/match.c +++ b/src/match.c @@ -38,16 +38,21 @@ #include "config.h" #include +#include #include #include "libssh/priv.h" +#define MAX_MATCH_RECURSION 32 + /* * Returns true if the given string matches the pattern (which may contain ? * and * as wildcards), and zero if it does not match. */ -static int match_pattern(const char *s, const char *pattern) { - if (s == NULL || pattern == NULL) { +static int match_pattern(const char *s, const char *pattern, size_t limit) +{ + bool had_asterisk = false; + if (s == NULL || pattern == NULL || limit <= 0) { return 0; } @@ -57,23 +62,26 @@ static int match_pattern(const char *s, const char *pattern) { return (*s == '\0'); } - if (*pattern == '*') { + while (*pattern == '*') { /* Skip the asterisk. */ + had_asterisk = true; pattern++; + } + if (had_asterisk) { /* If at end of pattern, accept immediately. */ if (!*pattern) return 1; /* If next character in pattern is known, optimize. */ - if (*pattern != '?' && *pattern != '*') { + if (*pattern != '?') { /* * Look instances of the next character in * pattern, and try to match starting from * those. */ for (; *s; s++) - if (*s == *pattern && match_pattern(s + 1, pattern + 1)) { + if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) { return 1; } /* Failed. */ @@ -84,7 +92,7 @@ static int match_pattern(const char *s, const char *pattern) { * match at each position. */ for (; *s; s++) { - if (match_pattern(s, pattern)) { + if (match_pattern(s, pattern, limit - 1)) { return 1; } } @@ -161,7 +169,7 @@ int match_pattern_list(const char *string, const char *pattern, sub[subi] = '\0'; /* Try to match the subpattern against the string. */ - if (match_pattern(string, sub)) { + if (match_pattern(string, sub, MAX_MATCH_RECURSION)) { if (negated) { return -1; /* Negative */ } else { diff --git a/src/mbedcrypto_missing.c b/src/mbedcrypto_missing.c index be290d2..ac0d688 100644 --- a/src/mbedcrypto_missing.c +++ b/src/mbedcrypto_missing.c @@ -45,7 +45,7 @@ void ssh_mbedcry_bn_free(bignum bn) SAFE_FREE(bn); } -unsigned char *ssh_mbedcry_bn2num(bignum num, int radix) +unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix) { char *buf = NULL; size_t olen; @@ -104,6 +104,9 @@ int ssh_mbedcry_rand(bignum rnd, int bits, int top, int bottom) if (top == 0) { rc = mbedtls_mpi_set_bit(rnd, bits - 1, 0); + if (rc != 0) { + return 0; + } } if (top == 1) { @@ -149,7 +152,8 @@ int ssh_mbedcry_rand_range(bignum dest, bignum max) return 0; } rc = bignum_rand(rnd, bits); - if (rc != 1){ + if (rc != 1) { + bignum_safe_free(rnd); return rc; } mbedtls_mpi_mod_mpi(dest, rnd, max); diff --git a/src/messages.c b/src/messages.c index 1c2a942..25683b2 100644 --- a/src/messages.c +++ b/src/messages.c @@ -71,6 +71,9 @@ static ssh_message ssh_message_new(ssh_session session) } msg->session = session; + /* Set states explicitly */ + msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_NONE; + return msg; } @@ -689,6 +692,9 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, int rc; crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto == NULL) { + return NULL; + } buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -696,7 +702,7 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, } rc = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &str); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -712,10 +718,10 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, ssh_string_get_char(algo), /* pubkey algorithm */ str); /* public key as a blob */ - ssh_string_free(str); + SSH_STRING_FREE(str); if (rc != SSH_OK) { ssh_set_error_oom(session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -797,7 +803,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ * 3.1. Initial Exchange * "The language tag is deprecated and SHOULD be the empty string." */ - ssh_string_free(lang); + SSH_STRING_FREE(lang); submethods = ssh_buffer_get_ssh_string(packet); if (submethods == NULL) { @@ -809,7 +815,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ * server is that, unless the user may use multiple different * submethods, the server ignores this field." */ - ssh_string_free(submethods); + SSH_STRING_FREE(submethods); goto end; } @@ -832,10 +838,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ } rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); pubkey_blob = NULL; if (rc < 0) { - ssh_string_free(algo); + SSH_STRING_FREE(algo); algo = NULL; goto error; } @@ -849,16 +855,16 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ if(sig_blob == NULL) { SSH_LOG(SSH_LOG_PACKET, "Invalid signature packet from peer"); msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR; - ssh_string_free(algo); + SSH_STRING_FREE(algo); algo = NULL; goto error; } digest = ssh_msg_userauth_build_digest(session, msg, service, algo); - ssh_string_free(algo); + SSH_STRING_FREE(algo); algo = NULL; if (digest == NULL) { - ssh_string_free(sig_blob); + SSH_STRING_FREE(sig_blob); SSH_LOG(SSH_LOG_PACKET, "Failed to get digest"); msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; goto error; @@ -891,8 +897,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ ssh_buffer_get_len(digest)); } } - ssh_string_free(sig_blob); - ssh_buffer_free(digest); + SSH_STRING_FREE(sig_blob); + SSH_BUFFER_FREE(digest); ssh_signature_free(sig); if (rc < 0) { SSH_LOG( @@ -906,7 +912,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID; } - ssh_string_free(algo); + SSH_STRING_FREE(algo); goto end; } #ifdef WITH_GSSAPI @@ -1094,7 +1100,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ goto error; } session->kbdint->answers[i] = ssh_string_to_char(tmp); - ssh_string_free(tmp); + SSH_STRING_FREE(tmp); if (session->kbdint->answers[i] == NULL) { ssh_set_error_oom(session); session->kbdint->nanswers = i; diff --git a/src/misc.c b/src/misc.c index 0c6b570..0f1a7d4 100644 --- a/src/misc.c +++ b/src/misc.c @@ -130,6 +130,31 @@ int ssh_file_readaccess_ok(const char *file) { return 1; } +/** + * @brief Check if the given path is an existing directory and that is + * accessible for writing. + * + * @param[in] path Path to the directory to be checked + * + * @return Return 1 if the directory exists and is accessible; 0 otherwise + * */ +int ssh_dir_writeable(const char *path) +{ + struct _stat buffer; + int rc; + + rc = _stat(path, &buffer); + if (rc < 0) { + return 0; + } + + if ((buffer.st_mode & _S_IFDIR) && (buffer.st_mode & _S_IWRITE)) { + return 1; + } + + return 0; +} + #define SSH_USEC_IN_SEC 1000000LL #define SSH_SECONDS_SINCE_1601 11644473600LL @@ -247,6 +272,31 @@ int ssh_file_readaccess_ok(const char *file) return 1; } +/** + * @brief Check if the given path is an existing directory and that is + * accessible for writing. + * + * @param[in] path Path to the directory to be checked + * + * @return Return 1 if the directory exists and is accessible; 0 otherwise + * */ +int ssh_dir_writeable(const char *path) +{ + struct stat buffer; + int rc; + + rc = stat(path, &buffer); + if (rc < 0) { + return 0; + } + + if (S_ISDIR(buffer.st_mode) && (buffer.st_mode & S_IWRITE)) { + return 1; + } + + return 0; +} + char *ssh_get_local_username(void) { struct passwd pwd; @@ -390,6 +440,193 @@ void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) { } /** + * @brief Log the content of a buffer in hexadecimal format, similar to the + * output of 'hexdump -C' command. + * + * The first logged line is the given description followed by the length. + * Then the content of the buffer is logged 16 bytes per line in the following + * format: + * + * (offset) (first 8 bytes) (last 8 bytes) (the 16 bytes as ASCII char values) + * + * The output for a 16 bytes array containing values from 0x00 to 0x0f would be: + * + * "Example (16 bytes):" + * " 00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................" + * + * The value for each byte as corresponding ASCII character is printed at the + * end if the value is printable. Otherwise it is replace with '.'. + * + * @param[in] descr A description for the content to be logged + * @param[in] what The buffer to be logged + * @param[in] len The length of the buffer given in what + * + * @note If a too long description is provided (which would result in a first + * line longer than 80 bytes), the function will fail. + */ +void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len) +{ + size_t i; + char ascii[17]; + const unsigned char *pc = NULL; + size_t count = 0; + ssize_t printed = 0; + + /* The required buffer size is calculated from: + * + * 2 bytes for spaces at the beginning + * 8 bytes for the offset + * 2 bytes for spaces + * 24 bytes to print the first 8 bytes + spaces + * 1 byte for an extra space + * 24 bytes to print next 8 bytes + spaces + * 2 bytes for extra spaces + * 16 bytes for the content as ASCII characters at the end + * 1 byte for the ending '\0' + * + * Resulting in 80 bytes. + * + * Except for the first line (description + size), all lines have fixed + * length. If a too long description is used, the function will fail. + * */ + char buffer[80]; + + /* Print description */ + if (descr != NULL) { + printed = snprintf(buffer, sizeof(buffer), "%s ", descr); + if (printed < 0) { + goto error; + } + count += printed; + } else { + printed = snprintf(buffer, sizeof(buffer), "(NULL description) "); + if (printed < 0) { + goto error; + } + count += printed; + } + + if (len == 0) { + printed = snprintf(buffer + count, sizeof(buffer) - count, + "(zero length):"); + if (printed < 0) { + goto error; + } + SSH_LOG(SSH_LOG_DEBUG, "%s", buffer); + return; + } else { + printed = snprintf(buffer + count, sizeof(buffer) - count, + "(%zu bytes):", len); + if (printed < 0) { + goto error; + } + count += printed; + } + + if (what == NULL) { + printed = snprintf(buffer + count, sizeof(buffer) - count, + "(NULL)"); + if (printed < 0) { + goto error; + } + SSH_LOG(SSH_LOG_DEBUG, "%s", buffer); + return; + } + + SSH_LOG(SSH_LOG_DEBUG, "%s", buffer); + + /* Reset state */ + count = 0; + pc = what; + + for (i = 0; i < len; i++) { + /* Add one space after printing 8 bytes */ + if ((i % 8) == 0) { + if (i != 0) { + printed = snprintf(buffer + count, sizeof(buffer) - count, " "); + if (printed < 0) { + goto error; + } + count += printed; + } + } + + /* Log previous line and reset state for new line */ + if ((i % 16) == 0) { + if (i != 0) { + printed = snprintf(buffer + count, sizeof(buffer) - count, + " %s", ascii); + if (printed < 0) { + goto error; + } + SSH_LOG(SSH_LOG_DEBUG, "%s", buffer); + count = 0; + } + + /* Start a new line with the offset */ + printed = snprintf(buffer, sizeof(buffer), + " %08zx ", i); + if (printed < 0) { + goto error; + } + count += printed; + } + + /* Print the current byte hexadecimal representation */ + printed = snprintf(buffer + count, sizeof(buffer) - count, + " %02x", pc[i]); + if (printed < 0) { + goto error; + } + count += printed; + + /* If printable, store the ASCII character */ + if (isprint(pc[i])) { + ascii[i % 16] = pc[i]; + } else { + ascii[i % 16] = '.'; + } + ascii[(i % 16) + 1] = '\0'; + } + + /* Add padding if not exactly 16 characters */ + while ((i % 16) != 0) { + /* Add one space after printing 8 bytes */ + if ((i % 8) == 0) { + if (i != 0) { + printed = snprintf(buffer + count, sizeof(buffer) - count, " "); + if (printed < 0) { + goto error; + } + count += printed; + } + } + + printed = snprintf(buffer + count, sizeof(buffer) - count, " "); + if (printed < 0) { + goto error; + } + count += printed; + i++; + } + + /* Print the last printable part */ + printed = snprintf(buffer + count, sizeof(buffer) - count, + " %s", ascii); + if (printed < 0) { + goto error; + } + + SSH_LOG(SSH_LOG_DEBUG, "%s", buffer); + + return; + +error: + SSH_LOG(SSH_LOG_WARN, "Could not print to buffer"); + return; +} + +/** * @brief Check if libssh is the required version or get the version * string. * @@ -713,16 +950,81 @@ char *ssh_basename (const char *path) { * * @return 0 on success, < 0 on error with errno set. */ -int ssh_mkdir(const char *pathname, mode_t mode) { - int r; +int ssh_mkdir(const char *pathname, mode_t mode) +{ + int r; +#ifdef _WIN32 + r = _mkdir(pathname); +#else + r = mkdir(pathname, mode); +#endif + + return r; +} + +/** + * @brief Attempts to create a directory with the given pathname. The missing + * directories in the given pathname are created recursively. + * + * @param[in] pathname The path name to create the directory. + * + * @param[in] mode The permissions to use. + * + * @return 0 on success, < 0 on error with errno set. + * + * @note mode is ignored on Windows systems. + */ +int ssh_mkdirs(const char *pathname, mode_t mode) +{ + int rc = 0; + char *parent = NULL; + + if (pathname == NULL || + pathname[0] == '\0' || + !strcmp(pathname, "/") || + !strcmp(pathname, ".")) + { + errno = EINVAL; + return -1; + } + errno = 0; + +#ifdef _WIN32 + rc = _mkdir(pathname); +#else + rc = mkdir(pathname, mode); +#endif + + if (rc < 0) { + /* If a directory was missing, try to create the parent */ + if (errno == ENOENT) { + parent = ssh_dirname(pathname); + if (parent == NULL) { + errno = ENOMEM; + return -1; + } + + rc = ssh_mkdirs(parent, mode); + if (rc < 0) { + /* We could not create the parent */ + SAFE_FREE(parent); + return -1; + } + + SAFE_FREE(parent); + + /* Try again */ + errno = 0; #ifdef _WIN32 - r = _mkdir(pathname); + rc = _mkdir(pathname); #else - r = mkdir(pathname, mode); + rc = mkdir(pathname, mode); #endif + } + } - return r; + return rc; } /** @@ -825,6 +1127,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) { for (i = 0; *p != '\0'; p++) { if (*p != '%') { + escape: buf[i] = *p; i++; if (i >= MAX_BUF_SIZE) { @@ -841,6 +1144,8 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) { } switch (*p) { + case '%': + goto escape; case 'd': x = strdup(session->opts.sshdir); break; @@ -1203,4 +1508,231 @@ uint64_inc(unsigned char *counter) } } +/** + * @internal + * + * @brief Quote file name to be used on shell. + * + * Try to put the given file name between single quotes. There are special + * cases: + * + * - When the '\'' char is found in the file name, it is double quoted + * - example: + * input: a'b + * output: 'a'"'"'b' + * - When the '!' char is found in the file name, it is replaced by an unquoted + * verbatim char "\!" + * - example: + * input: a!b + * output 'a'\!'b' + * + * @param[in] file_name File name string to be quoted before used on shell + * @param[out] buf Buffer to receive the final quoted file name. Must + * have room for the final quoted string. The maximum + * output length would be (3 * strlen(file_name) + 1) + * since in the worst case each character would be + * replaced by 3 characters, plus the terminating '\0'. + * @param[in] buf_len The size of the provided output buffer + * + * @returns SSH_ERROR on error; length of the resulting string not counting the + * string terminator '\0' + * */ +int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len) +{ + const char *src = NULL; + char *dst = NULL; + size_t required_buf_len; + + enum ssh_quote_state_e state = NO_QUOTE; + + if (file_name == NULL || buf == NULL || buf_len == 0) { + SSH_LOG(SSH_LOG_WARNING, "Invalid parameter"); + return SSH_ERROR; + } + + /* Only allow file names smaller than 32kb. */ + if (strlen(file_name) > 32 * 1024) { + SSH_LOG(SSH_LOG_WARNING, "File name too long"); + return SSH_ERROR; + } + + /* Paranoia check */ + required_buf_len = (size_t)3 * strlen(file_name) + 1; + if (required_buf_len > buf_len) { + SSH_LOG(SSH_LOG_WARNING, "Buffer too small"); + return SSH_ERROR; + } + + src = file_name; + dst = buf; + + while ((*src != '\0')) { + switch (*src) { + + /* The '\'' char is double quoted */ + + case '\'': + switch (state) { + case NO_QUOTE: + /* Start a new double quoted string. The '\'' char will be + * copied to the beginning of it at the end of the loop. */ + *dst++ = '"'; + break; + case SINGLE_QUOTE: + /* Close the current single quoted string and start a new double + * quoted string. The '\'' char will be copied to the beginning + * of it at the end of the loop. */ + *dst++ = '\''; + *dst++ = '"'; + break; + case DOUBLE_QUOTE: + /* If already in the double quoted string, keep copying the + * sequence of chars. */ + break; + default: + /* Should never be reached */ + goto error; + } + + /* When the '\'' char is found, the resulting state will be + * DOUBLE_QUOTE in any case*/ + state = DOUBLE_QUOTE; + break; + + /* The '!' char is replaced by unquoted "\!" */ + + case '!': + switch (state) { + case NO_QUOTE: + /* The '!' char is interpreted in some shells (e.g. CSH) even + * when is quoted with single quotes. Replace it with unquoted + * "\!" which is correctly interpreted as the '!' character. */ + *dst++ = '\\'; + break; + case SINGLE_QUOTE: + /* Close the current quoted string and replace '!' for unquoted + * "\!" */ + *dst++ = '\''; + *dst++ = '\\'; + break; + case DOUBLE_QUOTE: + /* Close current quoted string and replace "!" for unquoted + * "\!" */ + *dst++ = '"'; + *dst++ = '\\'; + break; + default: + /* Should never be reached */ + goto error; + } + + /* When the '!' char is found, the resulting state will be NO_QUOTE + * in any case*/ + state = NO_QUOTE; + break; + + /* Ordinary chars are single quoted */ + + default: + switch (state) { + case NO_QUOTE: + /* Start a new single quoted string */ + *dst++ = '\''; + break; + case SINGLE_QUOTE: + /* If already in the single quoted string, keep copying the + * sequence of chars. */ + break; + case DOUBLE_QUOTE: + /* Close current double quoted string and start a new single + * quoted string. */ + *dst++ = '"'; + *dst++ = '\''; + break; + default: + /* Should never be reached */ + goto error; + } + + /* When an ordinary char is found, the resulting state will be + * SINGLE_QUOTE in any case*/ + state = SINGLE_QUOTE; + break; + } + + /* Copy the current char to output */ + *dst++ = *src++; + } + + /* Close the quoted string when necessary */ + + switch (state) { + case NO_QUOTE: + /* No open string */ + break; + case SINGLE_QUOTE: + /* Close current single quoted string */ + *dst++ = '\''; + break; + case DOUBLE_QUOTE: + /* Close current double quoted string */ + *dst++ = '"'; + break; + default: + /* Should never be reached */ + goto error; + } + + /* Put the string terminator */ + *dst = '\0'; + + return dst - buf; + +error: + return SSH_ERROR; +} + +/** + * @internal + * + * @brief Given a string, encode existing newlines as the string "\\n" + * + * @param[in] string Input string + * @param[out] buf Output buffer. This buffer must be at least (2 * + * strlen(string)) + 1 long. In the worst case, + * each character can be encoded as 2 characters plus the + * terminating '\0'. + * @param[in] buf_len Size of the provided output buffer + * + * @returns SSH_ERROR on error; length of the resulting string not counting the + * terminating '\0' otherwise + */ +int ssh_newline_vis(const char *string, char *buf, size_t buf_len) +{ + const char *in = NULL; + char *out = NULL; + + if (string == NULL || buf == NULL || buf_len == 0) { + return SSH_ERROR; + } + + if ((2 * strlen(string) + 1) > buf_len) { + SSH_LOG(SSH_LOG_WARNING, "Buffer too small"); + return SSH_ERROR; + } + + out = buf; + for (in = string; *in != '\0'; in++) { + if (*in == '\n') { + *out++ = '\\'; + *out++ = 'n'; + } else { + *out++ = *in; + } + } + *out = '\0'; + + return out - buf; +} + /** @} */ diff --git a/src/options.c b/src/options.c index 672735a..b5f951a 100644 --- a/src/options.c +++ b/src/options.c @@ -154,7 +154,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) } } - for (i = 0; i < 10; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { if (src->opts.wanted_methods[i] != NULL) { new->opts.wanted_methods[i] = strdup(src->opts.wanted_methods[i]); if (new->opts.wanted_methods[i] == NULL) { @@ -531,7 +531,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->opts.port = *x & 0xffff; + session->opts.port = *x & 0xffffU; } break; case SSH_OPTIONS_PORT_STR: @@ -555,7 +555,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->opts.port = i & 0xffff; + session->opts.port = i & 0xffffU; } break; case SSH_OPTIONS_FD: @@ -693,7 +693,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->opts.timeout = *x & 0xffffffff; + session->opts.timeout = *x & 0xffffffffU; } break; case SSH_OPTIONS_TIMEOUT_USEC: @@ -707,7 +707,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->opts.timeout_usec = *x & 0xffffffff; + session->opts.timeout_usec = *x & 0xffffffffU; } break; case SSH_OPTIONS_SSH1: @@ -725,8 +725,8 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->common.log_verbosity = *x & 0xffff; - ssh_set_log_level(*x & 0xffff); + session->common.log_verbosity = *x & 0xffffU; + ssh_set_log_level(*x & 0xffffU); } break; case SSH_OPTIONS_LOG_VERBOSITY_STR: @@ -751,8 +751,8 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->common.log_verbosity = i & 0xffff; - ssh_set_log_level(i & 0xffff); + session->common.log_verbosity = i & 0xffffU; + ssh_set_log_level(i & 0xffffU); } break; case SSH_OPTIONS_CIPHERS_C_S: @@ -968,7 +968,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, case SSH_OPTIONS_PUBKEY_AUTH: case SSH_OPTIONS_KBDINT_AUTH: case SSH_OPTIONS_GSSAPI_AUTH: - u = 0; if (value == NULL) { ssh_set_error_invalid(session); return -1; @@ -1472,6 +1471,13 @@ int ssh_options_apply(ssh_session session) { it != NULL; it = it->next) { char *id = (char *) it->data; + if (strncmp(id, "pkcs11:", 6) == 0) { + /* PKCS#11 URIs are using percent-encoding so we can not mix + * it with ssh expansion of ssh escape characters. + * Skip these identities now, before we will have PKCS#11 support + */ + continue; + } tmp = ssh_path_expand_escape(session, id); if (tmp == NULL) { return -1; @@ -1798,12 +1804,12 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, return -1; } else { int *x = (int *) value; - sshbind->bindport = *x & 0xffff; + sshbind->bindport = *x & 0xffffU; } break; case SSH_BIND_OPTIONS_BINDPORT_STR: if (value == NULL) { - sshbind->bindport = 22 & 0xffff; + sshbind->bindport = 22 & 0xffffU; } else { q = strdup(value); if (q == NULL) { @@ -1816,7 +1822,7 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, } SAFE_FREE(q); - sshbind->bindport = i & 0xffff; + sshbind->bindport = i & 0xffffU; } break; case SSH_BIND_OPTIONS_LOG_VERBOSITY: @@ -1825,7 +1831,7 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, return -1; } else { int *x = (int *) value; - ssh_set_log_level(*x & 0xffff); + ssh_set_log_level(*x & 0xffffU); } break; case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: @@ -1843,7 +1849,7 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, } SAFE_FREE(q); - ssh_set_log_level(i & 0xffff); + ssh_set_log_level(i & 0xffffU); } break; case SSH_BIND_OPTIONS_DSAKEY: diff --git a/src/packet.c b/src/packet.c index 7855866..e9ae564 100644 --- a/src/packet.c +++ b/src/packet.c @@ -939,18 +939,38 @@ struct ssh_crypto_struct * ssh_packet_get_current_crypto(ssh_session session, enum ssh_crypto_direction_e direction) { + struct ssh_crypto_struct *crypto = NULL; + if (session == NULL) { return NULL; } if (session->current_crypto != NULL && session->current_crypto->used & direction) { - return session->current_crypto; + crypto = session->current_crypto; + } else if (session->next_crypto != NULL && + session->next_crypto->used & direction) { + crypto = session->next_crypto; + } else { + return NULL; } - if (session->next_crypto != NULL && - session->next_crypto->used & direction) { - return session->next_crypto; + switch (direction) { + case SSH_DIRECTION_IN: + if (crypto->in_cipher != NULL) { + return crypto; + } + break; + case SSH_DIRECTION_OUT: + if (crypto->out_cipher != NULL) { + return crypto; + } + break; + case SSH_DIRECTION_BOTH: + if (crypto->in_cipher != NULL && + crypto->out_cipher != NULL) { + return crypto; + } } return NULL; @@ -1039,8 +1059,8 @@ static bool ssh_packet_need_rekey(ssh_session session, int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) { ssh_session session = (ssh_session)user; - unsigned int blocksize = 8; - unsigned int lenfield_blocksize = 8; + uint32_t blocksize = 8; + uint32_t lenfield_blocksize = 8; size_t current_macsize = 0; uint8_t *ptr = NULL; int to_be_read; @@ -1055,7 +1075,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) enum ssh_packet_filter_result_e filter_result; struct ssh_crypto_struct *crypto = NULL; bool etm = false; - int etm_packet_offset = 0; + uint32_t etm_packet_offset = 0; bool ok; crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); @@ -1806,7 +1826,7 @@ int ssh_packet_send(ssh_session session) SSH_LOG(SSH_LOG_PACKET, "Queued packet triggered rekey"); return ssh_send_rekex(session); } - ssh_buffer_free(session->out_buffer); + SSH_BUFFER_FREE(session->out_buffer); session->out_buffer = ssh_list_pop_head(struct ssh_buffer_struct *, session->out_queue); payload = (uint8_t *)ssh_buffer_get(session->out_buffer); @@ -1864,6 +1884,10 @@ ssh_packet_set_newkeys(ssh_session session, direction & SSH_DIRECTION_IN ? " IN " : "", direction & SSH_DIRECTION_OUT ? " OUT " : ""); + if (session->next_crypto == NULL) { + return SSH_ERROR; + } + session->next_crypto->used |= direction; if (session->current_crypto != NULL) { if (session->current_crypto->used & direction) { @@ -1929,6 +1953,11 @@ ssh_packet_set_newkeys(ssh_session session, return SSH_ERROR; } + if (session->next_crypto->in_cipher == NULL || + session->next_crypto->out_cipher == NULL) { + return SSH_ERROR; + } + /* Initialize rekeying states */ ssh_init_rekey_state(session, session->next_crypto->out_cipher); @@ -1946,6 +1975,8 @@ ssh_packet_set_newkeys(ssh_session session, session->next_crypto->decryptkey, session->next_crypto->decryptIV); if (rc < 0) { + /* On error, make sure it is not used */ + session->next_crypto->used = 0; return SSH_ERROR; } @@ -1954,6 +1985,8 @@ ssh_packet_set_newkeys(ssh_session session, session->next_crypto->encryptkey, session->next_crypto->encryptIV); if (rc < 0) { + /* On error, make sure it is not used */ + session->next_crypto->used = 0; return SSH_ERROR; } diff --git a/src/packet_cb.c b/src/packet_cb.c index 22051c5..4e69291 100644 --- a/src/packet_cb.c +++ b/src/packet_cb.c @@ -61,7 +61,7 @@ SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){ error_s = ssh_buffer_get_ssh_string(packet); if (error_s != NULL) { error = ssh_string_to_char(error_s); - ssh_string_free(error_s); + SSH_STRING_FREE(error_s); } SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s", code, error != NULL ? error : "no error"); @@ -153,9 +153,8 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ session->next_crypto->secret_hash, session->next_crypto->digest_len); ssh_string_burn(sig_blob); - ssh_string_free(sig_blob); + SSH_STRING_FREE(sig_blob); ssh_signature_free(sig); - sig_blob = NULL; if (rc == SSH_ERROR) { goto error; } diff --git a/src/packet_crypt.c b/src/packet_crypt.c index 2ed097a..95d438c 100644 --- a/src/packet_crypt.c +++ b/src/packet_crypt.c @@ -60,7 +60,7 @@ uint32_t ssh_packet_decrypt_len(ssh_session session, crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); if (crypto != NULL) { if (crypto->in_cipher->aead_decrypt_length != NULL) { - crypto->in_cipher->aead_decrypt_length( + rc = crypto->in_cipher->aead_decrypt_length( crypto->in_cipher, source, destination, crypto->in_cipher->lenfield_blocksize, session->recv_seq); @@ -71,9 +71,9 @@ uint32_t ssh_packet_decrypt_len(ssh_session session, source, 0, crypto->in_cipher->blocksize); - if (rc < 0) { - return 0; - } + } + if (rc < 0) { + return 0; } } else { memcpy(destination, source, 8); @@ -103,6 +103,9 @@ int ssh_packet_decrypt(ssh_session session, } crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto == NULL) { + return SSH_ERROR; + } cipher = crypto->in_cipher; if (encrypted_size % cipher->blocksize != 0) { @@ -196,11 +199,11 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) hmac_final(ctx, crypto->hmacbuf, &finallen); } #ifdef DEBUG_CRYPTO - ssh_print_hexa("mac: ",data,hmac_digest_len(type)); + ssh_log_hexdump("mac: ", data, len); if (finallen != hmac_digest_len(type)) { - printf("Final len is %d\n",finallen); + printf("Final len is %d\n", finallen); } - ssh_print_hexa("Packet hmac", crypto->hmacbuf, hmac_digest_len(type)); + ssh_log_hexdump("Packet hmac", crypto->hmacbuf, hmac_digest_len(type)); #endif } explicit_bzero(out, len); @@ -252,6 +255,10 @@ int ssh_packet_hmac_verify(ssh_session session, } crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto == NULL) { + return SSH_ERROR; + } + ctx = hmac_init(crypto->decryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { return -1; @@ -264,9 +271,9 @@ int ssh_packet_hmac_verify(ssh_session session, hmac_final(ctx, hmacbuf, &hmaclen); #ifdef DEBUG_CRYPTO - ssh_print_hexa("received mac",mac,hmaclen); - ssh_print_hexa("Computed mac",hmacbuf,hmaclen); - ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t)); + ssh_log_hexdump("received mac",mac,hmaclen); + ssh_log_hexdump("Computed mac",hmacbuf,hmaclen); + ssh_log_hexdump("seq",(unsigned char *)&seq,sizeof(uint32_t)); #endif if (secure_memcmp(mac, hmacbuf, hmaclen) == 0) { return 0; diff --git a/src/pcap.c b/src/pcap.c index 93b7892..c089854 100644 --- a/src/pcap.c +++ b/src/pcap.c @@ -193,7 +193,7 @@ int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t o } err=ssh_pcap_file_write(pcap,header); error: - ssh_buffer_free(header); + SSH_BUFFER_FREE(header); return err; } @@ -255,7 +255,7 @@ int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){ } err=ssh_pcap_file_write(pcap,header); error: - ssh_buffer_free(header); + SSH_BUFFER_FREE(header); return err; } @@ -500,7 +500,7 @@ int ssh_pcap_context_write(ssh_pcap_context ctx, rc = ssh_pcap_file_write_packet(ctx->file, ip, origlen + TCPIPHDR_LEN); error: - ssh_buffer_free(ip); + SSH_BUFFER_FREE(ip); return rc; } diff --git a/src/pki.c b/src/pki.c index 482c58c..6dcb120 100644 --- a/src/pki.c +++ b/src/pki.c @@ -162,12 +162,19 @@ void ssh_key_clean (ssh_key key){ } #endif if (key->ed25519_privkey != NULL){ +#ifdef HAVE_OPENSSL_ED25519 + /* In OpenSSL implementation the private key is only the private + * original seed. In the internal implementation the private key is the + * concatenation of the original private seed with the public key.*/ + explicit_bzero(key->ed25519_privkey, ED25519_KEY_LEN); +#else explicit_bzero(key->ed25519_privkey, sizeof(ed25519_privkey)); +#endif SAFE_FREE(key->ed25519_privkey); } SAFE_FREE(key->ed25519_pubkey); if (key->cert != NULL) { - ssh_buffer_free(key->cert); + SSH_BUFFER_FREE(key->cert); } key->cert_type = SSH_KEYTYPE_UNKNOWN; key->flags=SSH_KEY_FLAG_EMPTY; @@ -298,7 +305,7 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) { return NULL; } -static enum ssh_digest_e ssh_key_hash_from_name(const char *name) +enum ssh_digest_e ssh_key_hash_from_name(const char *name) { if (name == NULL) { /* TODO we should rather fail */ @@ -679,7 +686,10 @@ void ssh_signature_free(ssh_signature sig) #endif break; case SSH_KEYTYPE_ED25519: +#ifndef HAVE_OPENSSL_ED25519 + /* When using OpenSSL, the signature is stored in sig->raw_sig */ SAFE_FREE(sig->ed25519_sig); +#endif break; case SSH_KEYTYPE_DSS_CERT01: case SSH_KEYTYPE_RSA_CERT01: @@ -695,7 +705,7 @@ void ssh_signature_free(ssh_signature sig) /* Explicitly zero the signature content before free */ ssh_string_burn(sig->raw_sig); - ssh_string_free(sig->raw_sig); + SSH_STRING_FREE(sig->raw_sig); SAFE_FREE(sig); } @@ -724,7 +734,7 @@ int ssh_pki_import_privkey_base64(const char *b64_key, ssh_key *pkey) { ssh_key key; - int cmp; + char *openssh_header = NULL; if (b64_key == NULL || pkey == NULL) { return SSH_ERROR; @@ -739,9 +749,9 @@ int ssh_pki_import_privkey_base64(const char *b64_key, passphrase ? "true" : "false"); /* Test for OpenSSH key format first */ - cmp = strncmp(b64_key, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN)); - if (cmp == 0) { - key = ssh_pki_openssh_privkey_import(b64_key, + openssh_header = strstr(b64_key, OPENSSH_HEADER_BEGIN); + if (openssh_header != NULL) { + key = ssh_pki_openssh_privkey_import(openssh_header, passphrase, auth_fn, auth_data); @@ -807,7 +817,7 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey, } b64 = strndup(ssh_string_data(blob), ssh_string_len(blob)); - ssh_string_free(blob); + SSH_STRING_FREE(blob); if (b64 == NULL) { return SSH_ERROR; } @@ -969,7 +979,7 @@ int ssh_pki_export_privkey_file(const ssh_key privkey, } rc = fwrite(ssh_string_data(blob), ssh_string_len(blob), 1, fp); - ssh_string_free(blob); + SSH_STRING_FREE(blob); if (rc != 1 || ferror(fp)) { fclose(fp); unlink(filename); @@ -1064,24 +1074,24 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type, rc = pki_privkey_build_dss(key, p, q, g, pubkey, privkey); #ifdef DEBUG_CRYPTO - ssh_print_hexa("p", ssh_string_data(p), ssh_string_len(p)); - ssh_print_hexa("q", ssh_string_data(q), ssh_string_len(q)); - ssh_print_hexa("g", ssh_string_data(g), ssh_string_len(g)); - ssh_print_hexa("pubkey", ssh_string_data(pubkey), + ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p)); + ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q)); + ssh_log_hexdump("g", ssh_string_data(g), ssh_string_len(g)); + ssh_log_hexdump("pubkey", ssh_string_data(pubkey), ssh_string_len(pubkey)); - ssh_print_hexa("privkey", ssh_string_data(privkey), + ssh_log_hexdump("privkey", ssh_string_data(privkey), ssh_string_len(privkey)); #endif ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); ssh_string_burn(pubkey); - ssh_string_free(pubkey); + SSH_STRING_FREE(pubkey); ssh_string_burn(privkey); - ssh_string_free(privkey); + SSH_STRING_FREE(privkey); if (rc == SSH_ERROR) { goto fail; } @@ -1105,26 +1115,26 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type, rc = pki_privkey_build_rsa(key, n, e, d, iqmp, p, q); #ifdef DEBUG_CRYPTO - ssh_print_hexa("n", ssh_string_data(n), ssh_string_len(n)); - ssh_print_hexa("e", ssh_string_data(e), ssh_string_len(e)); - ssh_print_hexa("d", ssh_string_data(d), ssh_string_len(d)); - ssh_print_hexa("iqmp", ssh_string_data(iqmp), + ssh_log_hexdump("n", ssh_string_data(n), ssh_string_len(n)); + ssh_log_hexdump("e", ssh_string_data(e), ssh_string_len(e)); + ssh_log_hexdump("d", ssh_string_data(d), ssh_string_len(d)); + ssh_log_hexdump("iqmp", ssh_string_data(iqmp), ssh_string_len(iqmp)); - ssh_print_hexa("p", ssh_string_data(p), ssh_string_len(p)); - ssh_print_hexa("q", ssh_string_data(q), ssh_string_len(q)); + ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p)); + ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q)); #endif ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(d); - ssh_string_free(d); + SSH_STRING_FREE(d); ssh_string_burn(iqmp); - ssh_string_free(iqmp); + SSH_STRING_FREE(iqmp); ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); if (rc == SSH_ERROR) { SSH_LOG(SSH_LOG_WARN, "Failed to build RSA private key"); goto fail; @@ -1148,16 +1158,16 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type, } nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i)); - ssh_string_free(i); + SSH_STRING_FREE(i); if (nid == -1) { goto fail; } rc = pki_privkey_build_ecdsa(key, nid, e, exp); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(exp); - ssh_string_free(exp); + SSH_STRING_FREE(exp); if (rc < 0) { SSH_LOG(SSH_LOG_WARN, "Failed to build ECDSA private key"); goto fail; @@ -1177,8 +1187,8 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type, rc = pki_privkey_build_ed25519(key, pubkey, privkey); ssh_string_burn(privkey); - ssh_string_free(privkey); - ssh_string_free(pubkey); + SSH_STRING_FREE(privkey); + SSH_STRING_FREE(pubkey); if (rc != SSH_OK) { SSH_LOG(SSH_LOG_WARN, "Failed to build ed25519 key"); goto fail; @@ -1237,18 +1247,18 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer, rc = pki_pubkey_build_dss(key, p, q, g, pubkey); #ifdef DEBUG_CRYPTO - ssh_print_hexa("p", ssh_string_data(p), ssh_string_len(p)); - ssh_print_hexa("q", ssh_string_data(q), ssh_string_len(q)); - ssh_print_hexa("g", ssh_string_data(g), ssh_string_len(g)); + ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p)); + ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q)); + ssh_log_hexdump("g", ssh_string_data(g), ssh_string_len(g)); #endif ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); ssh_string_burn(pubkey); - ssh_string_free(pubkey); + SSH_STRING_FREE(pubkey); if (rc == SSH_ERROR) { SSH_LOG(SSH_LOG_WARN, "Failed to build DSA public key"); goto fail; @@ -1268,13 +1278,13 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer, rc = pki_pubkey_build_rsa(key, e, n); #ifdef DEBUG_CRYPTO - ssh_print_hexa("e", ssh_string_data(e), ssh_string_len(e)); - ssh_print_hexa("n", ssh_string_data(n), ssh_string_len(n)); + ssh_log_hexdump("e", ssh_string_data(e), ssh_string_len(e)); + ssh_log_hexdump("n", ssh_string_data(n), ssh_string_len(n)); #endif ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); if (rc == SSH_ERROR) { SSH_LOG(SSH_LOG_WARN, "Failed to build RSA public key"); goto fail; @@ -1298,14 +1308,14 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer, } nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i)); - ssh_string_free(i); + SSH_STRING_FREE(i); if (nid == -1) { goto fail; } rc = pki_pubkey_build_ecdsa(key, nid, e); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); if (rc < 0) { SSH_LOG(SSH_LOG_WARN, "Failed to build ECDSA public key"); goto fail; @@ -1321,23 +1331,23 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer, case SSH_KEYTYPE_ED25519: { ssh_string pubkey = ssh_buffer_get_ssh_string(buffer); - if (ssh_string_len(pubkey) != ED25519_PK_LEN) { + if (ssh_string_len(pubkey) != ED25519_KEY_LEN) { SSH_LOG(SSH_LOG_WARN, "Invalid public key length"); ssh_string_burn(pubkey); - ssh_string_free(pubkey); + SSH_STRING_FREE(pubkey); goto fail; } - key->ed25519_pubkey = malloc(ED25519_PK_LEN); + key->ed25519_pubkey = malloc(ED25519_KEY_LEN); if (key->ed25519_pubkey == NULL) { ssh_string_burn(pubkey); - ssh_string_free(pubkey); + SSH_STRING_FREE(pubkey); goto fail; } - memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN); + memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_KEY_LEN); ssh_string_burn(pubkey); - ssh_string_free(pubkey); + SSH_STRING_FREE(pubkey); } break; case SSH_KEYTYPE_DSS_CERT01: @@ -1439,7 +1449,7 @@ static int pki_import_cert_buffer(ssh_buffer buffer, fail: ssh_key_free(key); - ssh_buffer_free(cert); + SSH_BUFFER_FREE(cert); return SSH_ERROR; } @@ -1475,17 +1485,17 @@ int ssh_pki_import_pubkey_base64(const char *b64_key, type_s = ssh_buffer_get_ssh_string(buffer); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return SSH_ERROR; } - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (is_cert_type(type)) { rc = pki_import_cert_buffer(buffer, type, pkey); } else { rc = pki_import_pubkey_buffer(buffer, type, pkey); } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -1540,7 +1550,7 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob, SSH_LOG(SSH_LOG_WARN, "Unknown key type found!"); goto fail; } - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (is_cert_type(type)) { rc = pki_import_cert_buffer(buffer, type, pkey); @@ -1548,12 +1558,12 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob, rc = pki_import_pubkey_buffer(buffer, type, pkey); } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; fail: - ssh_buffer_free(buffer); - ssh_string_free(type_s); + SSH_BUFFER_FREE(buffer); + SSH_STRING_FREE(type_s); return SSH_ERROR; } @@ -1871,7 +1881,7 @@ int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey, * * @return SSH_OK on success, SSH_ERROR otherwise. * - * @see ssh_string_free() + * @see SSH_STRING_FREE() */ int ssh_pki_export_pubkey_blob(const ssh_key key, ssh_string *pblob) @@ -1901,7 +1911,7 @@ int ssh_pki_export_pubkey_blob(const ssh_key key, * * @return SSH_OK on success, SSH_ERROR on error. * - * @see ssh_string_free_char() + * @see SSH_STRING_FREE_CHAR() */ int ssh_pki_export_pubkey_base64(const ssh_key key, char **b64_key) @@ -1919,7 +1929,7 @@ int ssh_pki_export_pubkey_base64(const ssh_key key, } b64 = bin_to_base64(ssh_string_data(key_blob), ssh_string_len(key_blob)); - ssh_string_free(key_blob); + SSH_STRING_FREE(key_blob); if (b64 == NULL) { return SSH_ERROR; } @@ -2019,7 +2029,7 @@ int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) { rc = ssh_buffer_add_buffer(cert_buffer, certkey->cert); if (rc != 0) { - ssh_buffer_free(cert_buffer); + SSH_BUFFER_FREE(cert_buffer); return SSH_ERROR; } @@ -2046,38 +2056,38 @@ int ssh_pki_export_signature_blob(const ssh_signature sig, str = ssh_string_from_char(sig->type_c); if (str == NULL) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } rc = ssh_buffer_add_ssh_string(buf, str); - ssh_string_free(str); + SSH_STRING_FREE(str); if (rc < 0) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } str = pki_signature_to_blob(sig); if (str == NULL) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } rc = ssh_buffer_add_ssh_string(buf, str); - ssh_string_free(str); + SSH_STRING_FREE(str); if (rc < 0) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } str = ssh_string_new(ssh_buffer_get_len(buf)); if (str == NULL) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } ssh_string_fill(str, ssh_buffer_get(buf), ssh_buffer_get_len(buf)); - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); *sig_blob = str; @@ -2109,29 +2119,29 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob, ssh_string_data(sig_blob), ssh_string_len(sig_blob)); if (rc < 0) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } algorithm = ssh_buffer_get_ssh_string(buf); if (algorithm == NULL) { - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return SSH_ERROR; } alg = ssh_string_get_char(algorithm); type = ssh_key_type_from_signature_name(alg); hash_type = ssh_key_hash_from_name(alg); - ssh_string_free(algorithm); + SSH_STRING_FREE(algorithm); blob = ssh_buffer_get_ssh_string(buf); - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); if (blob == NULL) { return SSH_ERROR; } sig = pki_signature_from_blob(pubkey, blob, type, hash_type); - ssh_string_free(blob); + SSH_STRING_FREE(blob); if (sig == NULL) { return SSH_ERROR; } @@ -2229,7 +2239,7 @@ int pki_key_check_hash_compatible(ssh_key key, int ssh_pki_signature_verify(ssh_session session, ssh_signature sig, const ssh_key key, - unsigned char *input, + const unsigned char *input, size_t input_len) { int rc; @@ -2259,7 +2269,7 @@ int ssh_pki_signature_verify(ssh_session session, return SSH_ERROR; } - rc = pki_signature_verify(session, sig, key, input, input_len); + rc = pki_verify_data_signature(sig, key, input, input_len); return rc; } @@ -2283,12 +2293,6 @@ ssh_signature pki_do_sign(const ssh_key privkey, return NULL; } - if (privkey->type == SSH_KEYTYPE_ED25519 || - privkey->type == SSH_KEYTYPE_ED25519_CERT01) - { - return pki_do_sign_hash(privkey, input, input_len, SSH_DIGEST_AUTO); - } - return pki_sign_data(privkey, hash_type, input, input_len); } @@ -2362,8 +2366,8 @@ ssh_string ssh_pki_do_sign(ssh_session session, end: ssh_signature_free(sig); - ssh_buffer_free(sign_input); - ssh_string_free(session_id); + SSH_BUFFER_FREE(sign_input); + SSH_STRING_FREE(session_id); return sig_blob; } @@ -2380,6 +2384,9 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session, int rc; crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); + if (crypto == NULL) { + return NULL; + } /* prepend session identifier */ session_id = ssh_string_new(crypto->digest_len); @@ -2390,28 +2397,28 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session, sig_buf = ssh_buffer_new(); if (sig_buf == NULL) { - ssh_string_free(session_id); + SSH_STRING_FREE(session_id); return NULL; } rc = ssh_buffer_add_ssh_string(sig_buf, session_id); if (rc < 0) { - ssh_string_free(session_id); - ssh_buffer_free(sig_buf); + SSH_STRING_FREE(session_id); + SSH_BUFFER_FREE(sig_buf); return NULL; } - ssh_string_free(session_id); + SSH_STRING_FREE(session_id); /* append out buffer */ if (ssh_buffer_add_buffer(sig_buf, buf) < 0) { - ssh_buffer_free(sig_buf); + SSH_BUFFER_FREE(sig_buf); return NULL; } /* create signature */ sig_blob = ssh_agent_sign_data(session, pubkey, sig_buf); - ssh_buffer_free(sig_buf); + SSH_BUFFER_FREE(sig_buf); return sig_blob; } @@ -2419,7 +2426,8 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session, #ifdef WITH_SERVER ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, - const ssh_key privkey) + const ssh_key privkey, + const enum ssh_digest_e digest) { struct ssh_crypto_struct *crypto = NULL; @@ -2428,8 +2436,6 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, ssh_buffer sign_input = NULL; - enum ssh_digest_e hash_type; - int rc; if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) { @@ -2444,9 +2450,6 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, return NULL; } - /* Get the hash type from the key type */ - hash_type = ssh_key_type_to_hash(session, privkey->type); - /* Fill the input */ sign_input = ssh_buffer_new(); if (sign_input == NULL) { @@ -2466,7 +2469,7 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, sig = pki_do_sign(privkey, ssh_buffer_get(sign_input), ssh_buffer_get_len(sign_input), - hash_type); + digest); if (sig == NULL) { goto end; } @@ -2479,7 +2482,7 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, end: ssh_signature_free(sig); - ssh_buffer_free(sign_input); + SSH_BUFFER_FREE(sign_input); return sig_blob; } diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c index f9d9e14..ecde4cd 100644 --- a/src/pki_container_openssh.c +++ b/src/pki_container_openssh.c @@ -108,11 +108,11 @@ static int pki_private_key_decrypt(ssh_string blob, { struct ssh_cipher_struct *ciphers = ssh_get_ciphertab(); struct ssh_cipher_struct cipher; - uint8_t key_material[128]; - char passphrase_buffer[128]; + uint8_t key_material[128] = {0}; + char passphrase_buffer[128] = {0}; size_t key_material_len; - ssh_buffer buffer; - ssh_string salt; + ssh_buffer buffer = NULL; + ssh_string salt = NULL; uint32_t rounds; int cmp; int rc; @@ -159,7 +159,7 @@ static int pki_private_key_decrypt(ssh_string blob, if (rc != SSH_ERROR){ rc = ssh_buffer_unpack(buffer, "Sd", &salt, &rounds); } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc == SSH_ERROR){ return SSH_ERROR; } @@ -233,7 +233,7 @@ ssh_pki_openssh_import(const char *text_key, void *auth_data, bool private) { - const char *ptr=text_key; + const char *ptr = text_key; const char *end; char *base64; int cmp; @@ -250,7 +250,7 @@ ssh_pki_openssh_import(const char *text_key, uint8_t padding; cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN)); - if (cmp != 0){ + if (cmp != 0) { SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)"); goto out; } @@ -259,15 +259,15 @@ ssh_pki_openssh_import(const char *text_key, ptr++; } end = strstr(ptr, OPENSSH_HEADER_END); - if (end == NULL){ + if (end == NULL) { SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)"); goto out; } base64 = malloc(end - ptr + 1); - if (base64 == NULL){ + if (base64 == NULL) { goto out; } - for (i = 0; ptr < end; ptr++){ + for (i = 0; ptr < end; ptr++) { if (!isspace((int)ptr[0])) { base64[i] = ptr[0]; i++; @@ -276,7 +276,7 @@ ssh_pki_openssh_import(const char *text_key, base64[i] = '\0'; buffer = base64_to_bin(base64); SAFE_FREE(base64); - if (buffer == NULL){ + if (buffer == NULL) { SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)"); goto out; } @@ -289,21 +289,21 @@ ssh_pki_openssh_import(const char *text_key, &nkeys, &pubkey0, &privkeys); - if (rc == SSH_ERROR){ + if (rc == SSH_ERROR) { SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)"); goto out; } cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC)); - if (cmp != 0){ + if (cmp != 0) { SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)"); goto out; } SSH_LOG(SSH_LOG_INFO, - "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", + "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d", ciphername, kdfname, nkeys); - if (nkeys != 1){ + if (nkeys != 1) { SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys); goto out; } @@ -327,7 +327,7 @@ ssh_pki_openssh_import(const char *text_key, kdfoptions, auth_fn, auth_data); - if (rc == SSH_ERROR){ + if (rc == SSH_ERROR) { goto out; } @@ -342,20 +342,20 @@ ssh_pki_openssh_import(const char *text_key, ssh_string_len(privkeys)); rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2); - if (rc == SSH_ERROR || checkint1 != checkint2){ + if (rc == SSH_ERROR || checkint1 != checkint2) { SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)"); goto out; } rc = pki_openssh_import_privkey_blob(privkey_buffer, &key); - if (rc == SSH_ERROR){ + if (rc == SSH_ERROR) { goto out; } comment = ssh_buffer_get_ssh_string(privkey_buffer); SAFE_FREE(comment); /* verify that the remaining data is correct padding */ - for (i=1; ssh_buffer_get_len(privkey_buffer) > 0; ++i){ + for (i = 1; ssh_buffer_get_len(privkey_buffer) > 0; ++i) { ssh_buffer_get_u8(privkey_buffer, &padding); - if (padding != i){ + if (padding != i) { ssh_key_free(key); key = NULL; SSH_LOG(SSH_LOG_WARN, "Invalid padding"); @@ -364,11 +364,11 @@ ssh_pki_openssh_import(const char *text_key, } out: if (buffer != NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); buffer = NULL; } if (privkey_buffer != NULL) { - ssh_buffer_free(privkey_buffer); + SSH_BUFFER_FREE(privkey_buffer); privkey_buffer = NULL; } SAFE_FREE(magic); @@ -415,12 +415,13 @@ static int pki_openssh_export_privkey_blob(const ssh_key privkey, return SSH_ERROR; } rc = ssh_buffer_pack(buffer, - "sdPdP", + "sdPdPP", privkey->type_c, - (uint32_t)ED25519_PK_LEN, - (size_t)ED25519_PK_LEN, privkey->ed25519_pubkey, - (uint32_t)ED25519_SK_LEN, - (size_t)ED25519_SK_LEN, privkey->ed25519_privkey); + (uint32_t)ED25519_KEY_LEN, + (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey, + (uint32_t)(2 * ED25519_KEY_LEN), + (size_t)ED25519_KEY_LEN, privkey->ed25519_privkey, + (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey); return rc; } @@ -439,9 +440,9 @@ static int pki_private_key_encrypt(ssh_buffer privkey_buffer, { struct ssh_cipher_struct *ciphers = ssh_get_ciphertab(); struct ssh_cipher_struct cipher; - uint8_t key_material[128]; + uint8_t key_material[128] = {0}; size_t key_material_len; - char passphrase_buffer[128]; + char passphrase_buffer[128] = {0}; int rc; int i; int cmp; @@ -619,26 +620,26 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, salt = ssh_string_new(16); if (salt == NULL){ - ssh_buffer_free(kdf_buf); + SSH_BUFFER_FREE(kdf_buf); goto error; } ok = ssh_get_random(ssh_string_data(salt), 16, 0); if (!ok) { - ssh_buffer_free(kdf_buf); + SSH_BUFFER_FREE(kdf_buf); goto error; } ssh_buffer_pack(kdf_buf, "Sd", salt, rounds); kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf)); if (kdf_options == NULL){ - ssh_buffer_free(kdf_buf); + SSH_BUFFER_FREE(kdf_buf); goto error; } memcpy(ssh_string_data(kdf_options), ssh_buffer_get(kdf_buf), ssh_buffer_get_len(kdf_buf)); - ssh_buffer_free(kdf_buf); + SSH_BUFFER_FREE(kdf_buf); rc = pki_private_key_encrypt(privkey_buffer, passphrase, "aes128-cbc", @@ -700,7 +701,7 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, str_len = ssh_buffer_get_len(buffer); len = ssh_buffer_get_data(buffer, ssh_string_data(str), str_len); if (str_len != len) { - ssh_string_free(str); + SSH_STRING_FREE(str); str = NULL; } @@ -708,13 +709,13 @@ error: if (privkey_buffer != NULL) { void *bufptr = ssh_buffer_get(privkey_buffer); explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer)); - ssh_buffer_free(privkey_buffer); + SSH_BUFFER_FREE(privkey_buffer); } SAFE_FREE(pubkey_s); SAFE_FREE(kdf_options); SAFE_FREE(salt); if (buffer != NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); } return str; diff --git a/src/pki_crypto.c b/src/pki_crypto.c index 4cb23e6..26d2da1 100644 --- a/src/pki_crypto.c +++ b/src/pki_crypto.c @@ -94,7 +94,7 @@ static int pki_key_ecdsa_to_nid(EC_KEY *k) static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(EC_KEY *k) { - static int nid; + int nid; nid = pki_key_ecdsa_to_nid(k); @@ -183,7 +183,7 @@ static ssh_string make_ecpoint_string(const EC_GROUP *g, ssh_string_len(s), NULL); if (len != ssh_string_len(s)) { - ssh_string_free(s); + SSH_STRING_FREE(s); return NULL; } @@ -730,29 +730,58 @@ ssh_string pki_private_key_to_pem(const ssh_key key, return NULL; } - pkey = EVP_PKEY_new(); - if (pkey == NULL) { - goto err; - } - switch (key->type) { case SSH_KEYTYPE_DSS: + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + goto err; + } + rc = EVP_PKEY_set1_DSA(pkey, key->dsa); break; case SSH_KEYTYPE_RSA: case SSH_KEYTYPE_RSA1: + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + goto err; + } + rc = EVP_PKEY_set1_RSA(pkey, key->rsa); break; #ifdef HAVE_ECC case SSH_KEYTYPE_ECDSA_P256: case SSH_KEYTYPE_ECDSA_P384: case SSH_KEYTYPE_ECDSA_P521: + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + goto err; + } + rc = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa); break; #endif case SSH_KEYTYPE_ED25519: +#ifdef HAVE_OPENSSL_ED25519 + /* In OpenSSL, the input is the private key seed only, which means + * the first half of the SSH private key (the second half is the + * public key) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, + (const uint8_t *)key->ed25519_privkey, + ED25519_KEY_LEN); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create ed25519 EVP_PKEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + + /* Mark the operation as successful as for the other key types */ + rc = 1; + break; +#else SSH_LOG(SSH_LOG_WARN, "PEM output not supported for key type ssh-ed25519"); goto err; +#endif case SSH_KEYTYPE_DSS_CERT01: case SSH_KEYTYPE_RSA_CERT01: case SSH_KEYTYPE_ECDSA_P256_CERT01: @@ -821,7 +850,11 @@ ssh_key pki_private_key_from_base64(const char *b64_key, BIO *mem = NULL; DSA *dsa = NULL; RSA *rsa = NULL; +#ifdef HAVE_OPENSSL_ED25519 + uint8_t *ed25519 = NULL; +#else ed25519_privkey *ed25519 = NULL; +#endif ssh_key key = NULL; enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN; #ifdef HAVE_OPENSSL_ECC @@ -861,7 +894,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, SSH_LOG(SSH_LOG_WARN, "Parsing private key: %s", ERR_error_string(ERR_get_error(),NULL)); - return NULL; + goto fail; } type = SSH_KEYTYPE_DSS; break; @@ -871,7 +904,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, SSH_LOG(SSH_LOG_WARN, "Parsing private key: %s", ERR_error_string(ERR_get_error(),NULL)); - return NULL; + goto fail; } type = SSH_KEYTYPE_RSA; break; @@ -882,7 +915,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, SSH_LOG(SSH_LOG_WARN, "Parsing private key: %s", ERR_error_string(ERR_get_error(), NULL)); - return NULL; + goto fail; } /* pki_privatekey_type_from_string always returns P256 for ECDSA @@ -895,6 +928,43 @@ ssh_key pki_private_key_from_base64(const char *b64_key, break; #endif +#ifdef HAVE_OPENSSL_ED25519 + case EVP_PKEY_ED25519: + { + size_t key_len; + int evp_rc = 0; + + /* Get the key length */ + evp_rc = EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get ed25519 raw private key length: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (key_len != ED25519_KEY_LEN) { + goto fail; + } + + ed25519 = malloc(key_len); + if (ed25519 == NULL) { + SSH_LOG(SSH_LOG_WARN, "Out of memory"); + goto fail; + } + + evp_rc = EVP_PKEY_get_raw_private_key(pkey, (uint8_t *)ed25519, + &key_len); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get ed25519 raw private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + type = SSH_KEYTYPE_ED25519; + } + break; +#endif default: EVP_PKEY_free(pkey); SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d", @@ -923,13 +993,16 @@ ssh_key pki_private_key_from_base64(const char *b64_key, return key; fail: + EVP_PKEY_free(pkey); ssh_key_free(key); DSA_free(dsa); RSA_free(rsa); #ifdef HAVE_OPENSSL_ECC EC_KEY_free(ecdsa); #endif - +#ifdef HAVE_OPENSSL_ED25519 + SAFE_FREE(ed25519); +#endif return NULL; } @@ -1117,7 +1190,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) if (key->cert != NULL) { rc = ssh_buffer_add_buffer(buffer, key->cert); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } goto makestring; @@ -1125,14 +1198,14 @@ ssh_string pki_publickey_to_blob(const ssh_key key) type_s = ssh_string_from_char(key->type_c); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } rc = ssh_buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -1175,16 +1248,16 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); p = NULL; ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); g = NULL; ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); q = NULL; ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); n = NULL; break; @@ -1211,10 +1284,10 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); e = NULL; ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); n = NULL; break; @@ -1231,21 +1304,21 @@ ssh_string pki_publickey_to_blob(const ssh_key key) #ifdef HAVE_OPENSSL_ECC type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid)); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } rc = ssh_buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } e = make_ecpoint_string(EC_KEY_get0_group(key->ecdsa), EC_KEY_get0_public_key(key->ecdsa)); if (e == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -1255,7 +1328,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); e = NULL; break; @@ -1275,23 +1348,23 @@ makestring: if (rc < 0) { goto fail; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return str; fail: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); ssh_string_burn(str); - ssh_string_free(str); + SSH_STRING_FREE(str); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); return NULL; } @@ -1358,8 +1431,8 @@ static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig) s_len - s_offset_in); DSA_SIG_free(dsa_sig); - ssh_string_free(r); - ssh_string_free(s); + SSH_STRING_FREE(r); + SSH_STRING_FREE(s); sig_blob = ssh_string_new(40); if (sig_blob == NULL) { @@ -1372,8 +1445,8 @@ static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig) error: DSA_SIG_free(dsa_sig); - ssh_string_free(r); - ssh_string_free(s); + SSH_STRING_FREE(r); + SSH_STRING_FREE(s); return NULL; } @@ -1445,18 +1518,18 @@ static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig) ssh_string_fill(sig_blob, ssh_buffer_get(buf), ssh_buffer_get_len(buf)); - ssh_string_free(r); - ssh_string_free(s); + SSH_STRING_FREE(r); + SSH_STRING_FREE(s); ECDSA_SIG_free(ecdsa_sig); - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return sig_blob; error: - ssh_string_free(r); - ssh_string_free(s); + SSH_STRING_FREE(r); + SSH_STRING_FREE(s); ECDSA_SIG_free(ecdsa_sig); - ssh_buffer_free(buf); + SSH_BUFFER_FREE(buf); return NULL; } @@ -1473,7 +1546,7 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) sig_blob = ssh_string_copy(sig->raw_sig); break; case SSH_KEYTYPE_ED25519: - sig_blob = pki_ed25519_sig_to_blob(sig); + sig_blob = pki_ed25519_signature_to_blob(sig); break; case SSH_KEYTYPE_ECDSA_P256: case SSH_KEYTYPE_ECDSA_P384: @@ -1496,9 +1569,9 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey, ssh_signature sig) { uint32_t pad_len = 0; - char *blob_orig; - char *blob_padded_data; - ssh_string sig_blob_padded; + char *blob_orig = NULL; + char *blob_padded_data = NULL; + ssh_string sig_blob_padded = NULL; size_t rsalen = 0; size_t len = ssh_string_len(sig_blob); @@ -1519,7 +1592,7 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey, #ifdef DEBUG_CRYPTO SSH_LOG(SSH_LOG_WARN, "RSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); + ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len); #endif if (len == rsalen) { @@ -1541,6 +1614,10 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey, blob_padded_data = (char *) ssh_string_data(sig_blob_padded); blob_orig = (char *) ssh_string_data(sig_blob); + if (blob_padded_data == NULL || blob_orig == NULL) { + goto errout; + } + /* front-pad the buffer with zeroes */ explicit_bzero(blob_padded_data, pad_len); /* fill the rest with the actual signature blob */ @@ -1552,6 +1629,7 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey, return SSH_OK; errout: + SSH_STRING_FREE(sig_blob_padded); return SSH_ERROR; } @@ -1583,8 +1661,8 @@ static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey), } #ifdef DEBUG_CRYPTO - ssh_print_hexa("r", ssh_string_data(sig_blob), 20); - ssh_print_hexa("s", (unsigned char *)ssh_string_data(sig_blob) + 20, 20); + ssh_log_hexdump("r", ssh_string_data(sig_blob), 20); + ssh_log_hexdump("s", (unsigned char *)ssh_string_data(sig_blob) + 20, 20); #endif r = ssh_string_new(20); @@ -1595,7 +1673,7 @@ static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey), pr = ssh_make_string_bn(r); ssh_string_burn(r); - ssh_string_free(r); + SSH_STRING_FREE(r); if (pr == NULL) { goto error; } @@ -1608,7 +1686,7 @@ static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey), ps = ssh_make_string_bn(s); ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); if (ps == NULL) { goto error; } @@ -1695,12 +1773,12 @@ static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey), } #ifdef DEBUG_CRYPTO - ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r)); + ssh_log_hexdump("r", ssh_string_data(r), ssh_string_len(r)); #endif pr = ssh_make_string_bn(r); ssh_string_burn(r); - ssh_string_free(r); + SSH_STRING_FREE(r); if (pr == NULL) { goto error; } @@ -1714,7 +1792,7 @@ static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey), if (rlen != 0) { ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); SSH_LOG(SSH_LOG_WARN, "Signature has remaining bytes in inner " "sigblob: %lu", @@ -1723,12 +1801,12 @@ static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey), } #ifdef DEBUG_CRYPTO - ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s)); + ssh_log_hexdump("s", ssh_string_data(s), ssh_string_len(s)); #endif ps = ssh_make_string_bn(s); ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); if (ps == NULL) { goto error; } @@ -1821,7 +1899,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, } break; case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_sig_from_blob(sig, sig_blob); + rc = pki_signature_from_ed25519_blob(sig, sig_blob); if (rc != SSH_OK){ goto error; } @@ -1852,54 +1930,6 @@ error: return NULL; } -int pki_signature_verify(ssh_session session, - const ssh_signature sig, - const ssh_key key, - const unsigned char *input, - size_t input_len) -{ - int rc; - - if (session == NULL || sig == NULL || key == NULL || input == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Bad parameter provided to " - "pki_signature_verify()"); - return SSH_ERROR; - } - - if (ssh_key_type_plain(key->type) != sig->type) { - SSH_LOG(SSH_LOG_WARN, - "Can not verify %s signature with %s key", - sig->type_c, - key->type_c); - return SSH_ERROR; - } - - /* Check if public key and hash type are compatible */ - rc = pki_key_check_hash_compatible(key, sig->hash_type); - if (rc != SSH_OK) { - return SSH_ERROR; - } - - /* For ed25519 keys, verify using the input directly */ - if (key->type == SSH_KEYTYPE_ED25519 || - key->type == SSH_KEYTYPE_ED25519_CERT01) - { - rc = pki_ed25519_verify(key, sig, input, input_len); - } else { - /* For the other key types, calculate the hash and verify the signature */ - rc = pki_verify_data_signature(sig, key, input, input_len); - } - - if (rc != SSH_OK){ - ssh_set_error(session, - SSH_FATAL, - "Signature verification error"); - return SSH_ERROR; - } - - return SSH_OK; -} - static const EVP_MD *pki_digest_to_md(enum ssh_digest_e hash_type) { const EVP_MD *md = NULL; @@ -1918,6 +1948,8 @@ static const EVP_MD *pki_digest_to_md(enum ssh_digest_e hash_type) md = EVP_sha1(); break; case SSH_DIGEST_AUTO: + md = NULL; + break; default: SSH_LOG(SSH_LOG_TRACE, "Unknown hash algorithm for type: %d", hash_type); @@ -1931,12 +1963,6 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key) { EVP_PKEY *pkey = NULL; - pkey = EVP_PKEY_new(); - if (pkey == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Out of memory"); - return NULL; - } - switch(key->type) { case SSH_KEYTYPE_DSS: case SSH_KEYTYPE_DSS_CERT01: @@ -1944,6 +1970,12 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key) SSH_LOG(SSH_LOG_TRACE, "NULL key->dsa"); goto error; } + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, "Out of memory"); + return NULL; + } + EVP_PKEY_set1_DSA(pkey, key->dsa); break; case SSH_KEYTYPE_RSA: @@ -1953,6 +1985,12 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key) SSH_LOG(SSH_LOG_TRACE, "NULL key->rsa"); goto error; } + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, "Out of memory"); + return NULL; + } + EVP_PKEY_set1_RSA(pkey, key->rsa); break; case SSH_KEYTYPE_ECDSA_P256: @@ -1966,12 +2004,46 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key) SSH_LOG(SSH_LOG_TRACE, "NULL key->ecdsa"); goto error; } + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, "Out of memory"); + return NULL; + } + EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa); break; # endif case SSH_KEYTYPE_ED25519: - /* Not supported yet. This type requires the use of EVP_DigestSign*() - * API and ECX keys. There is no EVP_set1_ECX_KEY() or equivalent yet. */ + case SSH_KEYTYPE_ED25519_CERT01: +# if defined(HAVE_OPENSSL_ED25519) + if (ssh_key_is_private(key)) { + if (key->ed25519_privkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, "NULL key->ed25519_privkey"); + goto error; + } + /* In OpenSSL, the input is the private key seed only, which means + * the first half of the SSH private key (the second half is the + * public key). Both keys have the same length (32 bytes) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, + (const uint8_t *)key->ed25519_privkey, + ED25519_KEY_LEN); + } else { + if (key->ed25519_pubkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, "NULL key->ed25519_pubkey"); + goto error; + } + pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, + (const uint8_t *)key->ed25519_pubkey, + ED25519_KEY_LEN); + } + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create ed25519 EVP_PKEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + break; +#endif case SSH_KEYTYPE_UNKNOWN: default: SSH_LOG(SSH_LOG_TRACE, "Unknown private key algorithm for type: %d", @@ -2009,7 +2081,7 @@ ssh_signature pki_sign_data(const ssh_key privkey, EVP_PKEY *pkey = NULL; unsigned char *raw_sig_data = NULL; - unsigned int raw_sig_len; + size_t raw_sig_len; ssh_signature sig = NULL; @@ -2027,10 +2099,20 @@ ssh_signature pki_sign_data(const ssh_key privkey, return NULL; } +#ifndef HAVE_OPENSSL_ED25519 + if (privkey->type == SSH_KEYTYPE_ED25519 || + privkey->type == SSH_KEYTYPE_ED25519_CERT01) + { + return pki_do_sign_hash(privkey, input, input_len, hash_type); + } +#endif + /* Set hash algorithm to be used */ md = pki_digest_to_md(hash_type); if (md == NULL) { - return NULL; + if (hash_type != SSH_DIGEST_AUTO) { + return NULL; + } } /* Setup private key EVP_PKEY */ @@ -2040,7 +2122,7 @@ ssh_signature pki_sign_data(const ssh_key privkey, } /* Allocate buffer for signature */ - raw_sig_len = EVP_PKEY_size(pkey); + raw_sig_len = (size_t)EVP_PKEY_size(pkey); raw_sig_data = (unsigned char *)malloc(raw_sig_len); if (raw_sig_data == NULL) { SSH_LOG(SSH_LOG_TRACE, "Out of memory"); @@ -2055,26 +2137,42 @@ ssh_signature pki_sign_data(const ssh_key privkey, } /* Sign the data */ - rc = EVP_SignInit_ex(ctx, md, NULL); - if (!rc){ - SSH_LOG(SSH_LOG_TRACE, "EVP_SignInit() failed"); + rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey); + if (rc != 1){ + SSH_LOG(SSH_LOG_TRACE, + "EVP_DigestSignInit() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto out; } - rc = EVP_SignUpdate(ctx, input, input_len); - if (!rc) { - SSH_LOG(SSH_LOG_TRACE, "EVP_SignUpdate() failed"); +#ifdef HAVE_OPENSSL_EVP_DIGESTSIGN + rc = EVP_DigestSign(ctx, raw_sig_data, &raw_sig_len, input, input_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "EVP_DigestSign() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } +#else + rc = EVP_DigestSignUpdate(ctx, input, input_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "EVP_DigestSignUpdate() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto out; } - rc = EVP_SignFinal(ctx, raw_sig_data, &raw_sig_len, pkey); - if (!rc) { - SSH_LOG(SSH_LOG_TRACE, "EVP_SignFinal() failed"); + rc = EVP_DigestSignFinal(ctx, raw_sig_data, &raw_sig_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "EVP_DigestSignFinal() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto out; } +#endif #ifdef DEBUG_CRYPTO - ssh_print_hexa("Generated signature", raw_sig_data, raw_sig_len); + ssh_log_hexdump("Generated signature", raw_sig_data, raw_sig_len); #endif /* Allocate and fill output signature */ @@ -2144,7 +2242,11 @@ int pki_verify_data_signature(ssh_signature signature, int evp_rc; if (pubkey == NULL || ssh_key_is_private(pubkey) || input == NULL || - signature == NULL || signature->raw_sig == NULL) + signature == NULL || (signature->raw_sig == NULL +#ifndef HAVE_OPENSSL_ED25519 + && signature->ed25519_sig == NULL +#endif + )) { SSH_LOG(SSH_LOG_TRACE, "Bad parameter provided to " "pki_verify_data_signature()"); @@ -2157,6 +2259,14 @@ int pki_verify_data_signature(ssh_signature signature, return SSH_ERROR; } +#ifndef HAVE_OPENSSL_ED25519 + if (pubkey->type == SSH_KEYTYPE_ED25519 || + pubkey->type == SSH_KEYTYPE_ED25519_CERT01) + { + return pki_ed25519_verify(pubkey, signature, input, input_len); + } +#endif + /* Get the signature to be verified */ raw_sig_data = ssh_string_data(signature->raw_sig); raw_sig_len = ssh_string_len(signature->raw_sig); @@ -2167,7 +2277,9 @@ int pki_verify_data_signature(ssh_signature signature, /* Set hash algorithm to be used */ md = pki_digest_to_md(signature->hash_type); if (md == NULL) { - return SSH_ERROR; + if (signature->hash_type != SSH_DIGEST_AUTO) { + return SSH_ERROR; + } } /* Setup public key EVP_PKEY */ @@ -2179,33 +2291,42 @@ int pki_verify_data_signature(ssh_signature signature, /* Create the context */ ctx = EVP_MD_CTX_create(); if (ctx == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Out of memory"); + SSH_LOG(SSH_LOG_TRACE, + "Failed to create EVP_MD_CTX: %s", + ERR_error_string(ERR_get_error(), NULL)); goto out; } /* Verify the signature */ - evp_rc = EVP_VerifyInit_ex(ctx, md, NULL); - if (!evp_rc){ - SSH_LOG(SSH_LOG_TRACE, "EVP_SignInit() failed"); + evp_rc = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey); + if (evp_rc != 1){ + SSH_LOG(SSH_LOG_TRACE, + "EVP_DigestVerifyInit() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto out; } - evp_rc = EVP_VerifyUpdate(ctx, input, input_len); - if (!evp_rc) { - SSH_LOG(SSH_LOG_TRACE, "EVP_SignUpdate() failed"); +#ifdef HAVE_OPENSSL_EVP_DIGESTVERIFY + evp_rc = EVP_DigestVerify(ctx, raw_sig_data, raw_sig_len, input, input_len); +#else + evp_rc = EVP_DigestVerifyUpdate(ctx, input, input_len); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "EVP_DigestVerifyUpdate() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto out; } - evp_rc = EVP_VerifyFinal(ctx, raw_sig_data, raw_sig_len, pkey); - if (evp_rc < 0) { - SSH_LOG(SSH_LOG_TRACE, "EVP_SignFinal() failed"); - rc = SSH_ERROR; - } else if (evp_rc == 0) { - SSH_LOG(SSH_LOG_TRACE, "Signature invalid"); - rc = SSH_ERROR; - } else if (evp_rc == 1) { + evp_rc = EVP_DigestVerifyFinal(ctx, raw_sig_data, raw_sig_len); +#endif + if (evp_rc == 1) { SSH_LOG(SSH_LOG_TRACE, "Signature valid"); rc = SSH_OK; + } else { + SSH_LOG(SSH_LOG_TRACE, + "Signature invalid: %s", + ERR_error_string(ERR_get_error(), NULL)); + rc = SSH_ERROR; } out: @@ -2218,6 +2339,92 @@ out: return rc; } +#ifdef HAVE_OPENSSL_ED25519 +int pki_key_generate_ed25519(ssh_key key) +{ + int evp_rc; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + size_t privkey_len = ED25519_KEY_LEN; + size_t pubkey_len = ED25519_KEY_LEN; + + if (key == NULL) { + return SSH_ERROR; + } + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL); + if (pctx == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create ed25519 EVP_PKEY_CTX: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + evp_rc = EVP_PKEY_keygen_init(pctx); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize ed25519 key generation: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + evp_rc = EVP_PKEY_keygen(pctx, &pkey); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to generate ed25519 key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + key->ed25519_privkey = malloc(ED25519_KEY_LEN); + if (key->ed25519_privkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to allocate memory for ed25519 private key"); + goto error; + } + + key->ed25519_pubkey = malloc(ED25519_KEY_LEN); + if (key->ed25519_pubkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to allocate memory for ed25519 public key"); + goto error; + } + + evp_rc = EVP_PKEY_get_raw_private_key(pkey, (uint8_t *)key->ed25519_privkey, + &privkey_len); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get ed25519 raw private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + evp_rc = EVP_PKEY_get_raw_public_key(pkey, (uint8_t *)key->ed25519_pubkey, + &pubkey_len); + if (evp_rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get ed25519 raw public key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return SSH_OK; + +error: + if (pctx != NULL) { + EVP_PKEY_CTX_free(pctx); + } + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + SAFE_FREE(key->ed25519_privkey); + SAFE_FREE(key->ed25519_pubkey); + + return SSH_ERROR; +} +#else ssh_signature pki_do_sign_hash(const ssh_key privkey, const unsigned char *hash, size_t hlen, @@ -2250,4 +2457,6 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey, return sig; } +#endif /* HAVE_OPENSSL_ED25519 */ + #endif /* _PKI_CRYPTO_H */ diff --git a/src/pki_ed25519.c b/src/pki_ed25519.c index 0eaa494..fdf94b4 100644 --- a/src/pki_ed25519.c +++ b/src/pki_ed25519.c @@ -56,28 +56,6 @@ error: return SSH_ERROR; } -int pki_privkey_build_ed25519(ssh_key key, - ssh_string pubkey, - ssh_string privkey) -{ - if (ssh_string_len(pubkey) != ED25519_PK_LEN || - ssh_string_len(privkey) != ED25519_SK_LEN) { - SSH_LOG(SSH_LOG_WARN, "Invalid ed25519 key len"); - return SSH_ERROR; - } - key->ed25519_privkey = malloc(ED25519_SK_LEN); - key->ed25519_pubkey = malloc(ED25519_PK_LEN); - if (key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL) { - return SSH_ERROR; - } - memcpy(key->ed25519_privkey, ssh_string_data(privkey), - ED25519_SK_LEN); - memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), - ED25519_PK_LEN); - - return SSH_OK; -} - int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig, const unsigned char *hash, @@ -170,165 +148,3 @@ error: return SSH_ERROR; } -/** - * @internal - * - * @brief Compare ed25519 keys if they are equal. - * - * @param[in] k1 The first key to compare. - * - * @param[in] k2 The second key to compare. - * - * @param[in] what What part or type of the key do you want to compare. - * - * @return 0 if equal, 1 if not. - */ -int pki_ed25519_key_cmp(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what) -{ - int cmp; - - switch(what) { - case SSH_KEY_CMP_PRIVATE: - if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) { - return 1; - } - cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_SK_LEN); - if (cmp != 0) { - return 1; - } - /* FALL THROUGH */ - case SSH_KEY_CMP_PUBLIC: - if (k1->ed25519_pubkey == NULL || k2->ed25519_pubkey == NULL) { - return 1; - } - cmp = memcmp(k1->ed25519_pubkey, k2->ed25519_pubkey, ED25519_PK_LEN); - if (cmp != 0) { - return 1; - } - } - - return 0; -} - -/** - * @internal - * - * @brief duplicate an ed25519 key - * - * @param[out\ new preinitialized output ssh_ke - * - * @param[in] key key to copy - * - * @return SSH_ERROR on error, SSH_OK on success - */ -int pki_ed25519_key_dup(ssh_key new, const ssh_key key) -{ - if (key->ed25519_privkey == NULL && key->ed25519_pubkey == NULL) { - return SSH_ERROR; - } - - if (key->ed25519_privkey != NULL) { - new->ed25519_privkey = malloc(ED25519_SK_LEN); - if (new->ed25519_privkey == NULL) { - return SSH_ERROR; - } - memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_SK_LEN); - } - - if (key->ed25519_pubkey != NULL) { - new->ed25519_pubkey = malloc(ED25519_PK_LEN); - if (new->ed25519_pubkey == NULL) { - SAFE_FREE(new->ed25519_privkey); - return SSH_ERROR; - } - memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_PK_LEN); - } - - return SSH_OK; -} - -/** - * @internal - * - * @brief outputs an ed25519 public key in a blob buffer. - * - * @param[out] buffer output buffer - * - * @param[in] key key to output - * - * @return SSH_ERROR on error, SSH_OK on success - */ -int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key) -{ - int rc; - - if (key->ed25519_pubkey == NULL){ - return SSH_ERROR; - } - - rc = ssh_buffer_pack(buffer, - "dP", - (uint32_t)ED25519_PK_LEN, - (size_t)ED25519_PK_LEN, key->ed25519_pubkey); - - return rc; -} - -/** - * @internal - * - * @brief output a signature blob from an ed25519 signature - * - * @param[in] sig signature to convert - * - * @return Signature blob in SSH string, or NULL on error - */ -ssh_string pki_ed25519_sig_to_blob(ssh_signature sig) -{ - ssh_string sig_blob; - - if (sig->ed25519_sig == NULL) { - return NULL; - } - - sig_blob = ssh_string_new(ED25519_SIG_LEN); - if (sig_blob == NULL) { - return NULL; - } - ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN); - - return sig_blob; -} - -/** - * @internal - * - * @brief Convert a signature blob in an ed25519 signature. - * - * @param[out] sig a preinitialized signature - * - * @param[in] sig_blob a signature blob - * - * @return SSH_ERROR on error, SSH_OK on success - */ -int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob) -{ - size_t len; - - len = ssh_string_len(sig_blob); - if (len != ED25519_SIG_LEN){ - SSH_LOG(SSH_LOG_WARN, "Invalid ssh-ed25519 signature len: %zu", len); - return SSH_ERROR; - } - - sig->ed25519_sig = malloc(ED25519_SIG_LEN); - if (sig->ed25519_sig == NULL){ - return SSH_ERROR; - } - - memcpy(sig->ed25519_sig, ssh_string_data(sig_blob), ED25519_SIG_LEN); - - return SSH_OK; -} diff --git a/src/pki_ed25519_common.c b/src/pki_ed25519_common.c new file mode 100644 index 0000000..738825f --- /dev/null +++ b/src/pki_ed25519_common.c @@ -0,0 +1,280 @@ +/* + * pki_ed25519_common.c - Common ed25519 functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2014 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include "libssh/pki.h" +#include "libssh/pki_priv.h" +#include "libssh/buffer.h" + +int pki_privkey_build_ed25519(ssh_key key, + ssh_string pubkey, + ssh_string privkey) +{ + if (ssh_string_len(pubkey) != ED25519_KEY_LEN || + ssh_string_len(privkey) != (2 * ED25519_KEY_LEN)) + { + SSH_LOG(SSH_LOG_WARN, "Invalid ed25519 key len"); + return SSH_ERROR; + } + +#ifdef HAVE_OPENSSL_ED25519 + /* In OpenSSL implementation, the private key is the original private seed, + * without the public key. */ + key->ed25519_privkey = malloc(ED25519_KEY_LEN); +#else + /* In the internal implementation, the private key is the concatenation of + * the private seed with the public key. */ + key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN); +#endif + if (key->ed25519_privkey == NULL) { + goto error; + } + + key->ed25519_pubkey = malloc(ED25519_KEY_LEN); + if (key->ed25519_pubkey == NULL) { + goto error; + } + +#ifdef HAVE_OPENSSL_ED25519 + memcpy(key->ed25519_privkey, ssh_string_data(privkey), + ED25519_KEY_LEN); +#else + memcpy(key->ed25519_privkey, ssh_string_data(privkey), + 2 * ED25519_KEY_LEN); +#endif + memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), + ED25519_KEY_LEN); + + return SSH_OK; + +error: + SAFE_FREE(key->ed25519_privkey); + SAFE_FREE(key->ed25519_pubkey); + + return SSH_ERROR; +} + +/** + * @internal + * + * @brief Compare ed25519 keys if they are equal. + * + * @param[in] k1 The first key to compare. + * + * @param[in] k2 The second key to compare. + * + * @param[in] what What part or type of the key do you want to compare. + * + * @return 0 if equal, 1 if not. + */ +int pki_ed25519_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + int cmp; + + switch(what) { + case SSH_KEY_CMP_PRIVATE: + if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) { + return 1; + } +#ifdef HAVE_OPENSSL_ED25519 + /* In OpenSSL implementation, the private key is the original private + * seed, without the public key. */ + cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_KEY_LEN); +#else + /* In the internal implementation, the private key is the concatenation + * of the private seed with the public key. */ + cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, + 2 * ED25519_KEY_LEN); +#endif + if (cmp != 0) { + return 1; + } + /* FALL THROUGH */ + case SSH_KEY_CMP_PUBLIC: + if (k1->ed25519_pubkey == NULL || k2->ed25519_pubkey == NULL) { + return 1; + } + cmp = memcmp(k1->ed25519_pubkey, k2->ed25519_pubkey, ED25519_KEY_LEN); + if (cmp != 0) { + return 1; + } + } + + return 0; +} + +/** + * @internal + * + * @brief Duplicate an Ed25519 key + * + * @param[out] new Pre-initialized ssh_key structure + * + * @param[in] key Key to copy + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_key_dup(ssh_key new, const ssh_key key) +{ + if (key->ed25519_privkey == NULL && key->ed25519_pubkey == NULL) { + return SSH_ERROR; + } + + if (key->ed25519_privkey != NULL) { +#ifdef HAVE_OPENSSL_ED25519 + /* In OpenSSL implementation, the private key is the original private + * seed, without the public key. */ + new->ed25519_privkey = malloc(ED25519_KEY_LEN); +#else + /* In the internal implementation, the private key is the concatenation + * of the private seed with the public key. */ + new->ed25519_privkey = malloc(2 * ED25519_KEY_LEN); +#endif + if (new->ed25519_privkey == NULL) { + return SSH_ERROR; + } +#ifdef HAVE_OPENSSL_ED25519 + memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN); +#else + memcpy(new->ed25519_privkey, key->ed25519_privkey, 2 * ED25519_KEY_LEN); +#endif + } + + if (key->ed25519_pubkey != NULL) { + new->ed25519_pubkey = malloc(ED25519_KEY_LEN); + if (new->ed25519_pubkey == NULL) { + SAFE_FREE(new->ed25519_privkey); + return SSH_ERROR; + } + memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_LEN); + } + + return SSH_OK; +} + +/** + * @internal + * + * @brief Outputs an Ed25519 public key in a blob buffer. + * + * @param[out] buffer Output buffer + * + * @param[in] key Key to output + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key) +{ + int rc; + + if (key->ed25519_pubkey == NULL){ + return SSH_ERROR; + } + + rc = ssh_buffer_pack(buffer, + "dP", + (uint32_t)ED25519_KEY_LEN, + (size_t)ED25519_KEY_LEN, key->ed25519_pubkey); + + return rc; +} + +/** + * @internal + * + * @brief output a signature blob from an ed25519 signature + * + * @param[in] sig signature to convert + * + * @return Signature blob in SSH string, or NULL on error + */ +ssh_string pki_ed25519_signature_to_blob(ssh_signature sig) +{ + ssh_string sig_blob; + +#ifdef HAVE_OPENSSL_ED25519 + /* When using the OpenSSL implementation, the signature is stored in raw_sig + * which is shared by all algorithms.*/ + if (sig->raw_sig == NULL) { + return NULL; + } +#else + /* When using the internal implementation, the signature is stored in an + * algorithm specific field. */ + if (sig->ed25519_sig == NULL) { + return NULL; + } +#endif + + sig_blob = ssh_string_new(ED25519_SIG_LEN); + if (sig_blob == NULL) { + return NULL; + } + +#ifdef HAVE_OPENSSL_ED25519 + ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig), + ssh_string_len(sig->raw_sig)); +#else + ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN); +#endif + + return sig_blob; +} + +/** + * @internal + * + * @brief Convert a signature blob in an ed25519 signature. + * + * @param[out] sig a preinitialized signature + * + * @param[in] sig_blob a signature blob + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob) +{ + size_t len; + + len = ssh_string_len(sig_blob); + if (len != ED25519_SIG_LEN){ + SSH_LOG(SSH_LOG_WARN, "Invalid ssh-ed25519 signature len: %zu", len); + return SSH_ERROR; + } + +#ifdef HAVE_OPENSSL_ED25519 + sig->raw_sig = ssh_string_copy(sig_blob); +#else + sig->ed25519_sig = malloc(ED25519_SIG_LEN); + if (sig->ed25519_sig == NULL){ + return SSH_ERROR; + } + memcpy(sig->ed25519_sig, ssh_string_data(sig_blob), ED25519_SIG_LEN); +#endif + + return SSH_OK; +} + diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c index 705830c..43d6089 100644 --- a/src/pki_gcrypt.c +++ b/src/pki_gcrypt.c @@ -139,7 +139,7 @@ static ssh_string asn1_get(ssh_buffer buffer, unsigned char want) { } if (ssh_buffer_get_data(buffer, ssh_string_data(str), size) == 0) { - ssh_string_free(str); + SSH_STRING_FREE(str); return NULL; } @@ -177,14 +177,14 @@ static ssh_string asn1_get_bit_string(ssh_buffer buffer) len = ssh_buffer_get_data(buffer, &unused, 1); if (len == 0) { - ssh_string_free(str); + SSH_STRING_FREE(str); return NULL; } if (unused == 0) { len = ssh_buffer_get_data(buffer, ssh_string_data(str), size); if (len == 0) { - ssh_string_free(str); + SSH_STRING_FREE(str); return NULL; } return str; @@ -197,7 +197,7 @@ static ssh_string asn1_get_bit_string(ssh_buffer buffer) len = ssh_buffer_get_data(buffer, &c, 1); if (len == 0) { - ssh_string_free(str); + SSH_STRING_FREE(str); return NULL; } *p = last | (c >> unused); @@ -315,7 +315,7 @@ static int privatekey_decrypt(int algo, int mode, unsigned int key_len, if (gcry_cipher_open(&cipher, algo, mode, 0) || gcry_cipher_setkey(cipher, key, key_len) || gcry_cipher_setiv(cipher, iv, iv_len) - || (tmp = calloc(ssh_buffer_get_len(data), sizeof(char))) == NULL + || (tmp = calloc(ssh_buffer_get_len(data), sizeof(unsigned char))) == NULL || gcry_cipher_decrypt(cipher, tmp, ssh_buffer_get_len(data), ssh_buffer_get(data), ssh_buffer_get_len(data))) { gcry_cipher_close(cipher); @@ -434,7 +434,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, header_end = ECDSA_HEADER_END; break; default: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -450,14 +450,14 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, get_next_line(p, len); } if (eol) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } /* skip header line */ get_next_line(p, len); if (eol) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -465,7 +465,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, /* skip line */ get_next_line(p, len); if (eol) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -474,24 +474,24 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, len = 0; get_next_line(p, len); if (eol) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } if (privatekey_dek_header(p, len, &algo, &mode, &key_len, &iv, &iv_len) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); SAFE_FREE(iv); return NULL; } } else { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); SAFE_FREE(iv); return NULL; } } else { if(len > 0) { if (ssh_buffer_add_data(buffer, p, len) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); SAFE_FREE(iv); return NULL; } @@ -501,7 +501,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, get_next_line(p, len); while(!eol && strncmp(p, header_end, header_end_size) != 0) { if (ssh_buffer_add_data(buffer, p, len) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); SAFE_FREE(iv); return NULL; } @@ -509,19 +509,19 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, } if (eol || strncmp(p, header_end, header_end_size) != 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); SAFE_FREE(iv); return NULL; } if (ssh_buffer_add_data(buffer, "\0", 1) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); SAFE_FREE(iv); return NULL; } out = base64_to_bin(ssh_buffer_get(buffer)); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (out == NULL) { SAFE_FREE(iv); return NULL; @@ -530,7 +530,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, if (algo) { if (privatekey_decrypt(algo, mode, key_len, iv, iv_len, out, cb, userdata, desc) < 0) { - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); SAFE_FREE(iv); return NULL; } @@ -561,20 +561,20 @@ static int b64decode_rsa_privatekey(const char *pkey, gcry_sexp_t *r, } if (!asn1_check_sequence(buffer)) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return 0; } v = asn1_get_int(buffer); if (v == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return 0; } data = ssh_string_data(v); if (ssh_string_len(v) != 1 || data[0] != 0) { - ssh_string_free(v); - ssh_buffer_free(buffer); + SSH_STRING_FREE(v); + SSH_BUFFER_FREE(buffer); return 0; } @@ -587,7 +587,7 @@ static int b64decode_rsa_privatekey(const char *pkey, gcry_sexp_t *r, unused2 = asn1_get_int(buffer); u = asn1_get_int(buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (n == NULL || e == NULL || d == NULL || p == NULL || q == NULL || unused1 == NULL || unused2 == NULL|| u == NULL) { @@ -608,20 +608,20 @@ static int b64decode_rsa_privatekey(const char *pkey, gcry_sexp_t *r, error: ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(d); - ssh_string_free(d); + SSH_STRING_FREE(d); ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(q); - ssh_string_free(q); - ssh_string_free(unused1); - ssh_string_free(unused2); + SSH_STRING_FREE(q); + SSH_STRING_FREE(unused1); + SSH_STRING_FREE(unused2); ssh_string_burn(u); - ssh_string_free(u); - ssh_string_free(v); + SSH_STRING_FREE(u); + SSH_STRING_FREE(v); return rc; } @@ -644,20 +644,20 @@ static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_c } if (!asn1_check_sequence(buffer)) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return 0; } v = asn1_get_int(buffer); if (v == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return 0; } data = ssh_string_data(v); if (ssh_string_len(v) != 1 || data[0] != 0) { - ssh_string_free(v); - ssh_buffer_free(buffer); + SSH_STRING_FREE(v); + SSH_BUFFER_FREE(buffer); return 0; } @@ -666,7 +666,7 @@ static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_c g = asn1_get_int(buffer); y = asn1_get_int(buffer); x = asn1_get_int(buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (p == NULL || q == NULL || g == NULL || y == NULL || x == NULL) { rc = 0; @@ -685,16 +685,16 @@ static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_c error: ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); ssh_string_burn(y); - ssh_string_free(y); + SSH_STRING_FREE(y); ssh_string_burn(x); - ssh_string_free(x); - ssh_string_free(v); + SSH_STRING_FREE(x); + SSH_STRING_FREE(v); return rc; } @@ -741,7 +741,7 @@ static int pki_key_ecdsa_to_nid(gcry_sexp_t k) static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(gcry_sexp_t k) { - static int nid; + int nid; nid = pki_key_ecdsa_to_nid(k); @@ -916,13 +916,13 @@ static int b64decode_ecdsa_privatekey(const char *pkey, gcry_sexp_t *r, } error: - ssh_buffer_free(buffer); - ssh_string_free(v); + SSH_BUFFER_FREE(buffer); + SSH_STRING_FREE(v); ssh_string_burn(d); - ssh_string_free(d); - ssh_string_free(oi); + SSH_STRING_FREE(d); + SSH_STRING_FREE(oi); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); return valid; } @@ -938,6 +938,8 @@ ssh_string pki_private_key_to_pem(const ssh_key key, (void) auth_fn; (void) auth_data; + SSH_LOG(SSH_LOG_WARN, "PEM export not supported by gcrypt backend!"); + return NULL; } @@ -1560,7 +1562,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) if (key->cert != NULL) { rc = ssh_buffer_add_buffer(buffer, key->cert); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } goto makestring; @@ -1568,14 +1570,14 @@ ssh_string pki_publickey_to_blob(const ssh_key key) type_s = ssh_string_from_char(key->type_c); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } rc = ssh_buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -1631,13 +1633,13 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); break; case SSH_KEYTYPE_RSA: @@ -1667,9 +1669,9 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); break; case SSH_KEYTYPE_ED25519: @@ -1685,21 +1687,21 @@ ssh_string pki_publickey_to_blob(const ssh_key key) type_s = ssh_string_from_char( pki_key_ecdsa_nid_to_char(key->ecdsa_nid)); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } rc = ssh_buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } e = ssh_sexp_extract_mpi(key->ecdsa, "q", GCRYMPI_FMT_STD, GCRYMPI_FMT_STD); if (e == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -1709,7 +1711,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); e = NULL; break; #endif @@ -1729,23 +1731,23 @@ makestring: if (rc < 0) { goto fail; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return str; fail: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); ssh_string_burn(str); - ssh_string_free(str); + SSH_STRING_FREE(str); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(p); - ssh_string_free(p); + SSH_STRING_FREE(p); ssh_string_burn(g); - ssh_string_free(g); + SSH_STRING_FREE(g); ssh_string_burn(q); - ssh_string_free(q); + SSH_STRING_FREE(q); ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); return NULL; } @@ -1832,7 +1834,7 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) gcry_sexp_release(sexp); break; case SSH_KEYTYPE_ED25519: - sig_blob = pki_ed25519_sig_to_blob(sig); + sig_blob = pki_ed25519_signature_to_blob(sig); break; case SSH_KEYTYPE_ECDSA_P256: case SSH_KEYTYPE_ECDSA_P384: @@ -1852,40 +1854,40 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) R = ssh_sexp_extract_mpi(sig->ecdsa_sig, "r", GCRYMPI_FMT_USG, GCRYMPI_FMT_STD); if (R == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } rc = ssh_buffer_add_ssh_string(b, R); - ssh_string_free(R); + SSH_STRING_FREE(R); if (rc < 0) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } S = ssh_sexp_extract_mpi(sig->ecdsa_sig, "s", GCRYMPI_FMT_USG, GCRYMPI_FMT_STD); if (S == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } rc = ssh_buffer_add_ssh_string(b, S); - ssh_string_free(S); + SSH_STRING_FREE(S); if (rc < 0) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } sig_blob = ssh_string_new(ssh_buffer_get_len(b)); if (sig_blob == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b)); - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); break; } #endif @@ -1945,7 +1947,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, SSH_LOG(SSH_LOG_DEBUG, "DSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("DSA signature", ssh_string_data(sig_blob), len); + ssh_log_hexdump("DSA signature", ssh_string_data(sig_blob), len); #endif err = gcry_sexp_build(&sig->dsa_sig, @@ -1980,7 +1982,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, #ifdef DEBUG_CRYPTO SSH_LOG(SSH_LOG_DEBUG, "RSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); + ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len); #endif err = gcry_sexp_build(&sig->rsa_sig, @@ -1994,7 +1996,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, } break; case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_sig_from_blob(sig, sig_blob); + rc = pki_signature_from_ed25519_blob(sig, sig_blob); if (rc != SSH_OK){ ssh_signature_free(sig); return NULL; @@ -2019,24 +2021,24 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, ssh_string_data(sig_blob), ssh_string_len(sig_blob)); if (rc < 0) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); ssh_signature_free(sig); return NULL; } r = ssh_buffer_get_ssh_string(b); if (r == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); ssh_signature_free(sig); return NULL; } s = ssh_buffer_get_ssh_string(b); rlen = ssh_buffer_get_len(b); - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); if (s == NULL) { ssh_string_burn(r); - ssh_string_free(r); + SSH_STRING_FREE(r); ssh_signature_free(sig); return NULL; } @@ -2047,16 +2049,16 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, "sigblob: %lu", (unsigned long)rlen); ssh_string_burn(r); - ssh_string_free(r); + SSH_STRING_FREE(r); ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); ssh_signature_free(sig); return NULL; } #ifdef DEBUG_CRYPTO - ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r)); - ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s)); + ssh_log_hexdump("r", ssh_string_data(r), ssh_string_len(r)); + ssh_log_hexdump("s", ssh_string_data(s), ssh_string_len(s)); #endif err = gcry_sexp_build(&sig->ecdsa_sig, @@ -2067,9 +2069,9 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, ssh_string_len(s), ssh_string_data(s)); ssh_string_burn(r); - ssh_string_free(r); + SSH_STRING_FREE(r); ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); if (err) { ssh_signature_free(sig); return NULL; @@ -2087,54 +2089,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, return sig; } -int pki_signature_verify(ssh_session session, - const ssh_signature sig, - const ssh_key key, - const unsigned char *input, - size_t input_len) -{ - int rc; - - if (session == NULL || sig == NULL || key == NULL || input == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Bad parameter provided to " - "pki_signature_verify()"); - return SSH_ERROR; - } - - if (ssh_key_type_plain(key->type) != sig->type) { - SSH_LOG(SSH_LOG_WARN, - "Can not verify %s signature with %s key", - sig->type_c, - key->type_c); - return SSH_ERROR; - } - - /* Check if public key and hash type are compatible */ - rc = pki_key_check_hash_compatible(key, sig->hash_type); - if (rc != SSH_OK) { - return SSH_ERROR; - } - - /* For ed25519 keys, verify using the input directly */ - if (key->type == SSH_KEYTYPE_ED25519 || - key->type == SSH_KEYTYPE_ED25519_CERT01) - { - rc = pki_ed25519_verify(key, sig, input, input_len); - } else { - /* For the other key types, calculate the hash and verify the signature */ - rc = pki_verify_data_signature(sig, key, input, input_len); - } - - if (rc != SSH_OK){ - ssh_set_error(session, - SSH_FATAL, - "Signature verification error"); - return SSH_ERROR; - } - - return SSH_OK; -} - ssh_signature pki_do_sign_hash(const ssh_key privkey, const unsigned char *hash, size_t hlen, @@ -2268,6 +2222,7 @@ ssh_signature pki_sign_data(const ssh_key privkey, size_t input_len) { unsigned char hash[SHA512_DIGEST_LEN] = {0}; + const unsigned char *sign_input = NULL; uint32_t hlen = 0; int rc; @@ -2287,27 +2242,38 @@ ssh_signature pki_sign_data(const ssh_key privkey, case SSH_DIGEST_SHA256: sha256(input, input_len, hash); hlen = SHA256_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_SHA384: sha384(input, input_len, hash); hlen = SHA384_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_SHA512: sha512(input, input_len, hash); hlen = SHA512_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_SHA1: sha1(input, input_len, hash); hlen = SHA_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_AUTO: + if (privkey->type == SSH_KEYTYPE_ED25519) { + /* SSH_DIGEST_AUTO should only be used with ed25519 */ + sign_input = input; + hlen = input_len; + break; + } + FALL_THROUGH; default: SSH_LOG(SSH_LOG_TRACE, "Unknown hash algorithm for type: %d", hash_type); return NULL; } - return pki_do_sign_hash(privkey, hash, hlen, hash_type); + return pki_do_sign_hash(privkey, sign_input, hlen, hash_type); } /** @@ -2336,6 +2302,8 @@ int pki_verify_data_signature(ssh_signature signature, unsigned char *hash = ghash + 1; uint32_t hlen = 0; + const unsigned char *verify_input = NULL; + int rc; if (pubkey == NULL || ssh_key_is_private(pubkey) || input == NULL || @@ -2357,23 +2325,35 @@ int pki_verify_data_signature(ssh_signature signature, sha256(input, input_len, hash); hlen = SHA256_DIGEST_LEN; hash_type = "sha256"; + verify_input = hash; break; case SSH_DIGEST_SHA384: sha384(input, input_len, hash); hlen = SHA384_DIGEST_LEN; hash_type = "sha384"; + verify_input = hash; break; case SSH_DIGEST_SHA512: sha512(input, input_len, hash); hlen = SHA512_DIGEST_LEN; hash_type = "sha512"; + verify_input = hash; break; case SSH_DIGEST_SHA1: sha1(input, input_len, hash); hlen = SHA_DIGEST_LEN; hash_type = "sha1"; + verify_input = hash; break; case SSH_DIGEST_AUTO: + if (pubkey->type == SSH_KEYTYPE_ED25519 || + pubkey->type == SSH_KEYTYPE_ED25519_CERT01) + { + verify_input = input; + hlen = input_len; + break; + } + FALL_THROUGH; default: SSH_LOG(SSH_LOG_TRACE, "Unknown sig->hash_type: %d", signature->hash_type); return SSH_ERROR; @@ -2461,6 +2441,14 @@ int pki_verify_data_signature(ssh_signature signature, } break; #endif + case SSH_KEYTYPE_ED25519: + case SSH_KEYTYPE_ED25519_CERT01: + rc = pki_ed25519_verify(pubkey, signature, verify_input, hlen); + if (rc != SSH_OK) { + SSH_LOG(SSH_LOG_TRACE, "ED25519 error: Signature invalid"); + return SSH_ERROR; + } + break; case SSH_KEYTYPE_RSA1: case SSH_KEYTYPE_UNKNOWN: default: diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c index 3993c74..6e3d425 100644 --- a/src/pki_mbedcrypto.c +++ b/src/pki_mbedcrypto.c @@ -32,6 +32,7 @@ #include "libssh/pki_priv.h" #include "libssh/buffer.h" #include "libssh/bignum.h" +#include "libssh/misc.h" #define MAX_PASSPHRASE_SIZE 1024 #define MAX_KEY_SIZE 32 @@ -63,7 +64,7 @@ static int pki_key_ecdsa_to_nid(mbedtls_ecdsa_context *ecdsa) static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(mbedtls_ecdsa_context *ecdsa) { - static int nid; + int nid; nid = pki_key_ecdsa_to_nid(ecdsa); @@ -112,7 +113,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase, valid = auth_fn("Passphrase for private key:", (char *) tmp, MAX_PASSPHRASE_SIZE, 0, 0, auth_data); if (valid < 0) { - return NULL; + goto fail; } /* TODO fix signedness and strlen */ valid = mbedtls_pk_parse_key(rsa, @@ -154,7 +155,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase, valid = auth_fn("Passphrase for private key:", (char *) tmp, MAX_PASSPHRASE_SIZE, 0, 0, auth_data); if (valid < 0) { - return NULL; + goto fail; } valid = mbedtls_pk_parse_key(ecdsa, (const unsigned char *) b64_key, @@ -245,7 +246,7 @@ int pki_privkey_build_rsa(ssh_key key, ssh_string n, ssh_string e, ssh_string d, - ssh_string iqmp, + UNUSED_PARAM(ssh_string iqmp), ssh_string p, ssh_string q) { @@ -595,7 +596,7 @@ ssh_string make_ecpoint_string(const mbedtls_ecp_group *g, const rc = mbedtls_ecp_point_write_binary(g, p, MBEDTLS_ECP_PF_UNCOMPRESSED, &len, ssh_string_data(s), ssh_string_len(s)); if (rc == MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) { - ssh_string_free(s); + SSH_STRING_FREE(s); s = ssh_string_new(len); if (s == NULL) { @@ -607,12 +608,12 @@ ssh_string make_ecpoint_string(const mbedtls_ecp_group *g, const } if (rc != 0) { - ssh_string_free(s); + SSH_STRING_FREE(s); return NULL; } if (len != ssh_string_len(s)) { - ssh_string_free(s); + SSH_STRING_FREE(s); return NULL; } @@ -652,7 +653,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) if (key->cert != NULL) { rc = ssh_buffer_add_buffer(buffer, key->cert); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -661,14 +662,14 @@ ssh_string pki_publickey_to_blob(const ssh_key key) type_s = ssh_string_from_char(key->type_c); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } rc = ssh_buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -676,7 +677,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) case SSH_KEYTYPE_RSA: { mbedtls_rsa_context *rsa; if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) == 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -701,10 +702,10 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); e = NULL; ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); n = NULL; break; @@ -715,21 +716,21 @@ ssh_string pki_publickey_to_blob(const ssh_key key) type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid)); if (type_s == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } rc = ssh_buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); + SSH_STRING_FREE(type_s); if (rc < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } e = make_ecpoint_string(&key->ecdsa->grp, &key->ecdsa->Q); if (e == NULL) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return NULL; } @@ -739,7 +740,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); e = NULL; break; @@ -764,16 +765,16 @@ makestring: goto fail; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return str; fail: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); ssh_string_burn(str); - ssh_string_free(str); + SSH_STRING_FREE(str); ssh_string_burn(e); - ssh_string_free(e); + SSH_STRING_FREE(e); ssh_string_burn(n); - ssh_string_free(n); + SSH_STRING_FREE(n); return NULL; } @@ -801,42 +802,42 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) r = ssh_make_bignum_string(sig->ecdsa_sig.r); if (r == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } rc = ssh_buffer_add_ssh_string(b, r); - ssh_string_free(r); + SSH_STRING_FREE(r); if (rc < 0) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } s = ssh_make_bignum_string(sig->ecdsa_sig.s); if (s == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } rc = ssh_buffer_add_ssh_string(b, s); - ssh_string_free(s); + SSH_STRING_FREE(s); if (rc < 0) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } sig_blob = ssh_string_new(ssh_buffer_get_len(b)); if (sig_blob == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); return NULL; } ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b)); - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); break; } case SSH_KEYTYPE_ED25519: - sig_blob = pki_ed25519_sig_to_blob(sig); + sig_blob = pki_ed25519_signature_to_blob(sig); break; default: SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %s", @@ -873,7 +874,7 @@ static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, const } #ifdef DEBUG_CRYPTO SSH_LOG(SSH_LOG_WARN, "RSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); + ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len); #endif if (len == rsalen) { @@ -954,43 +955,43 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, ssh_string_len(sig_blob)); if (rc < 0) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); ssh_signature_free(sig); return NULL; } r = ssh_buffer_get_ssh_string(b); if (r == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); ssh_signature_free(sig); return NULL; } #ifdef DEBUG_CRYPTO - ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r)); + ssh_log_hexdump("r", ssh_string_data(r), ssh_string_len(r)); #endif sig->ecdsa_sig.r = ssh_make_string_bn(r); ssh_string_burn(r); - ssh_string_free(r); + SSH_STRING_FREE(r); if (sig->ecdsa_sig.r == NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); ssh_signature_free(sig); return NULL; } s = ssh_buffer_get_ssh_string(b); rlen = ssh_buffer_get_len(b); - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); if (s == NULL) { ssh_signature_free(sig); return NULL; } #ifdef DEBUG_CRYPTO - ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s)); + ssh_log_hexdump("s", ssh_string_data(s), ssh_string_len(s)); #endif sig->ecdsa_sig.s = ssh_make_string_bn(s); ssh_string_burn(s); - ssh_string_free(s); + SSH_STRING_FREE(s); if (sig->ecdsa_sig.s == NULL) { ssh_signature_free(sig); return NULL; @@ -1007,7 +1008,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, break; } case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_sig_from_blob(sig, sig_blob); + rc = pki_signature_from_ed25519_blob(sig, sig_blob); if (rc == SSH_ERROR) { ssh_signature_free(sig); return NULL; @@ -1021,51 +1022,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, return sig; } -int pki_signature_verify(ssh_session session, const ssh_signature sig, const - ssh_key key, const unsigned char *input, size_t input_len) -{ - int rc; - - if (session == NULL || sig == NULL || key == NULL || input == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Bad parameter provided to " - "pki_signature_verify()"); - return SSH_ERROR; - } - - if (ssh_key_type_plain(key->type) != sig->type) { - SSH_LOG(SSH_LOG_WARN, - "Can not verify %s signature with %s key", - sig->type_c, - key->type_c); - return SSH_ERROR; - } - - /* Check if public key and hash type are compatible */ - rc = pki_key_check_hash_compatible(key, sig->hash_type); - if (rc != SSH_OK) { - return SSH_ERROR; - } - - /* For ed25519 keys, verify using the input directly */ - if (key->type == SSH_KEYTYPE_ED25519 || - key->type == SSH_KEYTYPE_ED25519_CERT01) - { - rc = pki_ed25519_verify(key, sig, input, input_len); - } else { - /* For the other key types, calculate the hash and verify the signature */ - rc = pki_verify_data_signature(sig, key, input, input_len); - } - - if (rc != SSH_OK){ - ssh_set_error(session, - SSH_FATAL, - "Signature verification error"); - return SSH_ERROR; - } - - return SSH_OK; -} - static ssh_string rsa_do_sign_hash(const unsigned char *digest, int dlen, mbedtls_pk_context *privkey, @@ -1213,6 +1169,7 @@ ssh_signature pki_sign_data(const ssh_key privkey, size_t input_len) { unsigned char hash[SHA512_DIGEST_LEN] = {0}; + const unsigned char *sign_input = NULL; uint32_t hlen = 0; int rc; @@ -1232,27 +1189,38 @@ ssh_signature pki_sign_data(const ssh_key privkey, case SSH_DIGEST_SHA256: sha256(input, input_len, hash); hlen = SHA256_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_SHA384: sha384(input, input_len, hash); hlen = SHA384_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_SHA512: sha512(input, input_len, hash); hlen = SHA512_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_SHA1: sha1(input, input_len, hash); hlen = SHA_DIGEST_LEN; + sign_input = hash; break; case SSH_DIGEST_AUTO: + if (privkey->type == SSH_KEYTYPE_ED25519) { + /* SSH_DIGEST_AUTO should only be used with ed25519 */ + sign_input = input; + hlen = input_len; + break; + } + FALL_THROUGH; default: SSH_LOG(SSH_LOG_TRACE, "Unknown hash algorithm for type: %d", hash_type); return NULL; } - return pki_do_sign_hash(privkey, hash, hlen, hash_type); + return pki_do_sign_hash(privkey, sign_input, hlen, hash_type); } /** @@ -1275,6 +1243,7 @@ int pki_verify_data_signature(ssh_signature signature, { unsigned char hash[SHA512_DIGEST_LEN] = {0}; + const unsigned char *verify_input = NULL; uint32_t hlen = 0; mbedtls_md_type_t md = 0; @@ -1300,23 +1269,35 @@ int pki_verify_data_signature(ssh_signature signature, sha256(input, input_len, hash); hlen = SHA256_DIGEST_LEN; md = MBEDTLS_MD_SHA256; + verify_input = hash; break; case SSH_DIGEST_SHA384: sha384(input, input_len, hash); hlen = SHA384_DIGEST_LEN; md = MBEDTLS_MD_SHA384; + verify_input = hash; break; case SSH_DIGEST_SHA512: sha512(input, input_len, hash); hlen = SHA512_DIGEST_LEN; md = MBEDTLS_MD_SHA512; + verify_input = hash; break; case SSH_DIGEST_SHA1: sha1(input, input_len, hash); hlen = SHA_DIGEST_LEN; md = MBEDTLS_MD_SHA1; + verify_input = hash; break; case SSH_DIGEST_AUTO: + if (pubkey->type == SSH_KEYTYPE_ED25519 || + pubkey->type == SSH_KEYTYPE_ED25519_CERT01) + { + verify_input = input; + hlen = input_len; + break; + } + FALL_THROUGH; default: SSH_LOG(SSH_LOG_TRACE, "Unknown sig->hash_type: %d", signature->hash_type); @@ -1353,6 +1334,14 @@ int pki_verify_data_signature(ssh_signature signature, } break; + case SSH_KEYTYPE_ED25519: + case SSH_KEYTYPE_ED25519_CERT01: + rc = pki_ed25519_verify(pubkey, signature, verify_input, hlen); + if (rc != SSH_OK) { + SSH_LOG(SSH_LOG_TRACE, "ED25519 error: Signature invalid"); + return SSH_ERROR; + } + break; default: SSH_LOG(SSH_LOG_TRACE, "Unknown public key type"); return SSH_ERROR; diff --git a/src/scp.c b/src/scp.c index fd9aaaa..85d670a 100644 --- a/src/scp.c +++ b/src/scp.c @@ -29,6 +29,7 @@ #include "libssh/priv.h" #include "libssh/scp.h" +#include "libssh/misc.h" /** * @defgroup libssh_scp The SSH scp functions @@ -57,30 +58,53 @@ * * @returns A ssh_scp handle, NULL if the creation was impossible. */ -ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){ - ssh_scp scp=malloc(sizeof(struct ssh_scp_struct)); - if(scp == NULL){ - ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp"); - return NULL; - } - ZERO_STRUCTP(scp); - if((mode&~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && (mode &~SSH_SCP_RECURSIVE) != SSH_SCP_READ){ - ssh_set_error(session,SSH_FATAL,"Invalid mode %d for ssh_scp_new()",mode); - ssh_scp_free(scp); - return NULL; - } - scp->location=strdup(location); - if (scp->location == NULL) { - ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp"); +ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location) +{ + ssh_scp scp = NULL; + + if (session == NULL) { + goto error; + } + + scp = (ssh_scp)calloc(1, sizeof(struct ssh_scp_struct)); + if (scp == NULL) { + ssh_set_error(session, SSH_FATAL, + "Error allocating memory for ssh_scp"); + goto error; + } + + if ((mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && + (mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_READ) + { + ssh_set_error(session, SSH_FATAL, + "Invalid mode %d for ssh_scp_new()", mode); + goto error; + } + + if (strlen(location) > 32 * 1024) { + ssh_set_error(session, SSH_FATAL, + "Location path is too long"); + goto error; + } + + scp->location = strdup(location); + if (scp->location == NULL) { + ssh_set_error(session, SSH_FATAL, + "Error allocating memory for ssh_scp"); + goto error; + } + + scp->session = session; + scp->mode = mode & ~SSH_SCP_RECURSIVE; + scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0; + scp->channel = NULL; + scp->state = SSH_SCP_NEW; + + return scp; + +error: ssh_scp_free(scp); return NULL; - } - scp->session=session; - scp->mode=mode & ~SSH_SCP_RECURSIVE; - scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0; - scp->channel=NULL; - scp->state=SSH_SCP_NEW; - return scp; } /** @@ -94,59 +118,115 @@ ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){ */ int ssh_scp_init(ssh_scp scp) { - int r; - char execbuffer[1024]; - uint8_t code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_NEW){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_init called under invalid state"); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'", - scp->mode==SSH_SCP_WRITE?"write":"read", - scp->recursive?"recursive ":"", - scp->location); - scp->channel=ssh_channel_new(scp->session); - if(scp->channel == NULL){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r= ssh_channel_open_session(scp->channel); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(scp->mode == SSH_SCP_WRITE) - snprintf(execbuffer,sizeof(execbuffer),"scp -t %s %s", - scp->recursive ? "-r":"", scp->location); - else - snprintf(execbuffer,sizeof(execbuffer),"scp -f %s %s", - scp->recursive ? "-r":"", scp->location); - if(ssh_channel_request_exec(scp->channel,execbuffer) == SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(scp->mode == SSH_SCP_WRITE){ - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - } else { - ssh_channel_write(scp->channel,"",1); - } - if(scp->mode == SSH_SCP_WRITE) - scp->state=SSH_SCP_WRITE_INITED; - else - scp->state=SSH_SCP_READ_INITED; - return SSH_OK; + int rc; + char execbuffer[1024] = {0}; + char *quoted_location = NULL; + size_t quoted_location_len = 0; + size_t scp_location_len; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_NEW) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_init called under invalid state"); + return SSH_ERROR; + } + + if (scp->location == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Invalid scp context: location is NULL"); + return SSH_ERROR; + } + + SSH_LOG(SSH_LOG_PROTOCOL, "Initializing scp session %s %son location '%s'", + scp->mode == SSH_SCP_WRITE?"write":"read", + scp->recursive ? "recursive " : "", + scp->location); + + scp->channel = ssh_channel_new(scp->session); + if (scp->channel == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Channel creation failed for scp"); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + rc = ssh_channel_open_session(scp->channel); + if (rc == SSH_ERROR) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to open channel for scp"); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + /* In the worst case, each character would be replaced by 3 plus the string + * terminator '\0' */ + scp_location_len = strlen(scp->location); + quoted_location_len = ((size_t)3 * scp_location_len) + 1; + /* Paranoia check */ + if (quoted_location_len < scp_location_len) { + ssh_set_error(scp->session, SSH_FATAL, + "Buffer overflow detected"); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + quoted_location = (char *)calloc(1, quoted_location_len); + if (quoted_location == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to allocate memory for quoted location"); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + rc = ssh_quote_file_name(scp->location, quoted_location, + quoted_location_len); + if (rc <= 0) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to single quote command location"); + SAFE_FREE(quoted_location); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + if (scp->mode == SSH_SCP_WRITE) { + snprintf(execbuffer, sizeof(execbuffer), "scp -t %s %s", + scp->recursive ? "-r" : "", quoted_location); + } else { + snprintf(execbuffer, sizeof(execbuffer), "scp -f %s %s", + scp->recursive ? "-r" : "", quoted_location); + } + + SAFE_FREE(quoted_location); + + SSH_LOG(SSH_LOG_DEBUG, "Executing command: %s", execbuffer); + + rc = ssh_channel_request_exec(scp->channel, execbuffer); + if (rc == SSH_ERROR){ + ssh_set_error(scp->session, SSH_FATAL, + "Failed executing command: %s", execbuffer); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + if (scp->mode == SSH_SCP_WRITE) { + rc = ssh_scp_response(scp, NULL); + if (rc != 0) { + return SSH_ERROR; + } + } else { + ssh_channel_write(scp->channel, "", 1); + } + + if (scp->mode == SSH_SCP_WRITE) { + scp->state = SSH_SCP_WRITE_INITED; + } else { + scp->state = SSH_SCP_READ_INITED; + } + + return SSH_OK; } /** @@ -160,33 +240,40 @@ int ssh_scp_init(ssh_scp scp) */ int ssh_scp_close(ssh_scp scp) { - char buffer[128]; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->channel != NULL){ - if(ssh_channel_send_eof(scp->channel) == SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - /* avoid situations where data are buffered and - * not yet stored on disk. This can happen if the close is sent - * before we got the EOF back - */ - while(!ssh_channel_is_eof(scp->channel)){ - err=ssh_channel_read(scp->channel,buffer,sizeof(buffer),0); - if(err==SSH_ERROR || err==0) - break; + char buffer[128] = {0}; + int rc; + + if (scp == NULL) { + return SSH_ERROR; } - if(ssh_channel_close(scp->channel) == SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; + + if (scp->channel != NULL) { + if (ssh_channel_send_eof(scp->channel) == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + /* avoid situations where data are buffered and + * not yet stored on disk. This can happen if the close is sent + * before we got the EOF back + */ + while (!ssh_channel_is_eof(scp->channel)) { + rc = ssh_channel_read(scp->channel, buffer, sizeof(buffer), 0); + if (rc == SSH_ERROR || rc == 0) { + break; + } + } + + if (ssh_channel_close(scp->channel) == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + ssh_channel_free(scp->channel); + scp->channel = NULL; } - ssh_channel_free(scp->channel); - scp->channel=NULL; - } - scp->state=SSH_SCP_NEW; - return SSH_OK; + + scp->state = SSH_SCP_NEW; + return SSH_OK; } /** @@ -198,16 +285,22 @@ int ssh_scp_close(ssh_scp scp) */ void ssh_scp_free(ssh_scp scp) { - if(scp==NULL) - return; - if(scp->state != SSH_SCP_NEW) - ssh_scp_close(scp); - if(scp->channel) - ssh_channel_free(scp->channel); - SAFE_FREE(scp->location); - SAFE_FREE(scp->request_name); - SAFE_FREE(scp->warning); - SAFE_FREE(scp); + if (scp == NULL) { + return; + } + + if (scp->state != SSH_SCP_NEW) { + ssh_scp_close(scp); + } + + if (scp->channel) { + ssh_channel_free(scp->channel); + } + + SAFE_FREE(scp->location); + SAFE_FREE(scp->request_name); + SAFE_FREE(scp->warning); + SAFE_FREE(scp); } /** @@ -224,81 +317,127 @@ void ssh_scp_free(ssh_scp scp) * * @see ssh_scp_leave_directory() */ -int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode){ - char buffer[1024]; - int r; - uint8_t code; - char *dir; - char *perms; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_directory called under invalid state"); - return SSH_ERROR; - } - dir=ssh_basename(dirname); - perms=ssh_scp_string_mode(mode); - snprintf(buffer, sizeof(buffer), "D%s 0 %s\n", perms, dir); - SAFE_FREE(dir); - SAFE_FREE(perms); - r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; +int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode) +{ + char buffer[1024] = {0}; + int rc; + char *dir = NULL; + char *perms = NULL; + char *vis_encoded = NULL; + size_t vis_encoded_len; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_WRITE_INITED) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_push_directory called under invalid state"); + return SSH_ERROR; + } + + dir = ssh_basename(dirname); + if (dir == NULL) { + ssh_set_error_oom(scp->session); + return SSH_ERROR; + } + + vis_encoded_len = (2 * strlen(dir)) + 1; + vis_encoded = (char *)calloc(1, vis_encoded_len); + if (vis_encoded == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to allocate buffer to vis encode directory name"); + goto error; + } + + rc = ssh_newline_vis(dir, vis_encoded, vis_encoded_len); + if (rc <= 0) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to vis encode directory name"); + goto error; + } + + perms = ssh_scp_string_mode(mode); + if (perms == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to get directory permission string"); + goto error; + } + + SSH_LOG(SSH_LOG_PROTOCOL, + "SCP pushing directory %s with permissions '%s'", + vis_encoded, perms); + + /* Use vis encoded directory name */ + snprintf(buffer, sizeof(buffer), + "D%s 0 %s\n", + perms, vis_encoded); + + SAFE_FREE(dir); + SAFE_FREE(perms); + SAFE_FREE(vis_encoded); + + rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); + if (rc == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + rc = ssh_scp_response(scp, NULL); + if (rc != 0) { + return SSH_ERROR; + } + + return SSH_OK; + +error: + SAFE_FREE(dir); + SAFE_FREE(perms); + SAFE_FREE(vis_encoded); + return SSH_ERROR; - } - return SSH_OK; } /** * @brief Leave a directory. * - * @returns SSH_OK if the directory has been left,SSH_ERROR if an + * @returns SSH_OK if the directory has been left, SSH_ERROR if an * error occured. * * @see ssh_scp_push_directory() */ - int ssh_scp_leave_directory(ssh_scp scp){ - char buffer[]="E\n"; - int r; - uint8_t code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_leave_directory called under invalid state"); - return SSH_ERROR; - } - r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - return SSH_OK; +int ssh_scp_leave_directory(ssh_scp scp) +{ + char buffer[] = "E\n"; + int rc; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_WRITE_INITED) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_leave_directory called under invalid state"); + return SSH_ERROR; + } + + rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); + if (rc == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + rc = ssh_scp_response(scp, NULL); + if (rc != 0) { + return SSH_ERROR; + } + + return SSH_OK; } /** - * @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit size. + * @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit + * size. * * @param[in] scp The scp handle. * @@ -314,44 +453,90 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode){ * * @see ssh_scp_push_file() */ -int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int mode){ - char buffer[1024]; - int r; - uint8_t code; - char *file; - char *perms; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_file called under invalid state"); - return SSH_ERROR; - } - file=ssh_basename(filename); - perms=ssh_scp_string_mode(mode); - SSH_LOG(SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",file,size,perms); - snprintf(buffer, sizeof(buffer), "C%s %" PRIu64 " %s\n", perms, size, file); - SAFE_FREE(file); - SAFE_FREE(perms); - r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; +int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, + int mode) +{ + char buffer[1024] = {0}; + int rc; + char *file = NULL; + char *perms = NULL; + char *vis_encoded = NULL; + size_t vis_encoded_len; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_WRITE_INITED) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_push_file called under invalid state"); + return SSH_ERROR; + } + + file = ssh_basename(filename); + if (file == NULL) { + ssh_set_error_oom(scp->session); + return SSH_ERROR; + } + + vis_encoded_len = (2 * strlen(file)) + 1; + vis_encoded = (char *)calloc(1, vis_encoded_len); + if (vis_encoded == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to allocate buffer to vis encode file name"); + goto error; + } + + rc = ssh_newline_vis(file, vis_encoded, vis_encoded_len); + if (rc <= 0) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to vis encode file name"); + goto error; + } + + perms = ssh_scp_string_mode(mode); + if (perms == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to get file permission string"); + goto error; + } + + SSH_LOG(SSH_LOG_PROTOCOL, + "SCP pushing file %s, size %" PRIu64 " with permissions '%s'", + vis_encoded, size, perms); + + /* Use vis encoded file name */ + snprintf(buffer, sizeof(buffer), + "C%s %" PRIu64 " %s\n", + perms, size, vis_encoded); + + SAFE_FREE(file); + SAFE_FREE(perms); + SAFE_FREE(vis_encoded); + + rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); + if (rc == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + rc = ssh_scp_response(scp, NULL); + if (rc != 0) { + return SSH_ERROR; + } + + scp->filelen = size; + scp->processed = 0; + scp->state = SSH_SCP_WRITE_WRITING; + + return SSH_OK; + +error: + SAFE_FREE(file); + SAFE_FREE(perms); + SAFE_FREE(vis_encoded); + return SSH_ERROR; - } - scp->filelen = size; - scp->processed = 0; - scp->state=SSH_SCP_WRITE_WRITING; - return SSH_OK; } /** @@ -369,8 +554,9 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int mo * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an * error occured. */ -int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode){ - return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode); +int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode) +{ + return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode); } /** @@ -385,41 +571,60 @@ int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode){ * * @returns The return code, SSH_ERROR a error occured. */ -int ssh_scp_response(ssh_scp scp, char **response){ - unsigned char code; - int r; - char msg[128]; - if(scp==NULL) - return SSH_ERROR; - r=ssh_channel_read(scp->channel,&code,1,0); - if(r == SSH_ERROR) - return SSH_ERROR; - if(code == 0) - return 0; - if(code > 2){ - ssh_set_error(scp->session,SSH_FATAL, "SCP: invalid status code %ud received", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_scp_read_string(scp,msg,sizeof(msg)); - if(r==SSH_ERROR) - return r; - /* Warning */ - if(code == 1){ - ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Warning: status code 1 received: %s", msg); - SSH_LOG(SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg); - if(response) - *response=strdup(msg); - return 1; - } - if(code == 2){ - ssh_set_error(scp->session,SSH_FATAL, "SCP: Error: status code 2 received: %s", msg); - if(response) - *response=strdup(msg); - return 2; - } - /* Not reached */ - return SSH_ERROR; +int ssh_scp_response(ssh_scp scp, char **response) +{ + unsigned char code; + int rc; + char msg[128] = {0}; + + if (scp == NULL) { + return SSH_ERROR; + } + + rc = ssh_channel_read(scp->channel, &code, 1, 0); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + if (code == 0) { + return 0; + } + + if (code > 2) { + ssh_set_error(scp->session, SSH_FATAL, + "SCP: invalid status code %u received", code); + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + rc = ssh_scp_read_string(scp, msg, sizeof(msg)); + if (rc == SSH_ERROR) { + return rc; + } + + /* Warning */ + if (code == 1) { + ssh_set_error(scp->session, SSH_REQUEST_DENIED, + "SCP: Warning: status code 1 received: %s", msg); + SSH_LOG(SSH_LOG_RARE, + "SCP: Warning: status code 1 received: %s", msg); + if (response) { + *response = strdup(msg); + } + return 1; + } + + if (code == 2) { + ssh_set_error(scp->session, SSH_FATAL, + "SCP: Error: status code 2 received: %s", msg); + if (response) { + *response = strdup(msg); + } + return 2; + } + + /* Not reached */ + return SSH_ERROR; } /** @@ -434,57 +639,66 @@ int ssh_scp_response(ssh_scp scp, char **response){ * @returns SSH_OK if the write was successful, SSH_ERROR an error * occured while writing. */ -int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len){ - int w; - int r; - uint8_t code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_WRITING){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_write called under invalid state"); - return SSH_ERROR; - } - if(scp->processed + len > scp->filelen) - len = (size_t) (scp->filelen - scp->processed); - /* hack to avoid waiting for window change */ - r = ssh_channel_poll(scp->channel, 0); - if (r == SSH_ERROR) { - scp->state = SSH_SCP_ERROR; - return SSH_ERROR; - } - w=ssh_channel_write(scp->channel,buffer,len); - if(w != SSH_ERROR) - scp->processed += w; - else { - scp->state=SSH_SCP_ERROR; - //return=channel_get_exit_status(scp->channel); - return SSH_ERROR; - } - /* Far end sometimes send a status message, which we need to read - * and handle */ - r = ssh_channel_poll(scp->channel,0); - if(r > 0){ - r = ssh_channel_read(scp->channel, &code, 1, 0); - if(r == SSH_ERROR){ - return SSH_ERROR; - } - if(code == 1 || code == 2){ - ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Error: status code %i received", code); - return SSH_ERROR; - } - } - /* Check if we arrived at end of file */ - if(scp->processed == scp->filelen) { - code = 0; - w = ssh_channel_write(scp->channel, &code, 1); - if(w == SSH_ERROR){ - scp->state = SSH_SCP_ERROR; - return SSH_ERROR; - } - scp->processed=scp->filelen=0; - scp->state=SSH_SCP_WRITE_INITED; - } - return SSH_OK; +int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len) +{ + int w; + int rc; + uint8_t code; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_WRITE_WRITING) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_write called under invalid state"); + return SSH_ERROR; + } + + if (scp->processed + len > scp->filelen) { + len = (size_t) (scp->filelen - scp->processed); + } + + /* hack to avoid waiting for window change */ + rc = ssh_channel_poll(scp->channel, 0); + if (rc == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + w = ssh_channel_write(scp->channel, buffer, len); + if (w != SSH_ERROR) { + scp->processed += w; + } else { + scp->state = SSH_SCP_ERROR; + //return = channel_get_exit_status(scp->channel); + return SSH_ERROR; + } + + /* Far end sometimes send a status message, which we need to read + * and handle */ + rc = ssh_channel_poll(scp->channel, 0); + if (rc > 0) { + rc = ssh_scp_response(scp, NULL); + if (rc != 0) { + return SSH_ERROR; + } + } + + /* Check if we arrived at end of file */ + if (scp->processed == scp->filelen) { + code = 0; + w = ssh_channel_write(scp->channel, &code, 1); + if (w == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + scp->processed = scp->filelen = 0; + scp->state = SSH_SCP_WRITE_INITED; + } + + return SSH_OK; } /** @@ -501,27 +715,36 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len){ * @returns SSH_OK if the string was read, SSH_ERROR if an error * occured while reading. */ -int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){ - size_t r=0; - int err=SSH_OK; - if(scp==NULL) - return SSH_ERROR; - while(rchannel,&buffer[r],1,0); - if(err==SSH_ERROR){ - break; - } - if(err==0){ - ssh_set_error(scp->session,SSH_FATAL,"End of file while reading string"); - err=SSH_ERROR; - break; - } - r++; - if(buffer[r-1] == '\n') - break; - } - buffer[r]=0; - return err; +int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len) +{ + size_t read = 0; + int err = SSH_OK; + + if (scp == NULL) { + return SSH_ERROR; + } + + while (read < len - 1) { + err = ssh_channel_read(scp->channel, &buffer[read], 1, 0); + if (err == SSH_ERROR) { + break; + } + + if (err == 0) { + ssh_set_error(scp->session, SSH_FATAL, + "End of file while reading string"); + err = SSH_ERROR; + break; + } + + read++; + if (buffer[read - 1] == '\n') { + break; + } + } + + buffer[read] = 0; + return err; } /** @@ -544,90 +767,105 @@ int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){ * @see ssh_scp_accept_request() * @see ssh_scp_request_get_warning() */ -int ssh_scp_pull_request(ssh_scp scp){ - char buffer[MAX_BUF_SIZE] = {0}; - char *mode=NULL; - char *p,*tmp; - uint64_t size; - char *name=NULL; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_READ_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_pull_request called under invalid state"); - return SSH_ERROR; - } - err=ssh_scp_read_string(scp,buffer,sizeof(buffer)); - if(err==SSH_ERROR){ - if(ssh_channel_is_eof(scp->channel)){ - scp->state=SSH_SCP_TERMINATED; - return SSH_SCP_REQUEST_EOF; - } - return err; - } - p=strchr(buffer,'\n'); - if(p!=NULL) - *p='\0'; - SSH_LOG(SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer); - switch(buffer[0]){ +int ssh_scp_pull_request(ssh_scp scp) +{ + char buffer[MAX_BUF_SIZE] = {0}; + char *mode = NULL; + char *p, *tmp; + uint64_t size; + char *name = NULL; + int rc; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_READ_INITED) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_pull_request called under invalid state"); + return SSH_ERROR; + } + + rc = ssh_scp_read_string(scp, buffer, sizeof(buffer)); + if (rc == SSH_ERROR) { + if (ssh_channel_is_eof(scp->channel)) { + scp->state = SSH_SCP_TERMINATED; + return SSH_SCP_REQUEST_EOF; + } + return rc; + } + + p = strchr(buffer, '\n'); + if (p != NULL) { + *p = '\0'; + } + + SSH_LOG(SSH_LOG_PROTOCOL, "Received SCP request: '%s'", buffer); + switch(buffer[0]) { case 'C': - /* File */ + /* File */ case 'D': - /* Directory */ - p=strchr(buffer,' '); - if(p==NULL) - goto error; - *p='\0'; - p++; - //mode=strdup(&buffer[1]); - scp->request_mode=ssh_scp_integer_mode(&buffer[1]); - tmp=p; - p=strchr(p,' '); - if(p==NULL) - goto error; - *p=0; - size = strtoull(tmp,NULL,10); - p++; - name=strdup(p); - SAFE_FREE(scp->request_name); - scp->request_name=name; - if(buffer[0]=='C'){ - scp->filelen=size; - scp->request_type=SSH_SCP_REQUEST_NEWFILE; - } else { - scp->filelen='0'; - scp->request_type=SSH_SCP_REQUEST_NEWDIR; - } - scp->state=SSH_SCP_READ_REQUESTED; - scp->processed = 0; - return scp->request_type; - break; + /* Directory */ + p = strchr(buffer, ' '); + if (p == NULL) { + goto error; + } + *p = '\0'; + p++; + //mode = strdup(&buffer[1]); + scp->request_mode = ssh_scp_integer_mode(&buffer[1]); + tmp = p; + p = strchr(p, ' '); + if (p == NULL) { + goto error; + } + *p = 0; + size = strtoull(tmp, NULL, 10); + p++; + name = strdup(p); + SAFE_FREE(scp->request_name); + scp->request_name = name; + if (buffer[0] == 'C') { + scp->filelen = size; + scp->request_type = SSH_SCP_REQUEST_NEWFILE; + } else { + scp->filelen = '0'; + scp->request_type = SSH_SCP_REQUEST_NEWDIR; + } + scp->state = SSH_SCP_READ_REQUESTED; + scp->processed = 0; + return scp->request_type; + break; case 'E': - scp->request_type=SSH_SCP_REQUEST_ENDDIR; - ssh_channel_write(scp->channel,"",1); - return scp->request_type; + scp->request_type = SSH_SCP_REQUEST_ENDDIR; + ssh_channel_write(scp->channel, "", 1); + return scp->request_type; case 0x1: - ssh_set_error(scp->session,SSH_REQUEST_DENIED,"SCP: Warning: %s",&buffer[1]); - scp->request_type=SSH_SCP_REQUEST_WARNING; - SAFE_FREE(scp->warning); - scp->warning=strdup(&buffer[1]); - return scp->request_type; + ssh_set_error(scp->session, SSH_REQUEST_DENIED, + "SCP: Warning: %s", &buffer[1]); + scp->request_type = SSH_SCP_REQUEST_WARNING; + SAFE_FREE(scp->warning); + scp->warning = strdup(&buffer[1]); + return scp->request_type; case 0x2: - ssh_set_error(scp->session,SSH_FATAL,"SCP: Error: %s",&buffer[1]); - return SSH_ERROR; + ssh_set_error(scp->session, SSH_FATAL, + "SCP: Error: %s", &buffer[1]); + return SSH_ERROR; case 'T': - /* Timestamp */ + /* Timestamp */ default: - ssh_set_error(scp->session,SSH_FATAL,"Unhandled message: (%d)%s",buffer[0],buffer); - return SSH_ERROR; - } - - /* a parsing error occured */ - error: - SAFE_FREE(name); - SAFE_FREE(mode); - ssh_set_error(scp->session,SSH_FATAL,"Parsing error while parsing message: %s",buffer); - return SSH_ERROR; + ssh_set_error(scp->session, SSH_FATAL, + "Unhandled message: (%d)%s", buffer[0], buffer); + return SSH_ERROR; + } + + /* a parsing error occured */ +error: + SAFE_FREE(name); + SAFE_FREE(mode); + ssh_set_error(scp->session, SSH_FATAL, + "Parsing error while parsing message: %s", buffer); + return SSH_ERROR; } /** @@ -641,24 +879,31 @@ int ssh_scp_pull_request(ssh_scp scp){ * @returns SSH_OK if the message was sent, SSH_ERROR if the sending * the message failed, or sending it in a bad state. */ -int ssh_scp_deny_request(ssh_scp scp, const char *reason){ - char buffer[MAX_BUF_SIZE]; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_READ_REQUESTED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state"); - return SSH_ERROR; - } - snprintf(buffer,sizeof(buffer),"%c%s\n",2,reason); - err=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(err==SSH_ERROR) { - return SSH_ERROR; - } - else { - scp->state=SSH_SCP_READ_INITED; - return SSH_OK; - } +int ssh_scp_deny_request(ssh_scp scp, const char *reason) +{ + char buffer[MAX_BUF_SIZE] = {0}; + int rc; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_READ_REQUESTED) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_deny_request called under invalid state"); + return SSH_ERROR; + } + + snprintf(buffer, sizeof(buffer), "%c%s\n", 2, reason); + rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + else { + scp->state = SSH_SCP_READ_INITED; + return SSH_OK; + } } /** @@ -670,24 +915,32 @@ int ssh_scp_deny_request(ssh_scp scp, const char *reason){ * @returns SSH_OK if the message was sent, SSH_ERROR if sending the * message failed, or sending it in a bad state. */ -int ssh_scp_accept_request(ssh_scp scp){ - char buffer[]={0x00}; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_READ_REQUESTED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state"); - return SSH_ERROR; - } - err=ssh_channel_write(scp->channel,buffer,1); - if(err==SSH_ERROR) { - return SSH_ERROR; - } - if(scp->request_type==SSH_SCP_REQUEST_NEWFILE) - scp->state=SSH_SCP_READ_READING; - else - scp->state=SSH_SCP_READ_INITED; - return SSH_OK; +int ssh_scp_accept_request(ssh_scp scp) +{ + char buffer[] = {0x00}; + int rc; + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state != SSH_SCP_READ_REQUESTED) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_deny_request called under invalid state"); + return SSH_ERROR; + } + + rc = ssh_channel_write(scp->channel, buffer, 1); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + if (scp->request_type == SSH_SCP_REQUEST_NEWFILE) { + scp->state = SSH_SCP_READ_READING; + } else { + scp->state = SSH_SCP_READ_INITED; + } + + return SSH_OK; } /** @brief Read from a remote scp file @@ -700,48 +953,64 @@ int ssh_scp_accept_request(ssh_scp scp){ * @returns The nNumber of bytes read, SSH_ERROR if an error occured * while reading. */ -int ssh_scp_read(ssh_scp scp, void *buffer, size_t size){ - int r; - int code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state == SSH_SCP_READ_REQUESTED && scp->request_type == SSH_SCP_REQUEST_NEWFILE){ - r=ssh_scp_accept_request(scp); - if(r==SSH_ERROR) - return r; - } - if(scp->state != SSH_SCP_READ_READING){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_read called under invalid state"); - return SSH_ERROR; - } - if(scp->processed + size > scp->filelen) - size = (size_t) (scp->filelen - scp->processed); - if(size > 65536) - size=65536; /* avoid too large reads */ - r=ssh_channel_read(scp->channel,buffer,size,0); - if(r != SSH_ERROR) - scp->processed += r; - else { - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - /* Check if we arrived at end of file */ - if(scp->processed == scp->filelen) { - scp->processed=scp->filelen=0; - ssh_channel_write(scp->channel,"",1); - code=ssh_scp_response(scp,NULL); - if(code == 0){ - scp->state=SSH_SCP_READ_INITED; - return r; - } - if(code==1){ - scp->state=SSH_SCP_READ_INITED; - return SSH_ERROR; - } - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - return r; +int ssh_scp_read(ssh_scp scp, void *buffer, size_t size) +{ + int rc; + int code; + + if (scp == NULL) { + return SSH_ERROR; + } + + if (scp->state == SSH_SCP_READ_REQUESTED && + scp->request_type == SSH_SCP_REQUEST_NEWFILE) + { + rc = ssh_scp_accept_request(scp); + if (rc == SSH_ERROR) { + return rc; + } + } + + if (scp->state != SSH_SCP_READ_READING) { + ssh_set_error(scp->session, SSH_FATAL, + "ssh_scp_read called under invalid state"); + return SSH_ERROR; + } + + if (scp->processed + size > scp->filelen) { + size = (size_t) (scp->filelen - scp->processed); + } + + if (size > 65536) { + size = 65536; /* avoid too large reads */ + } + + rc = ssh_channel_read(scp->channel, buffer, size, 0); + if (rc != SSH_ERROR) { + scp->processed += rc; + } else { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + /* Check if we arrived at end of file */ + if (scp->processed == scp->filelen) { + scp->processed = scp->filelen = 0; + ssh_channel_write(scp->channel, "", 1); + code = ssh_scp_response(scp, NULL); + if (code == 0) { + scp->state = SSH_SCP_READ_INITED; + return rc; + } + if (code == 1) { + scp->state = SSH_SCP_READ_INITED; + return SSH_ERROR; + } + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + + return rc; } /** @@ -751,10 +1020,13 @@ int ssh_scp_read(ssh_scp scp, void *buffer, size_t size){ * @returns The file name, NULL on error. The string should not be * freed. */ -const char *ssh_scp_request_get_filename(ssh_scp scp){ - if(scp==NULL) - return NULL; - return scp->request_name; +const char *ssh_scp_request_get_filename(ssh_scp scp) +{ + if (scp == NULL) { + return NULL; + } + + return scp->request_name; } /** @@ -763,10 +1035,13 @@ const char *ssh_scp_request_get_filename(ssh_scp scp){ * * @returns The UNIX permission, e.g 0644, -1 on error. */ -int ssh_scp_request_get_permissions(ssh_scp scp){ - if(scp==NULL) - return -1; - return scp->request_mode; +int ssh_scp_request_get_permissions(ssh_scp scp) +{ + if (scp == NULL) { + return -1; + } + + return scp->request_mode; } /** @brief Get the size of the file being pushed from the other party. @@ -776,20 +1051,24 @@ int ssh_scp_request_get_permissions(ssh_scp scp){ * be truncated. * @see ssh_scp_request_get_size64() */ -size_t ssh_scp_request_get_size(ssh_scp scp){ - if(scp==NULL) - return 0; - return (size_t)scp->filelen; +size_t ssh_scp_request_get_size(ssh_scp scp) +{ + if (scp == NULL) { + return 0; + } + return (size_t)scp->filelen; } /** @brief Get the size of the file being pushed from the other party. * * @returns The numeric size of the file being read. */ -uint64_t ssh_scp_request_get_size64(ssh_scp scp){ - if(scp==NULL) - return 0; - return scp->filelen; +uint64_t ssh_scp_request_get_size64(ssh_scp scp) +{ + if (scp == NULL) { + return 0; + } + return scp->filelen; } /** @@ -799,9 +1078,10 @@ uint64_t ssh_scp_request_get_size64(ssh_scp scp){ * * @returns An integer value, e.g. 420 for "0644". */ -int ssh_scp_integer_mode(const char *mode){ - int value=strtoul(mode,NULL,8) & 0xffff; - return value; +int ssh_scp_integer_mode(const char *mode) +{ + int value = strtoul(mode, NULL, 8) & 0xffff; + return value; } /** @@ -812,10 +1092,11 @@ int ssh_scp_integer_mode(const char *mode){ * @returns A pointer to a malloc'ed string containing the scp mode, * e.g. "0644". */ -char *ssh_scp_string_mode(int mode){ - char buffer[16]; - snprintf(buffer,sizeof(buffer),"%.4o",mode); - return strdup(buffer); +char *ssh_scp_string_mode(int mode) +{ + char buffer[16] = {0}; + snprintf(buffer, sizeof(buffer), "%.4o", mode); + return strdup(buffer); } /** @@ -826,10 +1107,13 @@ char *ssh_scp_string_mode(int mode){ * @returns A warning string, or NULL on error. The string should * not be freed. */ -const char *ssh_scp_request_get_warning(ssh_scp scp){ - if(scp==NULL) - return NULL; - return scp->warning; +const char *ssh_scp_request_get_warning(ssh_scp scp) +{ + if (scp == NULL) { + return NULL; + } + + return scp->warning; } /** @} */ diff --git a/src/server.c b/src/server.c index 9256c30..841a1c4 100644 --- a/src/server.c +++ b/src/server.c @@ -166,15 +166,22 @@ int server_set_kex(ssh_session session) return -1; } - for (i = 0; i < 10; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { wanted = session->opts.wanted_methods[i]; - if (wanted == NULL) { + if (wanted == NULL) { if (ssh_fips_mode()) { wanted = ssh_kex_get_fips_methods(i); } else { wanted = ssh_kex_get_default_methods(i); } } + if (wanted == NULL) { + for (j = 0; j < i; j++) { + SAFE_FREE(server->methods[j]); + } + return -1; + } + server->methods[i] = strdup(wanted); if (server->methods[i] == NULL) { for (j = 0; j < i; j++) { @@ -195,7 +202,7 @@ int ssh_server_init_kex(ssh_session session) { } /* free any currently-set methods: server_set_kex will allocate new ones */ - for (i = 0; i < 10 /* SSH_KEX_METHODS */; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { SAFE_FREE(session->next_crypto->server_kex.methods[i]); } @@ -264,7 +271,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ return SSH_PACKET_NOT_USED; } -int ssh_get_key_params(ssh_session session, ssh_key *privkey){ +int +ssh_get_key_params(ssh_session session, + ssh_key *privkey, + enum ssh_digest_e *digest) +{ ssh_key pubkey; ssh_string pubkey_blob; int rc; @@ -290,6 +301,7 @@ int ssh_get_key_params(ssh_session session, ssh_key *privkey){ *privkey = NULL; } + *digest = session->srv.hostkey_digest; rc = ssh_pki_export_privkey_to_pubkey(*privkey, &pubkey); if (rc < 0) { ssh_set_error(session, SSH_FATAL, @@ -306,7 +318,7 @@ int ssh_get_key_params(ssh_session session, ssh_key *privkey){ } rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); if (rc != 0) { ssh_set_error(session, SSH_FATAL, @@ -505,9 +517,11 @@ static int ssh_server_kex_termination(void *s){ return 1; } -void ssh_set_auth_methods(ssh_session session, int auth_methods){ - /* accept only methods in range */ - session->auth.supported_methods = auth_methods & 0x3f; +/* FIXME: auth_methods should be unsigned */ +void ssh_set_auth_methods(ssh_session session, int auth_methods) +{ + /* accept only methods in range */ + session->auth.supported_methods = (uint32_t)auth_methods & 0x3fU; } /* Do the banner and key exchange */ @@ -815,12 +829,17 @@ int ssh_message_auth_kbdint_is_response(ssh_message msg) { return msg->auth_request.kbdint_response != 0; } +/* FIXME: methods should be unsigned */ int ssh_message_auth_set_methods(ssh_message msg, int methods) { if (msg == NULL || msg->session == NULL) { return -1; } - msg->session->auth.supported_methods = methods; + if (methods < 0) { + return -1; + } + + msg->session->auth.supported_methods = (uint32_t)methods; return 0; } @@ -1013,14 +1032,14 @@ int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) { ret = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &pubkey_blob); if (ret < 0) { - ssh_string_free(algo); + SSH_STRING_FREE(algo); return SSH_ERROR; } ret = ssh_message_auth_reply_pk_ok(msg, algo, pubkey_blob); - ssh_string_free(algo); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(algo); + SSH_STRING_FREE(pubkey_blob); return ret; } diff --git a/src/session.c b/src/session.c index 7b5222b..3b3830f 100644 --- a/src/session.c +++ b/src/session.c @@ -56,50 +56,51 @@ * * @returns A new ssh_session pointer, NULL on error. */ -ssh_session ssh_new(void) { - ssh_session session; - char *id = NULL; - int rc; +ssh_session ssh_new(void) +{ + ssh_session session; + char *id = NULL; + int rc; - session = calloc(1, sizeof (struct ssh_session_struct)); - if (session == NULL) { - return NULL; - } + session = calloc(1, sizeof (struct ssh_session_struct)); + if (session == NULL) { + return NULL; + } - session->next_crypto = crypto_new(); - if (session->next_crypto == NULL) { - goto err; - } + session->next_crypto = crypto_new(); + if (session->next_crypto == NULL) { + goto err; + } - session->socket = ssh_socket_new(session); - if (session->socket == NULL) { - goto err; - } + session->socket = ssh_socket_new(session); + if (session->socket == NULL) { + goto err; + } - session->out_buffer = ssh_buffer_new(); - if (session->out_buffer == NULL) { - goto err; - } + session->out_buffer = ssh_buffer_new(); + if (session->out_buffer == NULL) { + goto err; + } - session->in_buffer=ssh_buffer_new(); - if (session->in_buffer == NULL) { - goto err; - } + session->in_buffer = ssh_buffer_new(); + if (session->in_buffer == NULL) { + goto err; + } - session->out_queue = ssh_list_new(); - if (session->out_queue == NULL) { - goto err; - } + session->out_queue = ssh_list_new(); + if (session->out_queue == NULL) { + goto err; + } - session->alive = 0; - session->auth.supported_methods = 0; - ssh_set_blocking(session, 1); - session->maxchannel = FIRST_CHANNEL; + session->alive = 0; + session->auth.supported_methods = 0; + ssh_set_blocking(session, 1); + session->maxchannel = FIRST_CHANNEL; #ifndef _WIN32 session->agent = ssh_agent_new(session); if (session->agent == NULL) { - goto err; + goto err; } #endif /* _WIN32 */ @@ -107,55 +108,70 @@ ssh_session ssh_new(void) { session->opts.StrictHostKeyChecking = 1; session->opts.port = 0; session->opts.fd = -1; - session->opts.compressionlevel=7; + session->opts.compressionlevel = 7; session->opts.nodelay = 0; - session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH | SSH_OPT_FLAG_PUBKEY_AUTH | - SSH_OPT_FLAG_KBDINT_AUTH | SSH_OPT_FLAG_GSSAPI_AUTH; + + session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH | + SSH_OPT_FLAG_PUBKEY_AUTH | + SSH_OPT_FLAG_KBDINT_AUTH | + SSH_OPT_FLAG_GSSAPI_AUTH; + session->opts.identity = ssh_list_new(); if (session->opts.identity == NULL) { - goto err; + goto err; } id = strdup("%d/id_ed25519"); if (id == NULL) { - goto err; + goto err; } + rc = ssh_list_append(session->opts.identity, id); if (rc == SSH_ERROR) { - goto err; + goto err; } #ifdef HAVE_ECC id = strdup("%d/id_ecdsa"); if (id == NULL) { - goto err; + goto err; } rc = ssh_list_append(session->opts.identity, id); if (rc == SSH_ERROR) { - goto err; + goto err; } #endif id = strdup("%d/id_rsa"); if (id == NULL) { - goto err; + goto err; } rc = ssh_list_append(session->opts.identity, id); if (rc == SSH_ERROR) { - goto err; + goto err; } #ifdef HAVE_DSA id = strdup("%d/id_dsa"); if (id == NULL) { - goto err; + goto err; } rc = ssh_list_append(session->opts.identity, id); if (rc == SSH_ERROR) { - goto err; + goto err; } #endif + /* Explicitly initialize states */ + session->session_state = SSH_SESSION_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_NONE; + session->packet_state = PACKET_STATE_INIT; + session->dh_handshake_state = DH_STATE_INIT; + session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE; + + session->auth.state = SSH_AUTH_STATE_NONE; + session->auth.service_state = SSH_AUTH_SERVICE_NONE; + return session; err: @@ -212,15 +228,15 @@ void ssh_free(ssh_session session) ssh_poll_ctx_free(session->default_poll_ctx); } - ssh_buffer_free(session->in_buffer); - ssh_buffer_free(session->out_buffer); + SSH_BUFFER_FREE(session->in_buffer); + SSH_BUFFER_FREE(session->out_buffer); session->in_buffer = session->out_buffer = NULL; if (session->in_hashbuf != NULL) { - ssh_buffer_free(session->in_hashbuf); + SSH_BUFFER_FREE(session->in_hashbuf); } if (session->out_hashbuf != NULL) { - ssh_buffer_free(session->out_hashbuf); + SSH_BUFFER_FREE(session->out_hashbuf); } crypto_free(session->current_crypto); @@ -272,7 +288,7 @@ void ssh_free(ssh_session session) while ((b = ssh_list_pop_head(struct ssh_buffer_struct *, session->out_queue)) != NULL) { - ssh_buffer_free(b); + SSH_BUFFER_FREE(b); } ssh_list_free(session->out_queue); @@ -298,7 +314,7 @@ void ssh_free(ssh_session session) SAFE_FREE(session->opts.gss_client_identity); SAFE_FREE(session->opts.pubkey_accepted_types); - for (i = 0; i < 10; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { if (session->opts.wanted_methods[i]) { SAFE_FREE(session->opts.wanted_methods[i]); } @@ -356,6 +372,8 @@ const char* ssh_get_kex_algo(ssh_session session) { return "diffie-hellman-group1-sha1"; case SSH_KEX_DH_GROUP14_SHA1: return "diffie-hellman-group14-sha1"; + case SSH_KEX_DH_GROUP14_SHA256: + return "diffie-hellman-group14-sha256"; case SSH_KEX_DH_GROUP16_SHA512: return "diffie-hellman-group16-sha512"; case SSH_KEX_DH_GROUP18_SHA512: @@ -1009,7 +1027,7 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) } md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob)); - ssh_string_free(pubkey_blob); + SSH_STRING_FREE(pubkey_blob); md5_final(h, ctx); *hash = h; @@ -1198,7 +1216,7 @@ int ssh_get_publickey_hash(const ssh_key key, *hash = h; rc = 0; out: - ssh_string_free(blob); + SSH_STRING_FREE(blob); return rc; } diff --git a/src/sftp.c b/src/sftp.c index f88ae63..b64aad6 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -44,6 +44,7 @@ #include "libssh/priv.h" #include "libssh/ssh2.h" #include "libssh/sftp.h" +#include "libssh/sftp_priv.h" #include "libssh/buffer.h" #include "libssh/channels.h" #include "libssh/session.h" @@ -57,7 +58,7 @@ #define SFTP_BUFFER_SIZE_MAX 16384 struct sftp_ext_struct { - unsigned int count; + uint32_t count; char **name; char **data; }; @@ -164,7 +165,7 @@ error: } if (sftp->read_packet != NULL) { if (sftp->read_packet->payload != NULL) { - ssh_buffer_free(sftp->read_packet->payload); + SSH_BUFFER_FREE(sftp->read_packet->payload); } SAFE_FREE(sftp->read_packet); } @@ -229,7 +230,7 @@ error: ssh_set_error_oom(session); if (sftp->read_packet != NULL) { if (sftp->read_packet->payload != NULL) { - ssh_buffer_free(sftp->read_packet->payload); + SSH_BUFFER_FREE(sftp->read_packet->payload); } SAFE_FREE(sftp->read_packet); } @@ -262,7 +263,7 @@ int sftp_server_init(sftp_session sftp){ ssh_buffer_get_u32(packet->payload, &version); version = ntohl(version); SSH_LOG(SSH_LOG_PACKET, "Client version: %d", version); - sftp->client_version = version; + sftp->client_version = (int)version; reply = ssh_buffer_new(); if (reply == NULL) { @@ -278,22 +279,22 @@ int sftp_server_init(sftp_session sftp){ "1"); if (rc != SSH_OK) { ssh_set_error_oom(session); - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); return -1; } if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) { - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); return -1; } - ssh_buffer_free(reply); + SSH_BUFFER_FREE(reply); SSH_LOG(SSH_LOG_PROTOCOL, "Server version sent"); if (version > LIBSFTP_VERSION) { sftp->version = LIBSFTP_VERSION; } else { - sftp->version=version; + sftp->version = (int)version; } return 0; @@ -358,11 +359,11 @@ void sftp_free(sftp_session sftp) SAFE_FREE(sftp); } -int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload) +ssize_t sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload) { uint8_t header[5] = {0}; uint32_t payload_size; - int size; + ssize_t size; int rc; /* Add size of type */ @@ -387,7 +388,7 @@ int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload) if ((uint32_t)size != ssh_buffer_get_len(payload)) { SSH_LOG(SSH_LOG_PACKET, - "Had to write %d bytes, wrote only %d", + "Had to write %d bytes, wrote only %zd", ssh_buffer_get_len(payload), size); } @@ -623,12 +624,13 @@ static int sftp_read_and_dispatch(sftp_session sftp) return 0; } -void sftp_packet_free(sftp_packet packet) { +void sftp_packet_free(sftp_packet packet) +{ if (packet == NULL) { return; } - ssh_buffer_free(packet->payload); + SSH_BUFFER_FREE(packet->payload); free(packet); } @@ -651,15 +653,15 @@ int sftp_init(sftp_session sftp) { rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } if (sftp_packet_write(sftp, SSH_FXP_INIT, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); packet = sftp_packet_read(sftp); if (packet == NULL) { @@ -683,7 +685,7 @@ int sftp_init(sftp_session sftp) { version); rc = ssh_buffer_unpack(packet->payload, "s", &ext_name); while (rc == SSH_OK) { - int count = sftp->ext->count; + uint32_t count = sftp->ext->count; char **tmp; rc = ssh_buffer_unpack(packet->payload, "s", &ext_data); @@ -723,7 +725,7 @@ int sftp_init(sftp_session sftp) { rc = ssh_buffer_unpack(packet->payload, "s", &ext_name); } - sftp->version = sftp->server_version = version; + sftp->version = sftp->server_version = (int)version; return 0; @@ -771,7 +773,7 @@ const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) { int sftp_extension_supported(sftp_session sftp, const char *name, const char *data) { - int i, n; + size_t i, n; if (sftp == NULL || name == NULL || data == NULL) { return 0; @@ -1014,13 +1016,13 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path) path); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); + SSH_BUFFER_FREE(payload); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_OPENDIR, payload); - ssh_buffer_free(payload); + SSH_BUFFER_FREE(payload); if (rc < 0) { return NULL; } @@ -1121,7 +1123,7 @@ static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, break; } attr->owner = ssh_string_to_char(owner); - ssh_string_free(owner); + SSH_STRING_FREE(owner); if (attr->owner == NULL) { break; } @@ -1131,7 +1133,7 @@ static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, break; } attr->group = ssh_string_to_char(group); - ssh_string_free(group); + SSH_STRING_FREE(group); if (attr->group == NULL) { break; } @@ -1235,9 +1237,9 @@ static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, if (ok == 0) { /* break issued somewhere */ - ssh_string_free(attr->acl); - ssh_string_free(attr->extended_type); - ssh_string_free(attr->extended_data); + SSH_STRING_FREE(attr->acl); + SSH_STRING_FREE(attr->extended_type); + SSH_STRING_FREE(attr->extended_data); SAFE_FREE(attr->owner); SAFE_FREE(attr->group); SAFE_FREE(attr); @@ -1435,8 +1437,8 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, return attr; error: - ssh_string_free(attr->extended_type); - ssh_string_free(attr->extended_data); + SSH_STRING_FREE(attr->extended_type); + SSH_STRING_FREE(attr->extended_data); SAFE_FREE(attr->name); SAFE_FREE(attr->longname); SAFE_FREE(attr->owner); @@ -1448,8 +1450,8 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, return NULL; } -/* FIXME is this really needed as a public function? */ -int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) { +int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) +{ uint32_t flags = (attr ? attr->flags : 0); int rc; @@ -1494,8 +1496,10 @@ int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) { } -sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf, - int expectname) { +sftp_attributes sftp_parse_attr(sftp_session session, + ssh_buffer buf, + int expectname) +{ switch(session->version) { case 4: return sftp_parse_attr_4(session, buf, expectname); @@ -1545,12 +1549,12 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) if (rc != 0) { ssh_set_error_oom(sftp->session); sftp_set_error(sftp, SSH_FX_FAILURE); - ssh_buffer_free(payload); + SSH_BUFFER_FREE(payload); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_READDIR, payload); - ssh_buffer_free(payload); + SSH_BUFFER_FREE(payload); if (rc < 0) { return NULL; } @@ -1624,7 +1628,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) dir->count--; if (dir->count == 0) { - ssh_buffer_free(dir->buffer); + SSH_BUFFER_FREE(dir->buffer); dir->buffer = NULL; } @@ -1642,9 +1646,9 @@ void sftp_attributes_free(sftp_attributes file){ return; } - ssh_string_free(file->acl); - ssh_string_free(file->extended_data); - ssh_string_free(file->extended_type); + SSH_STRING_FREE(file->acl); + SSH_STRING_FREE(file->extended_data); + SSH_STRING_FREE(file->extended_type); SAFE_FREE(file->name); SAFE_FREE(file->longname); @@ -1677,13 +1681,13 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) handle); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } rc = sftp_packet_write(sftp, SSH_FXP_CLOSE, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return -1; } @@ -1733,7 +1737,7 @@ int sftp_close(sftp_file file){ SAFE_FREE(file->name); if (file->handle){ err = sftp_handle_close(file->sftp,file->handle); - ssh_string_free(file->handle); + SSH_STRING_FREE(file->handle); } /* FIXME: check server response and implement errno */ SAFE_FREE(file); @@ -1748,10 +1752,10 @@ int sftp_closedir(sftp_dir dir){ SAFE_FREE(dir->name); if (dir->handle) { err = sftp_handle_close(dir->sftp, dir->handle); - ssh_string_free(dir->handle); + SSH_STRING_FREE(dir->handle); } /* FIXME: check server response and implement errno */ - ssh_buffer_free(dir->buffer); + SSH_BUFFER_FREE(dir->buffer); SAFE_FREE(dir); return err; @@ -1809,7 +1813,7 @@ sftp_file sftp_open(sftp_session sftp, sftp_flags); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } @@ -1817,13 +1821,13 @@ sftp_file sftp_open(sftp_session sftp, rc = buffer_add_attributes(buffer, &attr); if (rc < 0) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_OPEN, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } @@ -1851,6 +1855,9 @@ sftp_file sftp_open(sftp_session sftp, return NULL; case SSH_FXP_HANDLE: handle = parse_handle_msg(msg); + if (handle == NULL) { + return NULL; + } sftp_message_free(msg); if ((flags & O_APPEND) == O_APPEND) { stat_data = sftp_stat(sftp, file); @@ -1895,7 +1902,7 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { ssh_string datastring; size_t datalen; ssh_buffer buffer; - int id; + uint32_t id; int rc; if (handle->eof) { @@ -1918,15 +1925,15 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { count); if (rc != SSH_OK){ ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } if (sftp_packet_write(handle->sftp, SSH_FXP_READ, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); while (msg == NULL) { if (handle->nonblocking) { @@ -1977,12 +1984,12 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { "Received a too big DATA packet from sftp server: " "%" PRIdS " and asked for %" PRIdS, datalen, count); - ssh_string_free(datastring); + SSH_STRING_FREE(datastring); return -1; } handle->offset += (uint64_t)datalen; memcpy(buf, ssh_string_data(datastring), datalen); - ssh_string_free(datastring); + SSH_STRING_FREE(datastring); return datalen; default: ssh_set_error(sftp->session, SSH_FATAL, @@ -2019,15 +2026,15 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ len); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } if (sftp_packet_write(sftp, SSH_FXP_READ, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); file->offset += len; /* assume we'll read len bytes */ @@ -2099,14 +2106,14 @@ int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){ "Received a too big DATA packet from sftp server: " "%" PRIdS " and asked for %u", ssh_string_len(datastring), size); - ssh_string_free(datastring); + SSH_STRING_FREE(datastring); return SSH_ERROR; } len = ssh_string_len(datastring); /* Update the offset with the correct value */ file->offset = file->offset - (size - len); memcpy(data, ssh_string_data(datastring), len); - ssh_string_free(datastring); + SSH_STRING_FREE(datastring); return len; default: ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type); @@ -2124,8 +2131,8 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { sftp_status_message status; ssh_buffer buffer; uint32_t id; - int len; - int packetlen; + ssize_t len; + size_t packetlen; int rc; buffer = ssh_buffer_new(); @@ -2146,16 +2153,16 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { (size_t)count, buf); if (rc != SSH_OK){ ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } packetlen=ssh_buffer_get_len(buffer); len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (len < 0) { return -1; - } else if (len != packetlen) { + } else if ((size_t)len != packetlen) { SSH_LOG(SSH_LOG_PACKET, "Could not write as much data as expected"); } @@ -2261,16 +2268,16 @@ int sftp_unlink(sftp_session sftp, const char *file) { file); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } if (sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); while (msg == NULL) { if (sftp_read_and_dispatch(sftp)) { @@ -2336,15 +2343,15 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { directory); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); while (msg == NULL) { if (sftp_read_and_dispatch(sftp) < 0) { @@ -2413,7 +2420,7 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) directory); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } @@ -2421,13 +2428,13 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) rc = buffer_add_attributes(buffer, &attr); if (rc < 0) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } rc = sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return -1; } @@ -2509,7 +2516,7 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { newname); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } @@ -2521,10 +2528,10 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { } if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); while (msg == NULL) { if (sftp_read_and_dispatch(sftp) < 0) { @@ -2592,7 +2599,7 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) file); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } @@ -2600,13 +2607,13 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) rc = buffer_add_attributes(buffer, attr); if (rc != 0) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } rc = sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return -1; } @@ -2732,16 +2739,16 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { } if (rc != SSH_OK){ ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } if (sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) { - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return -1; } - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); while (msg == NULL) { if (sftp_read_and_dispatch(sftp) < 0) { @@ -2820,13 +2827,13 @@ char *sftp_readlink(sftp_session sftp, const char *path) path); if (rc < 0) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_READLINK, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } @@ -2947,13 +2954,13 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) path); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } @@ -3087,7 +3094,7 @@ int sftp_fsync(sftp_file file) rc = -1; done: - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); return rc; } @@ -3122,13 +3129,13 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) file->handle); if (rc < 0) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } @@ -3208,13 +3215,13 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) path); if (rc < 0) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } @@ -3299,13 +3306,13 @@ static sftp_attributes sftp_xstat(sftp_session sftp, path); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(sftp, param, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } @@ -3377,13 +3384,13 @@ sftp_attributes sftp_fstat(sftp_file file) file->handle); if (rc != SSH_OK) { ssh_set_error_oom(file->sftp->session); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); sftp_set_error(file->sftp, SSH_FX_FAILURE); return NULL; } rc = sftp_packet_write(file->sftp, SSH_FXP_FSTAT, buffer); - ssh_buffer_free(buffer); + SSH_BUFFER_FREE(buffer); if (rc < 0) { return NULL; } diff --git a/src/sftpserver.c b/src/sftpserver.c index 1717aa4..5a2110e 100644 --- a/src/sftpserver.c +++ b/src/sftpserver.c @@ -32,11 +32,14 @@ #include "libssh/libssh.h" #include "libssh/sftp.h" +#include "libssh/sftp_priv.h" #include "libssh/ssh2.h" #include "libssh/priv.h" #include "libssh/buffer.h" #include "libssh/misc.h" +#define SFTP_HANDLES 256 + sftp_client_message sftp_get_client_message(sftp_session sftp) { ssh_session session = sftp->session; sftp_packet packet; @@ -274,10 +277,10 @@ void sftp_client_message_free(sftp_client_message msg) { SAFE_FREE(msg->filename); SAFE_FREE(msg->submessage); - ssh_string_free(msg->data); - ssh_string_free(msg->handle); + SSH_STRING_FREE(msg->data); + SSH_STRING_FREE(msg->handle); sftp_attributes_free(msg->attr); - ssh_buffer_free(msg->complete_message); + SSH_BUFFER_FREE(msg->complete_message); SAFE_FREE(msg->str_data); ZERO_STRUCTP(msg); SAFE_FREE(msg); @@ -295,7 +298,7 @@ int sftp_reply_name(sftp_client_message msg, const char *name, file = ssh_string_from_char(name); if (file == NULL) { - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return -1; } @@ -305,12 +308,12 @@ int sftp_reply_name(sftp_client_message msg, const char *name, ssh_buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */ buffer_add_attributes(out, attr) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { - ssh_buffer_free(out); - ssh_string_free(file); + SSH_BUFFER_FREE(out); + SSH_STRING_FREE(file); return -1; } - ssh_buffer_free(out); - ssh_string_free(file); + SSH_BUFFER_FREE(out); + SSH_STRING_FREE(file); return 0; } @@ -326,10 +329,10 @@ int sftp_reply_handle(sftp_client_message msg, ssh_string handle){ if (ssh_buffer_add_u32(out, msg->id) < 0 || ssh_buffer_add_ssh_string(out, handle) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) { - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return -1; } - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return 0; } @@ -345,10 +348,10 @@ int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) { if (ssh_buffer_add_u32(out, msg->id) < 0 || buffer_add_attributes(out, attr) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) { - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return -1; } - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return 0; } @@ -365,27 +368,27 @@ int sftp_reply_names_add(sftp_client_message msg, const char *file, if (msg->attrbuf == NULL) { msg->attrbuf = ssh_buffer_new(); if (msg->attrbuf == NULL) { - ssh_string_free(name); + SSH_STRING_FREE(name); return -1; } } if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0) { - ssh_string_free(name); + SSH_STRING_FREE(name); return -1; } - ssh_string_free(name); + SSH_STRING_FREE(name); name = ssh_string_from_char(longname); if (name == NULL) { return -1; } if (ssh_buffer_add_ssh_string(msg->attrbuf,name) < 0 || buffer_add_attributes(msg->attrbuf,attr) < 0) { - ssh_string_free(name); + SSH_STRING_FREE(name); return -1; } - ssh_string_free(name); + SSH_STRING_FREE(name); msg->attr_num++; return 0; @@ -396,7 +399,7 @@ int sftp_reply_names(sftp_client_message msg) { out = ssh_buffer_new(); if (out == NULL) { - ssh_buffer_free(msg->attrbuf); + SSH_BUFFER_FREE(msg->attrbuf); return -1; } @@ -405,13 +408,13 @@ int sftp_reply_names(sftp_client_message msg) { ssh_buffer_add_data(out, ssh_buffer_get(msg->attrbuf), ssh_buffer_get_len(msg->attrbuf)) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { - ssh_buffer_free(out); - ssh_buffer_free(msg->attrbuf); + SSH_BUFFER_FREE(out); + SSH_BUFFER_FREE(msg->attrbuf); return -1; } - ssh_buffer_free(out); - ssh_buffer_free(msg->attrbuf); + SSH_BUFFER_FREE(out); + SSH_BUFFER_FREE(msg->attrbuf); msg->attr_num = 0; msg->attrbuf = NULL; @@ -431,7 +434,7 @@ int sftp_reply_status(sftp_client_message msg, uint32_t status, s = ssh_string_from_char(message ? message : ""); if (s == NULL) { - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return -1; } @@ -440,13 +443,13 @@ int sftp_reply_status(sftp_client_message msg, uint32_t status, ssh_buffer_add_ssh_string(out, s) < 0 || ssh_buffer_add_u32(out, 0) < 0 || /* language string */ sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) { - ssh_buffer_free(out); - ssh_string_free(s); + SSH_BUFFER_FREE(out); + SSH_STRING_FREE(s); return -1; } - ssh_buffer_free(out); - ssh_string_free(s); + SSH_BUFFER_FREE(out); + SSH_STRING_FREE(s); return 0; } @@ -463,10 +466,10 @@ int sftp_reply_data(sftp_client_message msg, const void *data, int len) { ssh_buffer_add_u32(out, ntohl(len)) < 0 || ssh_buffer_add_data(out, data, len) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) { - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return -1; } - ssh_buffer_free(out); + SSH_BUFFER_FREE(out); return 0; } @@ -480,7 +483,7 @@ int sftp_reply_data(sftp_client_message msg, const void *data, int len) { ssh_string sftp_handle_alloc(sftp_session sftp, void *info) { ssh_string ret; uint32_t val; - int i; + uint32_t i; if (sftp->handles == NULL) { sftp->handles = calloc(SFTP_HANDLES, sizeof(void *)); diff --git a/src/socket.c b/src/socket.c index bbba443..2fef8e7 100644 --- a/src/socket.c +++ b/src/socket.c @@ -42,8 +42,10 @@ #else /* _WIN32 */ #include #include +#include #include #include +#include #endif /* _WIN32 */ #include "libssh/priv.h" @@ -87,6 +89,9 @@ struct ssh_socket_struct { ssh_session session; ssh_socket_callbacks callbacks; ssh_poll_handle poll_handle; +#ifndef _WIN32 + pid_t proxy_pid; +#endif }; static int sockets_initialized = 0; @@ -162,7 +167,7 @@ ssh_socket ssh_socket_new(ssh_session session) s->out_buffer=ssh_buffer_new(); if (s->out_buffer == NULL) { ssh_set_error_oom(session); - ssh_buffer_free(s->in_buffer); + SSH_BUFFER_FREE(s->in_buffer); SAFE_FREE(s); return NULL; } @@ -191,6 +196,9 @@ void ssh_socket_reset(ssh_socket s) s->data_except = 0; s->poll_handle = NULL; s->state=SSH_SOCKET_NONE; +#ifndef _WIN32 + s->proxy_pid = 0; +#endif } /** @@ -393,8 +401,8 @@ void ssh_socket_free(ssh_socket s) return; } ssh_socket_close(s); - ssh_buffer_free(s->in_buffer); - ssh_buffer_free(s->out_buffer); + SSH_BUFFER_FREE(s->in_buffer); + SSH_BUFFER_FREE(s->out_buffer); SAFE_FREE(s); } @@ -454,6 +462,28 @@ void ssh_socket_close(ssh_socket s) } s->state = SSH_SOCKET_CLOSED; + +#ifndef _WIN32 + /* If the proxy command still runs try to kill it */ + if (s->proxy_pid != 0) { + int status; + pid_t pid = s->proxy_pid; + + s->proxy_pid = 0; + kill(pid, SIGTERM); + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s", strerror(errno)); + return; + } + } + if (!WIFEXITED(status)) { + SSH_LOG(SSH_LOG_WARN, "Proxy command exitted abnormally"); + return; + } + SSH_LOG(SSH_LOG_TRACE, "Proxy command returned %d", WEXITSTATUS(status)); + } +#endif } /** @@ -806,9 +836,10 @@ int ssh_socket_set_blocking(socket_t fd) * @bug It only tries connecting to one of the available AI's * which is problematic for hosts having DNS fail-over. */ - -int -ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr) +int ssh_socket_connect(ssh_socket s, + const char *host, + uint16_t port, + const char *bind_addr) { socket_t fd; @@ -842,7 +873,7 @@ ssh_execute_command(const char *command, socket_t in, socket_t out) /* Prepare /dev/null socket for the stderr redirection */ int devnull = open("/dev/null", O_WRONLY); if (devnull == -1) { - SSH_LOG(SSH_LOG_WARNING, "Failed to open stderr"); + SSH_LOG(SSH_LOG_WARNING, "Failed to open /dev/null"); exit(1); } @@ -885,9 +916,11 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command) SSH_LOG(SSH_LOG_PROTOCOL, "Executing proxycommand '%s'", command); pid = fork(); - if(pid == 0) { + if (pid == 0) { ssh_execute_command(command, pair[0], pair[0]); + /* Does not return */ } + s->proxy_pid = pid; close(pair[0]); SSH_LOG(SSH_LOG_PROTOCOL, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]); ssh_socket_set_fd(s, pair[1]); diff --git a/src/token.c b/src/token.c index aee235a..0924d3b 100644 --- a/src/token.c +++ b/src/token.c @@ -26,6 +26,7 @@ #include #include +#include #include "libssh/priv.h" #include "libssh/token.h" @@ -175,7 +176,7 @@ char *ssh_find_matching(const char *available_list, for (i = 0; p_tok->tokens[i]; i++) { for (j = 0; a_tok->tokens[j]; j++) { - if (strcmp(a_tok->tokens[j], p_tok->tokens[i]) == 0){ + if (strcmp(a_tok->tokens[j], p_tok->tokens[i]) == 0) { ret = strdup(a_tok->tokens[j]); goto out; } @@ -260,3 +261,152 @@ out: ssh_tokens_free(p_tok); return ret; } + +/** + * @internal + * + * @brief Given a string containing a list of elements, remove all duplicates + * and return in a newly allocated string. + * + * @param[in] list The list to be freed of duplicates + * + * @return A newly allocated copy of the string free of duplicates; NULL in + * case of error. + */ +char *ssh_remove_duplicates(const char *list) +{ + struct ssh_tokens_st *tok = NULL; + + size_t i, j, num_tokens, max_len; + char *ret = NULL; + bool *should_copy = NULL, need_comma = false; + + if (list == NULL) { + return NULL; + } + + /* The maximum number of tokens is the size of the list */ + max_len = strlen(list); + if (max_len == 0) { + return NULL; + } + + /* Add space for ending '\0' */ + max_len++; + + tok = ssh_tokenize(list, ','); + if ((tok == NULL) || (tok->tokens == NULL) || (tok->tokens[0] == NULL)) { + goto out; + } + + should_copy = calloc(1, max_len); + if (should_copy == NULL) { + goto out; + } + + if (strlen(tok->tokens[0]) > 0) { + should_copy[0] = true; + } + + for (i = 1; tok->tokens[i]; i++) { + for (j = 0; j < i; j++) { + if (strcmp(tok->tokens[i], tok->tokens[j]) == 0) { + /* Found a duplicate; do not copy */ + should_copy[i] = false; + break; + } + } + + /* No matching token before */ + if (j == i) { + /* Only copy if it is not an empty string */ + if (strlen(tok->tokens[i]) > 0) { + should_copy[i] = true; + } else { + should_copy[i] = false; + } + } + } + + num_tokens = i; + + ret = calloc(1, max_len); + if (ret == NULL) { + goto out; + } + + for (i = 0; i < num_tokens; i++) { + if (should_copy[i]) { + if (need_comma) { + strncat(ret, ",", (max_len - strlen(ret) - 1)); + } + strncat(ret, tok->tokens[i], (max_len - strlen(ret) - 1)); + need_comma = true; + } + } + + /* If no comma is needed, nothing was copied */ + if (!need_comma) { + SAFE_FREE(ret); + } + +out: + SAFE_FREE(should_copy); + ssh_tokens_free(tok); + return ret; +} + +/** + * @internal + * + * @brief Given two strings containing lists of tokens, return a newly + * allocated string containing all the elements of the first list appended with + * all the elements of the second list, without duplicates. The order of the + * elements will be preserved. + * + * @param[in] list The first list + * @param[in] appended_list The list to be appended + * + * @return A newly allocated copy list containing all the elements of the + * kept_list appended with the elements of the appended_list without duplicates; + * NULL in case of error. + */ +char *ssh_append_without_duplicates(const char *list, + const char *appended_list) +{ + size_t concat_len = 0; + char *ret = NULL, *concat = NULL; + + if (list != NULL) { + concat_len = strlen(list); + } + + if (appended_list != NULL) { + concat_len += strlen(appended_list); + } + + if (concat_len == 0) { + return NULL; + } + + /* Add room for ending '\0' and for middle ',' */ + concat_len += 2; + concat = calloc(1, concat_len); + if (concat == NULL) { + return NULL; + } + + if (list != NULL) { + strcpy(concat, list); + strncat(concat, ",", concat_len - strlen(concat) - 1); + } + if (appended_list != NULL) { + strncat(concat, appended_list, concat_len - strlen(concat) - 1); + } + + ret = ssh_remove_duplicates(concat); + + SAFE_FREE(concat); + + return ret; +} diff --git a/src/wrapper.c b/src/wrapper.c index 299d4f3..7e57ab5 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -539,11 +539,13 @@ int crypt_set_algorithms_server(ssh_session session){ method = session->next_crypto->kex_methods[SSH_HOSTKEYS]; session->srv.hostkey = ssh_key_type_from_signature_name(method); + session->srv.hostkey_digest = ssh_key_hash_from_name(method); /* setup DH key exchange type */ switch (session->next_crypto->kex_type) { case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP14_SHA1: + case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP18_SHA512: ssh_server_dh_init(session); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index aeb0b17..294038e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,20 +6,16 @@ endif (BSD OR SOLARIS OR OSX) set(TORTURE_LIBRARY torture) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${OPENSSL_INCLUDE_DIR} - ${CMOCKA_INCLUDE_DIR} - ${ZLIB_INCLUDE_DIR} - ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR} -) +include_directories(${OPENSSL_INCLUDE_DIR} + ${CMOCKA_INCLUDE_DIR} + ${ZLIB_INCLUDE_DIR} + ${libssh_BINARY_DIR} + ${libssh_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}) set(TORTURE_LINK_LIBRARIES ${CMOCKA_LIBRARY} - ${LIBSSH_STATIC_LIBRARY} - ${LIBSSH_LINK_LIBRARIES}) + ssh::static) # create test library add_library(${TORTURE_LIBRARY} @@ -47,7 +43,7 @@ set(TEST_TARGET_LIBRARIES add_subdirectory(unittests) -if (CLIENT_TESTING) +if (CLIENT_TESTING OR SERVER_TESTING) find_package(socket_wrapper 1.1.5 REQUIRED) find_package(nss_wrapper 1.1.2 REQUIRED) find_package(uid_wrapper 1.2.0 REQUIRED) @@ -92,7 +88,7 @@ if (CLIENT_TESTING) # ssh_ping add_executable(ssh_ping ssh_ping.c) target_compile_options(ssh_ping PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) - target_link_libraries(ssh_ping ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(ssh_ping ssh::ssh) # homedir will be used in passwd set(HOMEDIR ${CMAKE_CURRENT_BINARY_DIR}/home) @@ -123,7 +119,7 @@ if (CLIENT_TESTING) file(COPY keys/id_ed25519 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE) file(COPY keys/id_ed25519.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE) - # Allow to auth with bob his public keys on alice account + # Allow to auth with bob's public keys on alice account configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/alice/.ssh/authorized_keys @ONLY) # append ECDSA public key file(READ keys/id_ecdsa.pub CONTENTS) @@ -139,18 +135,20 @@ if (CLIENT_TESTING) file(COPY keys/certauth/id_rsa-cert.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE) message(STATUS "TORTURE_ENVIRONMENT=${TORTURE_ENVIRONMENT}") - - add_subdirectory(client) -endif (CLIENT_TESTING) +endif () if (WITH_BENCHMARKS) add_subdirectory(benchmarks) -endif (WITH_BENCHMARKS) +endif () + +if (CLIENT_TESTING) + add_subdirectory(client) +endif () if (WITH_SERVER AND SERVER_TESTING) add_subdirectory(pkd) add_subdirectory(server) -endif (WITH_SERVER AND SERVER_TESTING) +endif () if (FUZZ_TESTING) add_subdirectory(fuzz) diff --git a/tests/benchmarks/CMakeLists.txt b/tests/benchmarks/CMakeLists.txt index 8dc8552..dbaa4d5 100644 --- a/tests/benchmarks/CMakeLists.txt +++ b/tests/benchmarks/CMakeLists.txt @@ -4,18 +4,8 @@ set(benchmarks_SRCS bench_scp.c bench_sftp bench_raw.c benchmarks.c latency.c ) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} -) +include_directories(${libssh_BINARY_DIR}) add_executable(benchmarks ${benchmarks_SRCS}) -target_link_libraries(benchmarks ${LIBSSH_SHARED_LIBRARY}) - -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} -) - -if (WITH_SFTP) -endif (WITH_SFTP) +target_link_libraries(benchmarks ssh::ssh) diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt index 0998e4a..70b5de3 100644 --- a/tests/client/CMakeLists.txt +++ b/tests/client/CMakeLists.txt @@ -17,11 +17,19 @@ set(LIBSSH_CLIENT_TESTS torture_request_env torture_client_global_requests) +find_program(SCP_EXECUTABLE NAMES scp) +if (SCP_EXECUTABLE) + set(LIBSSH_CLIENT_TESTS + ${LIBSSH_CLIENT_TESTS} + torture_scp) +endif() + if (DEFAULT_C_NO_DEPRECATION_FLAGS) set_source_files_properties(torture_knownhosts.c PROPERTIES COMPILE_FLAGS ${DEFAULT_C_NO_DEPRECATION_FLAGS}) endif() + if (WITH_SFTP) if (WITH_BENCHMARKS) set(SFTP_BENCHMARK_TESTS diff --git a/tests/client/torture_algorithms.c b/tests/client/torture_algorithms.c index 247ed29..19f25db 100644 --- a/tests/client/torture_algorithms.c +++ b/tests/client/torture_algorithms.c @@ -691,6 +691,16 @@ static void torture_algorithms_dh_group14(void **state) { test_algorithm(s->ssh.session, "diffie-hellman-group14-sha1", NULL/*cipher*/, NULL/*hmac*/); } +static void torture_algorithms_dh_group14_sha256(void **state) { + struct torture_state *s = *state; + + if (ssh_fips_mode()) { + skip(); + } + + test_algorithm(s->ssh.session, "diffie-hellman-group14-sha256", NULL/*cipher*/, NULL/*hmac*/); +} + static void torture_algorithms_dh_group16(void **state) { struct torture_state *s = *state; @@ -901,6 +911,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_algorithms_dh_group14, session_setup, session_teardown), + cmocka_unit_test_setup_teardown(torture_algorithms_dh_group14_sha256, + session_setup, + session_teardown), cmocka_unit_test_setup_teardown(torture_algorithms_dh_group16, session_setup, session_teardown), diff --git a/tests/client/torture_knownhosts.c b/tests/client/torture_knownhosts.c index d075998..fcc5484 100644 --- a/tests/client/torture_knownhosts.c +++ b/tests/client/torture_knownhosts.c @@ -33,7 +33,7 @@ #include "session.c" #include "known_hosts.c" -#define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts" +#define TMP_FILE_TEMPLATE "known_hosts_XXXXXX" #define BADRSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQChm5" \ "a6Av65O8cKtx5YXOnui3wJnYE6A6J/I4kZSAibbn14Jcl+34VJQwv96f25AxNmo" \ @@ -62,6 +62,7 @@ static int session_setup(void **state) struct torture_state *s = *state; int verbosity = torture_libssh_verbosity(); struct passwd *pwd; + bool process_config = false; int rc; pwd = getpwnam("bob"); @@ -74,8 +75,9 @@ static int session_setup(void **state) assert_non_null(s->ssh.session); ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, + &process_config); ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); - ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE); return 0; @@ -84,19 +86,10 @@ static int session_setup(void **state) static int session_teardown(void **state) { struct torture_state *s = *state; - char known_hosts_file[1024]; - - snprintf(known_hosts_file, - sizeof(known_hosts_file), - "%s/%s", - s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); ssh_disconnect(s->ssh.session); ssh_free(s->ssh.session); - unlink(known_hosts_file); - return 0; } @@ -104,17 +97,22 @@ static int session_teardown(void **state) static void torture_knownhosts_port(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; char buffer[200]; char *p; FILE *file; int rc; + bool process_config = false; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); @@ -140,8 +138,10 @@ static void torture_knownhosts_port(void **state) { /* Now, connect back to the ssh server and verify the known host line */ s->ssh.session = session = ssh_new(); + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); + free(known_hosts_file); rc = ssh_connect(session); assert_ssh_return_code(session, rc); @@ -155,16 +155,20 @@ static void torture_knownhosts_wildcard(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; const char *key = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); file = fopen(known_hosts_file, "w"); assert_non_null(file); @@ -176,6 +180,7 @@ static void torture_knownhosts_wildcard(void **state) assert_ssh_return_code(session, rc); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); + free(known_hosts_file); rc = ssh_connect(session); assert_ssh_return_code(session, rc); @@ -188,16 +193,20 @@ static void torture_knownhosts_standard_port(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; const char *key = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); file = fopen(known_hosts_file, "w"); assert_non_null(file); @@ -209,6 +218,7 @@ static void torture_knownhosts_standard_port(void **state) assert_ssh_return_code(session, rc); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); + free(known_hosts_file); rc = ssh_connect(session); assert_ssh_return_code(session, rc); @@ -220,15 +230,19 @@ static void torture_knownhosts_standard_port(void **state) static void torture_knownhosts_fail(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); @@ -238,6 +252,7 @@ static void torture_knownhosts_fail(void **state) { file = fopen(known_hosts_file, "w"); assert_non_null(file); + free(known_hosts_file); fprintf(file, "127.0.0.10 ssh-rsa %s\n", BADRSA); fclose(file); @@ -252,15 +267,19 @@ static void torture_knownhosts_fail(void **state) { static void torture_knownhosts_other(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); @@ -270,6 +289,8 @@ static void torture_knownhosts_other(void **state) { file = fopen(known_hosts_file, "w"); assert_non_null(file); + free(known_hosts_file); + fprintf(file, "127.0.0.10 ssh-rsa %s\n", BADRSA); fclose(file); @@ -283,14 +304,18 @@ static void torture_knownhosts_other(void **state) { static void torture_knownhosts_other_auto(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); assert_ssh_return_code(session, rc); @@ -333,20 +358,25 @@ static void torture_knownhosts_other_auto(void **state) { assert_int_equal(rc, SSH_SERVER_KNOWN_OK); /* session will be freed by session_teardown() */ + free(known_hosts_file); } static void torture_knownhosts_conflict(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024]; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); assert_ssh_return_code(session, rc); @@ -393,6 +423,7 @@ static void torture_knownhosts_conflict(void **state) { assert_int_equal(rc, SSH_SERVER_KNOWN_OK); /* session will be freed by session_teardown() */ + free(known_hosts_file); } static void torture_knownhosts_no_hostkeychecking(void **state) @@ -400,19 +431,24 @@ static void torture_knownhosts_no_hostkeychecking(void **state) struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024] = {0}; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; enum ssh_known_hosts_e found; int strict_host_key_checking = 0; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); + free(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ecdsa-sha2-nistp521"); assert_ssh_return_code(session, rc); diff --git a/tests/client/torture_knownhosts_verify.c b/tests/client/torture_knownhosts_verify.c index df8363e..8596334 100644 --- a/tests/client/torture_knownhosts_verify.c +++ b/tests/client/torture_knownhosts_verify.c @@ -31,9 +31,17 @@ #include "knownhosts.c" -#define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts" +#define TMP_FILE_TEMPLATE "known_hosts_XXXXXX" -#define BAD_RSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQDXvXuawzaArEwkLIXTz/EWywLOCtqQL3P9yKkrhz6AplXP2PhOh5pyxa1VfGKe453jNeYBJ0ROto3BshXgZXbo86oLXTkbe0gO5xi3r5WjXxjOFvRRTLot5fPLNDOv9+TnsPmkNn0iIeyPnfrcPIyjWt5zSWUfkNC8oNHxsiSshjpbJvTXSDipukpUy41d7jg4uWGuonMTF7yu7HfuHqq7lhb0WlwSpfbqAbfYARBddcdcARyhix4RMWZZqVY20H3Vsjq8bjKC+NJXFce1PRg+qcOWQdlXEei4dkzAvHvfQRx1TjzkrBZ6B6thmZtyeb9IsiB0tg2g0JN2VTAGkxqp" +#define BAD_RSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQDXvXuawzaArEwkLIXTz/EWywLOC" \ + "tqQL3P9yKkrhz6AplXP2PhOh5pyxa1VfGKe453jNeYBJ0ROto3BshXgZX" \ + "bo86oLXTkbe0gO5xi3r5WjXxjOFvRRTLot5fPLNDOv9+TnsPmkNn0iIey" \ + "PnfrcPIyjWt5zSWUfkNC8oNHxsiSshjpbJvTXSDipukpUy41d7jg4uWGu" \ + "onMTF7yu7HfuHqq7lhb0WlwSpfbqAbfYARBddcdcARyhix4RMWZZqVY20" \ + "H3Vsjq8bjKC+NJXFce1PRg+qcOWQdlXEei4dkzAvHvfQRx1TjzkrBZ6B6" \ + "thmZtyeb9IsiB0tg2g0JN2VTAGkxqp" + +const char template[] = "temp_dir_XXXXXX"; static int sshd_group_setup(void **state) { @@ -55,6 +63,8 @@ static int session_setup(void **state) struct passwd *pwd; int rc; + bool process_config = false; + pwd = getpwnam("bob"); assert_non_null(pwd); @@ -67,6 +77,10 @@ static int session_setup(void **state) rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); assert_ssh_return_code(s->ssh.session, rc); + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, + &process_config); + assert_ssh_return_code(s->ssh.session, rc); + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); assert_ssh_return_code(s->ssh.session, rc); @@ -81,19 +95,9 @@ static int session_setup(void **state) static int session_teardown(void **state) { struct torture_state *s = *state; - char known_hosts_file[1024]; - - snprintf(known_hosts_file, - sizeof(known_hosts_file), - "%s/%s", - s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); - ssh_disconnect(s->ssh.session); ssh_free(s->ssh.session); - unlink(known_hosts_file); - return 0; } @@ -147,15 +151,19 @@ static void torture_knownhosts_precheck(void **state) struct ssh_iterator *it = NULL; size_t algo_count; const char *algo = NULL; - char known_hosts_file[1024] = {0}; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); file = fopen(known_hosts_file, "w"); assert_non_null(file); @@ -175,6 +183,7 @@ static void torture_knownhosts_precheck(void **state) rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); + free(known_hosts_file); algo_list = ssh_known_hosts_get_algorithms(session); assert_non_null(algo_list); @@ -212,15 +221,19 @@ static void torture_knownhosts_duplicate(void **state) struct ssh_iterator *it = NULL; size_t algo_count; const char *algo = NULL; - char known_hosts_file[1024] = {0}; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; FILE *file; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); file = fopen(known_hosts_file, "w"); assert_non_null(file); @@ -240,6 +253,7 @@ static void torture_knownhosts_duplicate(void **state) rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); + free(known_hosts_file); algo_list = ssh_known_hosts_get_algorithms(session); assert_non_null(algo_list); @@ -259,16 +273,20 @@ static void torture_knownhosts_other(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024] = {0}; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; enum ssh_known_hosts_e found; FILE *file = NULL; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); @@ -282,6 +300,7 @@ static void torture_knownhosts_other(void **state) "127.0.0.10 %s\n", torture_get_testkey_pub(SSH_KEYTYPE_RSA)); fclose(file); + free(known_hosts_file); rc = ssh_connect(session); assert_ssh_return_code(session, rc); @@ -294,15 +313,19 @@ static void torture_knownhosts_unknown(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024] = {0}; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; enum ssh_known_hosts_e found; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); @@ -347,22 +370,27 @@ static void torture_knownhosts_unknown(void **state) assert_int_equal(found, SSH_KNOWN_HOSTS_OK); /* session will be freed by session_teardown() */ + free(known_hosts_file); } static void torture_knownhosts_conflict(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; - char known_hosts_file[1024] = {0}; + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; enum ssh_known_hosts_e found; FILE *file = NULL; int rc; - snprintf(known_hosts_file, - sizeof(known_hosts_file), + snprintf(tmp_file, + sizeof(tmp_file), "%s/%s", s->socket_dir, - TORTURE_KNOWN_HOSTS_FILE); + TMP_FILE_TEMPLATE); + + known_hosts_file = torture_create_temp_file(tmp_file); + assert_non_null(known_hosts_file); file = fopen(known_hosts_file, "w"); assert_non_null(file); @@ -412,6 +440,44 @@ static void torture_knownhosts_conflict(void **state) assert_int_equal(found, SSH_KNOWN_HOSTS_OK); /* session will be freed by session_teardown() */ + free(known_hosts_file); +} + +static void torture_knownhosts_new_file(void **state) +{ + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + enum ssh_known_hosts_e found; + int rc; + + char new_known_hosts[256]; + char *tmp_dir = NULL; + ssize_t count = 0; + + /* Create a disposable directory */ + tmp_dir = torture_make_temp_dir(template); + assert_non_null(tmp_dir); + + count = snprintf(new_known_hosts, sizeof(new_known_hosts), + "%s/a/b/c/d/known_hosts", tmp_dir); + assert_return_code(count, errno); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, new_known_hosts); + assert_ssh_return_code(session, rc); + + rc = ssh_connect(session); + assert_ssh_return_code(session, rc); + + rc = ssh_session_update_known_hosts(session); + assert_ssh_return_code(session, rc); + + found = ssh_session_is_known_server(session); + assert_int_equal(found, SSH_KNOWN_HOSTS_OK); + + /* Cleanup */ + torture_rmdirs(tmp_dir); + + SAFE_FREE(tmp_dir); } int torture_run_tests(void) { @@ -438,6 +504,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_knownhosts_duplicate, session_setup, session_teardown), + cmocka_unit_test_setup_teardown(torture_knownhosts_new_file, + session_setup, + session_teardown), }; ssh_init(); diff --git a/tests/client/torture_proxycommand.c b/tests/client/torture_proxycommand.c index f6149c2..77dd8ad 100644 --- a/tests/client/torture_proxycommand.c +++ b/tests/client/torture_proxycommand.c @@ -7,6 +7,7 @@ #include "libssh/priv.h" #include +#include #include #include #include @@ -65,10 +66,17 @@ static void torture_options_set_proxycommand(void **state) const char *address = torture_server_address(AF_INET); int port = torture_server_port(); char command[255] = {0}; + struct stat sb; int rc; socket_t fd; - rc = snprintf(command, sizeof(command), "nc %s %d", address, port); + rc = stat("/bin/nc", &sb); + if (rc != 0 || (sb.st_mode & S_IXOTH) == 0) { + SSH_LOG(SSH_LOG_WARNING, "Could not find /bin/nc: Skipping the test"); + skip(); + } + + rc = snprintf(command, sizeof(command), "/bin/nc %s %d", address, port); assert_true((size_t)rc < sizeof(command)); rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command); @@ -102,7 +110,9 @@ static void torture_options_set_proxycommand_ssh(void **state) int rc; socket_t fd; - rc = snprintf(command, sizeof(command), "ssh -W [%%h]:%%p alice@%s", address); + rc = snprintf(command, sizeof(command), + "ssh -oStrictHostKeyChecking=no -W [%%h]:%%p alice@%s", + address); assert_true((size_t)rc < sizeof(command)); rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command); @@ -124,7 +134,10 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state) int rc; socket_t fd; - rc = snprintf(command, sizeof(command), "ssh -vvv -W [%%h]:%%p alice@%s", address); + /* The -vvv switches produce the desired output on the standard error */ + rc = snprintf(command, sizeof(command), + "ssh -vvv -oStrictHostKeyChecking=no -W [%%h]:%%p alice@%s", + address); assert_true((size_t)rc < sizeof(command)); rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command); diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c index b8e9376..7c9d812 100644 --- a/tests/client/torture_rekey.c +++ b/tests/client/torture_rekey.c @@ -72,7 +72,7 @@ static int session_setup(void **state) ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); - /* Authenticate as alice with bob his pubkey */ + /* Authenticate as alice with bob's pubkey */ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE); assert_int_equal(rc, SSH_OK); diff --git a/tests/client/torture_scp.c b/tests/client/torture_scp.c new file mode 100644 index 0000000..8f080af --- /dev/null +++ b/tests/client/torture_scp.c @@ -0,0 +1,571 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2019 by Red Hat, Inc. + * + * Author: Anderson Toshiyuki Sasaki + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "config.h" + +#include "torture.h" +#include "libssh/scp.h" + +#include +#include +#include +#include +#include + +#define BUF_SIZE 1024 + +#define TEMPLATE BINARYDIR "/tests/home/alice/temp_dir_XXXXXX" + +struct scp_st { + struct torture_state *s; + char *tmp_dir; + char *tmp_dir_basename; +}; + +static int sshd_setup(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + ts = (struct scp_st *)calloc(1, sizeof(struct scp_st)); + assert_non_null(ts); + + torture_setup_sshd_server((void **)&s, false); + assert_non_null(s); + + ts->s = s; + + *state = ts; + + return 0; +} + +static int sshd_teardown(void **state) +{ + struct scp_st *ts = NULL; + + ts = *((struct scp_st **)state); + assert_non_null(ts); + assert_non_null(ts->s); + + torture_teardown_sshd_server((void **)&(ts->s)); + + SAFE_FREE(ts); + + return 0; +} + +static int session_setup(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + char *tmp_dir = NULL; + char *tmp_dir_basename = NULL; + + struct passwd *pwd; + + int rc; + + assert_non_null(state); + + ts = *state; + + assert_non_null(ts); + assert_non_null(ts->s); + + s = ts->s; + + /* Create temporary directory for alice */ + tmp_dir = torture_make_temp_dir(TEMPLATE); + assert_non_null(tmp_dir); + ts->tmp_dir = tmp_dir; + + tmp_dir_basename = ssh_basename(tmp_dir); + assert_non_null(tmp_dir_basename); + ts->tmp_dir_basename = tmp_dir_basename; + + pwd = getpwnam("bob"); + assert_non_null(pwd); + + rc = setuid(pwd->pw_uid); + assert_return_code(rc, errno); + + s->ssh.session = torture_ssh_session(s, + TORTURE_SSH_SERVER, + NULL, + TORTURE_SSH_USER_ALICE, + NULL); + assert_non_null(s->ssh.session); + + return 0; +} + +static int session_teardown(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + assert_non_null(state); + ts = *((struct scp_st **)state); + + assert_non_null(ts->s); + s = ts->s; + + ssh_disconnect(s->ssh.session); + ssh_free(s->ssh.session); + + assert_non_null(ts->tmp_dir); + torture_rmdirs(ts->tmp_dir); + + SAFE_FREE(ts->tmp_dir); + SAFE_FREE(ts->tmp_dir_basename); + + return 0; +} + +static void torture_scp_upload(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + ssh_session session = NULL; + ssh_scp scp = NULL; + + char expected_a[BUF_SIZE]; + char buf[BUF_SIZE]; + FILE *file = NULL; + size_t len = 0; + int rc; + + assert_non_null(state); + ts = *state; + + assert_non_null(ts->s); + s = ts->s; + + session = s->ssh.session; + assert_non_null(session); + + assert_non_null(ts->tmp_dir_basename); + assert_non_null(ts->tmp_dir); + + /* Upload file "a" to alice's temp dir */ + + /* When writing the file_name must be the directory name */ + scp = ssh_scp_new(session, SSH_SCP_WRITE, ts->tmp_dir_basename); + assert_non_null(scp); + + rc = ssh_scp_init(scp); + assert_ssh_return_code(session, rc); + + /* Init buffer content to be written */ + memset(expected_a, 'A', BUF_SIZE); + + /* For ssh_scp_push_file(), the file_name is the name of the file without + * path */ + rc = ssh_scp_push_file(scp, "a", BUF_SIZE, 0644); + assert_ssh_return_code(session, rc); + + rc = ssh_scp_write(scp, expected_a, BUF_SIZE); + assert_ssh_return_code(session, rc); + + /* Cleanup */ + ssh_scp_close(scp); + ssh_scp_free(scp); + + /* Open file and check content */ + snprintf(buf, BUF_SIZE, "%s/a", ts->tmp_dir); + + file = fopen(buf, "r"); + assert_non_null(file); + + len = fread(buf, BUF_SIZE, 1, file); + assert_int_equal(len, 1); + assert_memory_equal(buf, expected_a, BUF_SIZE); + + fclose(file); +} + +static void torture_scp_upload_recursive(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + ssh_session session = NULL; + ssh_scp scp = NULL; + + char expected_b[BUF_SIZE]; + char buf[BUF_SIZE]; + FILE *file = NULL; + size_t len = 0; + + int rc; + + assert_non_null(state); + ts = *state; + + assert_non_null(ts->s); + s = ts->s; + + session = s->ssh.session; + assert_non_null(session); + + assert_non_null(ts->tmp_dir_basename); + assert_non_null(ts->tmp_dir); + + /* Upload directory "test_dir" containing file "b" to alice's temp dir */ + + /* When writing the file_name must be the directory name */ + scp = ssh_scp_new(session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, + ts->tmp_dir_basename); + assert_non_null(scp); + + rc = ssh_scp_init(scp); + assert_ssh_return_code(session, rc); + + /* Push directory where the new file will be copied */ + rc = ssh_scp_push_directory(scp, "test_dir", 0755); + assert_ssh_return_code(session, rc); + + memset(expected_b, 'B', BUF_SIZE); + + /* For ssh_scp_push_file(), the file_name is the name of the file without + * path */ + rc = ssh_scp_push_file(scp, "b", BUF_SIZE, 0644); + assert_ssh_return_code(session, rc); + + rc = ssh_scp_write(scp, expected_b, BUF_SIZE); + assert_ssh_return_code(session, rc); + + /* Leave the directory */ + rc = ssh_scp_leave_directory(scp); + assert_ssh_return_code(session, rc); + + /* Cleanup */ + ssh_scp_close(scp); + ssh_scp_free(scp); + + /* Open file and check content */ + snprintf(buf, BUF_SIZE, "%s/test_dir/b", ts->tmp_dir); + + file = fopen(buf, "r"); + assert_non_null(file); + + len = fread(buf, BUF_SIZE, 1, file); + assert_int_equal(len, 1); + assert_memory_equal(buf, expected_b, BUF_SIZE); + + fclose(file); +} + +static void torture_scp_download(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + ssh_session session = NULL; + ssh_scp scp = NULL; + + char expected_a[BUF_SIZE]; + char buf[BUF_SIZE]; + const char *remote_file = NULL; + + FILE *file = NULL; + int fd = 0; + + size_t size; + + int mode; + int rc; + + assert_non_null(state); + + ts = *state; + + assert_non_null(ts); + assert_non_null(ts->s); + + s = ts->s; + + session = s->ssh.session; + assert_non_null(session); + + assert_non_null(ts->tmp_dir_basename); + assert_non_null(ts->tmp_dir); + + /* Create file "a" for alice */ + memset(expected_a, 'A', BUF_SIZE); + + snprintf(buf, BUF_SIZE, "%s/a", ts->tmp_dir); + + fd = open(buf, O_WRONLY | O_CREAT, 0644); + assert_true(fd > 0); + + file = fdopen(fd, "w"); + assert_non_null(file); + + size = fwrite(expected_a, 1, BUF_SIZE, file); + assert_int_equal(size, BUF_SIZE); + fclose(file); + + /* Construct the file path */ + snprintf(buf, BUF_SIZE, "%s/a", ts->tmp_dir_basename); + + /* When reading, the location is the file path */ + scp = ssh_scp_new(session, SSH_SCP_READ, buf); + assert_non_null(scp); + + rc = ssh_scp_init(scp); + assert_ssh_return_code(session, rc); + + rc = ssh_scp_pull_request(scp); + assert_int_equal(rc, SSH_SCP_REQUEST_NEWFILE); + + size = ssh_scp_request_get_size(scp); + assert_int_equal(size, BUF_SIZE); + + mode = ssh_scp_request_get_permissions(scp); + assert_int_equal(mode, 0644); + + remote_file = ssh_scp_request_get_filename(scp); + assert_non_null(remote_file); + assert_string_equal(remote_file, "a"); + + rc = ssh_scp_accept_request(scp); + assert_ssh_return_code(session, rc); + + rc = ssh_scp_read(scp, buf, BUF_SIZE); + assert_int_equal(rc, size); + + assert_memory_equal(expected_a, buf, BUF_SIZE); + + /* Cleanup */ + ssh_scp_close(scp); + ssh_scp_free(scp); +} + +static void torture_scp_download_recursive(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + ssh_session session = NULL; + ssh_scp scp = NULL; + + char expected_b[BUF_SIZE]; + char buf[BUF_SIZE]; + const char *remote_file = NULL; + FILE *file = NULL; + int fd = 0; + + size_t size; + + int mode; + int rc; + + assert_non_null(state); + ts = *state; + + assert_non_null(ts->s); + s = ts->s; + + session = s->ssh.session; + assert_non_null(session); + + assert_non_null(ts->tmp_dir_basename); + assert_non_null(ts->tmp_dir); + + /* Create file "b" for alice */ + memset(expected_b, 'B', BUF_SIZE); + + snprintf(buf, BUF_SIZE, "%s/b", ts->tmp_dir); + + fd = open(buf, O_WRONLY | O_CREAT, 0644); + assert_true(fd > 0); + + file = fdopen(fd, "w"); + assert_non_null(file); + + size = fwrite(expected_b, 1, BUF_SIZE, file); + assert_int_equal(size, BUF_SIZE); + fclose(file); + + /* Copy the directory containing the file "b" */ + scp = ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE, + ts->tmp_dir_basename); + assert_non_null(scp); + + rc = ssh_scp_init(scp); + assert_ssh_return_code(session, rc); + + /* Receive the directory */ + rc = ssh_scp_pull_request(scp); + assert_int_equal(rc, SSH_SCP_REQUEST_NEWDIR); + + mode = ssh_scp_request_get_permissions(scp); + assert_int_equal(mode, 0700); + + remote_file = ssh_scp_request_get_filename(scp); + assert_non_null(remote_file); + assert_string_equal(remote_file, ts->tmp_dir_basename); + + rc = ssh_scp_accept_request(scp); + assert_ssh_return_code(session, rc); + + /* Receive the file "b" */ + rc = ssh_scp_pull_request(scp); + assert_int_equal(rc, SSH_SCP_REQUEST_NEWFILE); + + size = ssh_scp_request_get_size(scp); + assert_int_equal(size, BUF_SIZE); + + mode = ssh_scp_request_get_permissions(scp); + assert_int_equal(mode, 0644); + + remote_file = ssh_scp_request_get_filename(scp); + assert_non_null(remote_file); + assert_string_equal(remote_file, "b"); + + rc = ssh_scp_accept_request(scp); + assert_ssh_return_code(session, rc); + + rc = ssh_scp_read(scp, buf, BUF_SIZE); + assert_int_equal(rc, size); + + /* Check if the content was the expected */ + assert_memory_equal(expected_b, buf, BUF_SIZE); + + /* Receive end of directory */ + rc = ssh_scp_pull_request(scp); + assert_int_equal(rc, SSH_SCP_REQUEST_ENDDIR); + + /* Receive end of communication */ + rc = ssh_scp_pull_request(scp); + assert_int_equal(rc, SSH_SCP_REQUEST_EOF); + + /* Cleanup */ + ssh_scp_close(scp); + ssh_scp_free(scp); +} + +static void torture_scp_upload_newline(void **state) +{ + struct scp_st *ts = NULL; + struct torture_state *s = NULL; + + ssh_session session = NULL; + ssh_scp scp = NULL; + + FILE *file = NULL; + + char buf[1024]; + char *rs = NULL; + int rc; + + assert_non_null(state); + ts = *state; + + assert_non_null(ts->s); + s = ts->s; + + session = s->ssh.session; + assert_non_null(session); + + assert_non_null(ts->tmp_dir_basename); + assert_non_null(ts->tmp_dir); + + /* Upload recursively trying to inject protocol messages */ + + /* When writing the file_name must be the directory name */ + scp = ssh_scp_new(session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, + ts->tmp_dir_basename); + assert_non_null(scp); + + rc = ssh_scp_init(scp); + assert_ssh_return_code(session, rc); + + /* Push directory where the new file will be copied */ + rc = ssh_scp_push_directory(scp, "test_inject", 0755); + assert_ssh_return_code(session, rc); + + /* Try to push file with injected protocol messages */ + rc = ssh_scp_push_file(scp, "original\nreplacedC0777 8 injected", 8, 0644); + assert_ssh_return_code(session, rc); + + rc = ssh_scp_write(scp, "original", 8); + assert_ssh_return_code(session, rc); + + /* Leave the directory */ + rc = ssh_scp_leave_directory(scp); + assert_ssh_return_code(session, rc); + + /* Cleanup */ + ssh_scp_close(scp); + ssh_scp_free(scp); + + /* Open the file and check content */ + snprintf(buf, BUF_SIZE, "%s/test_inject/" + "original\\nreplacedC0777 8 injected", + ts->tmp_dir); + file = fopen(buf, "r"); + assert_non_null(file); + + rs = fgets(buf, 1024, file); + assert_non_null(rs); + assert_string_equal(buf, "original"); + + fclose(file); +} + +int torture_run_tests(void) +{ + int rc; + struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(torture_scp_upload, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_scp_upload_recursive, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_scp_download, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_scp_download_recursive, + session_setup, + session_teardown), + cmocka_unit_test_setup_teardown(torture_scp_upload_newline, + session_setup, + session_teardown), + }; + + ssh_init(); + + torture_filter_tests(tests); + rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown); + ssh_finalize(); + + return rc; +} diff --git a/tests/pkd/CMakeLists.txt b/tests/pkd/CMakeLists.txt index 71355b0..85a5777 100644 --- a/tests/pkd/CMakeLists.txt +++ b/tests/pkd/CMakeLists.txt @@ -2,14 +2,12 @@ project(pkd C) if (WITH_SERVER AND UNIX AND NOT WIN32) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMOCKA_INCLUDE_DIR} - ${ZLIB_INCLUDE_DIR} - ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR} -) + include_directories(${libssh_SOURCE_DIR}/include + ${CMOCKA_INCLUDE_DIR} + ${ZLIB_INCLUDE_DIR} + ${CMAKE_BINARY_DIR} + ${libssh_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}) set(pkd_hello_src pkd_daemon.c @@ -20,8 +18,7 @@ set(pkd_hello_src set(pkd_libs ${CMOCKA_LIBRARY} - ${LIBSSH_STATIC_LIBRARY} - ${LIBSSH_LINK_LIBRARIES} + ssh::static ${ARGP_LIBRARIES} pthread ) diff --git a/tests/pkd/pkd_daemon.c b/tests/pkd/pkd_daemon.c index 141d79a..910b374 100644 --- a/tests/pkd/pkd_daemon.c +++ b/tests/pkd/pkd_daemon.c @@ -252,6 +252,7 @@ static int pkd_exec_hello(int fd, struct pkd_daemon_args *args) size_t kex_len = 0; const char *all_ciphers = NULL; const uint64_t rekey_data_limit = args->rekey_data_limit; + bool process_config = false; pkd_state.eof_received = 0; pkd_state.close_received = 0; @@ -291,6 +292,13 @@ static int pkd_exec_hello(int fd, struct pkd_daemon_args *args) goto outclose; } + rc = ssh_bind_options_set(b, SSH_BIND_OPTIONS_PROCESS_CONFIG, + &process_config); + if (rc != 0) { + pkderr("ssh_bind_options_set process config: %s\n", ssh_get_error(b)); + goto outclose; + } + if (!ssh_fips_mode()) { /* Add methods not enabled by default */ #define GEX_SHA1 "diffie-hellman-group-exchange-sha1" diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c index 4233fe7..9ba4587 100644 --- a/tests/pkd/pkd_hello.c +++ b/tests/pkd/pkd_hello.c @@ -266,21 +266,25 @@ static int torture_pkd_setup_ecdsa_521(void **state) { f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown) \ f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown) \ f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown) \ + f(client, rsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_rsa, teardown) \ f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown) \ f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown) \ f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown) \ f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown) \ f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_256, teardown) \ f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown) \ f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown) \ f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown) \ f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown) \ f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_384, teardown) \ f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown) \ f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown) \ f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown) \ f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown) \ f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_521, teardown) \ f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown) \ f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown) #endif @@ -318,6 +322,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) { f(client, dsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_dsa, teardown) \ + f(client, dsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_dsa, teardown) \ @@ -337,6 +342,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) { f(client, dsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_dsa, teardown) \ + f(client, dsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_dsa, teardown) \ f(client, dsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_dsa, teardown) #elif defined(WITH_GEX) /* && !defined(HAVE_DSA) */ @@ -362,6 +368,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) { f(client, ed25519_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ed25519, teardown) \ f(client, ed25519_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ed25519, teardown) \ f(client, ed25519_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ed25519, teardown) \ + f(client, ed25519_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_ed25519, teardown) \ f(client, ed25519_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_ed25519, teardown) \ f(client, ed25519_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_ed25519, teardown) \ f(client, ed25519_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ed25519, teardown) \ @@ -376,6 +383,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) { f(client, ed25519_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ed25519, teardown) \ f(client, ed25519_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ed25519, teardown) \ f(client, ed25519_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ed25519, teardown) \ + f(client, ed25519_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_ed25519, teardown) \ f(client, ed25519_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_ed25519, teardown) \ f(client, ed25519_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_ed25519, teardown) \ f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown) \ @@ -1006,7 +1014,6 @@ int main(int argc, char **argv) { rc = ssh_init(); if (rc != 0) { - rc = SSH_ERROR; goto out; } diff --git a/tests/server/CMakeLists.txt b/tests/server/CMakeLists.txt index deba556..9476ea9 100644 --- a/tests/server/CMakeLists.txt +++ b/tests/server/CMakeLists.txt @@ -12,11 +12,9 @@ set(LIBSSH_SERVER_TESTS torture_server_config ) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} - test_server -) +include_directories(${libssh_SOURCE_DIR}/include + ${libssh_BINARY_DIR} + test_server) if (ARGP_INCLUDE_DIR) include_directories(${ARGP_INCLUDE_DIR}) diff --git a/tests/server/test_server/CMakeLists.txt b/tests/server/test_server/CMakeLists.txt index cc8ed6f..10bf4b7 100644 --- a/tests/server/test_server/CMakeLists.txt +++ b/tests/server/test_server/CMakeLists.txt @@ -16,10 +16,8 @@ set(LIBSSH_SERVER_TESTS # torture_server_kbdint ) -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} -) +include_directories(${libssh_SOURCE_DIR}/include + ${libssh_BINARY_DIR}) if (ARGP_INCLUDE_DIR) include_directories(${ARGP_INCLUDE_DIR}) @@ -30,7 +28,7 @@ if (UNIX AND NOT WIN32) target_compile_options(test_server PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) target_link_libraries(test_server testserver - ${LIBSSH_SHARED_LIBRARY} + ssh::ssh ${ARGP_LIBRARY} util) endif () diff --git a/tests/server/test_server/test_server.c b/tests/server/test_server/test_server.c index 5587ca3..42d2149 100644 --- a/tests/server/test_server/test_server.c +++ b/tests/server/test_server/test_server.c @@ -73,7 +73,9 @@ int run_server(struct server_state_st *state) ssh_bind sshbind = NULL; ssh_event event = NULL; - struct sigaction sa = {0}; + struct sigaction sa = { + .sa_flags = 0 + }; int rc; diff --git a/tests/server/torture_server.c b/tests/server/torture_server.c index 3a23a3c..518fc97 100644 --- a/tests/server/torture_server.c +++ b/tests/server/torture_server.c @@ -404,7 +404,7 @@ static void torture_server_auth_pubkey(void **state) session = s->ssh.session; assert_non_null(session); - /* Authenticate as alice with bob his pubkey */ + /* Authenticate as alice with bob's pubkey */ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE); assert_int_equal(rc, SSH_OK); diff --git a/tests/server/torture_server_auth_kbdint.c b/tests/server/torture_server_auth_kbdint.c index 6d7f585..5bf8159 100644 --- a/tests/server/torture_server_auth_kbdint.c +++ b/tests/server/torture_server_auth_kbdint.c @@ -657,9 +657,9 @@ static int session_setup(void **state) assert_non_null(s->ssh.session); rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - assert_return_code(s->ssh.session, rc); + assert_ssh_return_code(s->ssh.session, rc); rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); - assert_return_code(s->ssh.session, rc); + assert_ssh_return_code(s->ssh.session, rc); /* Make sure no other configuration options from system will get used */ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b); assert_ssh_return_code(s->ssh.session, rc); diff --git a/tests/server/torture_server_config.c b/tests/server/torture_server_config.c index ac2f266..d751dd7 100644 --- a/tests/server/torture_server_config.c +++ b/tests/server/torture_server_config.c @@ -321,9 +321,9 @@ static int session_setup(void **state) assert_non_null(s->ssh.session); rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - assert_return_code(s->ssh.session, rc); + assert_ssh_return_code(s->ssh.session, rc); rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); - assert_return_code(s->ssh.session, rc); + assert_ssh_return_code(s->ssh.session, rc); /* Make sure no other configuration options from system will get used */ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b); assert_ssh_return_code(s->ssh.session, rc); @@ -430,10 +430,7 @@ static size_t setup_hostkey_files(struct test_server_st *tss) hostkey_files[0] = tss->rsa_hostkey; -#ifndef TEST_ALL_CRYPTO_COMBINATIONS - goto end; -#endif - +#ifdef TEST_ALL_CRYPTO_COMBINATIONS hostkey_files[1] = tss->ecdsa_256_hostkey; hostkey_files[2] = tss->ecdsa_384_hostkey; hostkey_files[3] = tss->ecdsa_521_hostkey; @@ -448,8 +445,8 @@ static size_t setup_hostkey_files(struct test_server_st *tss) num_hostkey_files++; #endif } +#endif /* TEST_ALL_CRYPTO_COMBINATIONS */ -end: return num_hostkey_files; } @@ -471,9 +468,6 @@ static void torture_server_config_hostkey(void **state) "HostKey %s\n", hostkey_files[i]); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); } @@ -513,9 +507,6 @@ static void torture_server_config_ciphers(void **state) "HostKey %s\nCiphers %s\n", hostkey_files[i], ciphers); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -527,9 +518,6 @@ static void torture_server_config_ciphers(void **state) "HostKey %s\nCiphers %s\n", hostkey_files[i], tokens->tokens[j]); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -574,9 +562,6 @@ static void torture_server_config_macs(void **state) "HostKey %s\nMACs %s\n", hostkey_files[i], macs); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -588,9 +573,6 @@ static void torture_server_config_macs(void **state) "HostKey %s\nMACs %s\n", hostkey_files[i], tokens->tokens[j]); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -635,9 +617,6 @@ static void torture_server_config_kex(void **state) "HostKey %s\nKexAlgorithms %s\n", hostkey_files[i], kex); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -649,9 +628,6 @@ static void torture_server_config_kex(void **state) "HostKey %s\nKexAlgorithms %s\n", hostkey_files[i], tokens->tokens[j]); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -691,9 +667,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostKeyAlgorithms %s\n", hostkey_files[i], allowed); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); } @@ -707,9 +680,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->ed25519_hostkey, "ssh-ed25519"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -719,9 +689,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->rsa_hostkey, "ssh-rsa"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); } @@ -732,9 +699,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->rsa_hostkey, "rsa-sha2-256"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -744,9 +708,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->rsa_hostkey, "rsa-sha2-512"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -756,9 +717,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->ecdsa_256_hostkey, "ecdsa-sha2-nistp256"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -768,9 +726,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->ecdsa_384_hostkey, "ecdsa-sha2-nistp384"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -780,9 +735,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->ecdsa_521_hostkey, "ecdsa-sha2-nistp521"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); @@ -794,9 +746,6 @@ static void torture_server_config_hostkey_algorithms(void **state) "HostKey %s\nHostkeyAlgorithms %s\n", tss->dsa_hostkey, "ssh-dss"); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); } @@ -818,9 +767,6 @@ static void torture_server_config_unknown(void **state) "HostKey %s\nUnknownOption unknown-value1,unknown-value2\n", tss->rsa_hostkey); - rc = try_config_content(state, config_content, true); - assert_int_equal(rc, 0); - rc = try_config_content(state, config_content, false); assert_int_equal(rc, 0); } diff --git a/tests/torture.c b/tests/torture.c index 772942c..907f45b 100644 --- a/tests/torture.c +++ b/tests/torture.c @@ -196,6 +196,7 @@ torture_read_pidfile(const char *pidfile) { char buf[8] = {0}; long int tmp; + pid_t ret; ssize_t rc; int fd; @@ -213,11 +214,16 @@ torture_read_pidfile(const char *pidfile) buf[sizeof(buf) - 1] = '\0'; tmp = strtol(buf, NULL, 10); - if (tmp == 0 || tmp > 0xFFFF || errno == ERANGE) { + if (tmp == 0 || errno == ERANGE) { + return -1; + } + ret = (pid_t)tmp; + /* Check if we are out of pid_t range on this system */ + if ((long)ret != tmp) { return -1; } - return (pid_t)(tmp & 0xFFFF); + return ret; } int torture_terminate_process(const char *pidfile) @@ -262,6 +268,8 @@ ssh_session torture_ssh_session(struct torture_state *s, int method; int rc; + bool process_config = false; + if (host == NULL) { return NULL; } @@ -277,6 +285,10 @@ ssh_session torture_ssh_session(struct torture_state *s, } #endif /* WITH_PCAP */ + if (ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity) < 0) { + goto failed; + } + if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) { goto failed; } @@ -293,7 +305,8 @@ ssh_session torture_ssh_session(struct torture_state *s, } } - if (ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity) < 0) { + if (ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, + &process_config) < 0) { goto failed; } @@ -581,7 +594,7 @@ static void torture_setup_create_sshd_config(void **state, bool pam) char rsa_hostkey[1024]; char ecdsa_hostkey[1024]; char trusted_ca_pubkey[1024]; - char sshd_config[2048]; + char sshd_config[4096]; char sshd_path[1024]; const char *additional_config = NULL; struct stat sb; diff --git a/tests/torture_key.c b/tests/torture_key.c index e0c7643..5854026 100644 --- a/tests/torture_key.c +++ b/tests/torture_key.c @@ -602,7 +602,12 @@ static const char torture_ecdsa521_testkey_cert[] = * ED25519 KEYS ****************************************************************************/ -static const char torture_ed25519_private_testkey[] = +static const char torture_ed25519_private_pkcs8_testkey[] = + "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VwBCIEIGBhcqLe61tkqVjIHKEzwB3oINasSHWGbIWXQWcLPmGN\n" + "-----END PRIVATE KEY-----\n"; + +static const char torture_ed25519_private_openssh_testkey[] = "-----BEGIN OPENSSH PRIVATE KEY-----\n" "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n" "QyNTUxOQAAACAVlp8bgmIjsrzGC7ZIKBMhCpS1fpJTPgVOjYdz5gIqlwAAAJBzsDN1c7Az\n" @@ -611,16 +616,24 @@ static const char torture_ed25519_private_testkey[] = "lLV+klM+BU6Nh3PmAiqXAAAADGFyaXNAa2FsaXg4NgE=\n" "-----END OPENSSH PRIVATE KEY-----\n"; -static const char torture_ed25519_private_testkey_passphrase[] = +static const char torture_ed25519_private_openssh_testkey_passphrase[] = "-----BEGIN OPENSSH PRIVATE KEY-----\n" - "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABB3FWpQcE\n" - "KHKq6PcjkxjmKzAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIOGFVuOyZBL0T+NR\n" - "C7qEV9qr6QiGhz2XSXrxuQoU84FgAAAAkBlOVfS5U7FxtBEtxfxQhZjrZAj2z9d4OfGRPl\n" - "ZfCnAJNEM3BZ3XCabsujhMkqEs9eptRfj41X6NA8aSFs5JYT+JFVfg470FKtpyUmAibMIo\n" - "JzI41zAncFd1x7bAgO5HBDe3xNsV159D+sXRkWB9Tzk0l4F8SZvInheIS7VSbqH7t1+yDB\n" - "Y3GsmYTDstmicanQ==\n" + "b3BlbnNzaC1rZXktdjEAAAAACmFlczEyOC1jYmMAAAAGYmNyeXB0AAAAGAAAABDYuz+a8i\n" + "nb/BgGjQjQtvkUAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCYiOyvMYL\n" + "tkgoEyEKlLV+klM+BU6Nh3PmAiqXAAAAkOBxqvzvPSns3TbhjkCayvANI66100OELnpDOm\n" + "JBGgXr5q846NkAovH3pmJ4O7qzPLTQ/cm0+959VUODRhM1i96qBg5MTNtV33lf5Y57Klzu\n" + "JegbiexcqkHIzriH42K0XSOEpfW8f/rTH7ffjbE/7l8HRNwf7AmcnxLx/d8J8FTBr+8aU7\n" + "qMU3xAJ4ixnwhYFg==\n" "-----END OPENSSH PRIVATE KEY-----\n"; +static const char torture_ed25519_private_pkcs8_testkey_passphrase[] = + "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + "MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAie1RBk/ub+EwICCAAw\n" + "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEECRLkPChQx/sZPYLdNJhxMUEQFLj\n" + "7nelAdOx3WXIBbCOfOqg3aAn8C5cXPtIQ+fiui1V8wlXXV8RBiuDCC97ScLs91D5\n" + "qQhQtw0vgfnq1um/izg=\n" + "-----END ENCRYPTED PRIVATE KEY-----\n"; + static const char torture_ed25519_public_testkey[] = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCYiOyvMYLtkgoEyEKlLV+klM+" "BU6Nh3PmAiqX aris@kalix86"; @@ -733,16 +746,19 @@ static const char *torture_get_testkey_internal(enum ssh_keytypes_e type, return torture_ed25519_public_testkey; } else if (with_passphrase) { if (format == 1) { - return torture_ed25519_private_testkey_passphrase; + return torture_ed25519_private_openssh_testkey_passphrase; + } + if (format == 2) { + return torture_ed25519_private_pkcs8_testkey_passphrase; } /* ed25519 keys are not available in legacy PEM format */ return NULL; } if (format == 1) { - return torture_ed25519_private_testkey; + return torture_ed25519_private_openssh_testkey; } /* ed25519 keys are not available in legacy PEM format */ - return NULL; + return torture_ed25519_private_pkcs8_testkey; case SSH_KEYTYPE_DSS_CERT01: return torture_dsa_testkey_cert; case SSH_KEYTYPE_RSA_CERT01: diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c index 2968080..f91112a 100644 --- a/tests/unittests/torture_config.c +++ b/tests/unittests/torture_config.c @@ -5,6 +5,8 @@ #include "torture.h" #include "libssh/options.h" #include "libssh/session.h" +#include "libssh/config_parser.h" +#include "match.c" extern LIBSSH_THREAD int ssh_log_level; @@ -440,6 +442,8 @@ static void torture_config_unknown(void **state) { static void torture_config_match(void **state) { ssh_session session = *state; + char *localuser = NULL; + char config[1024]; int ret = 0; /* Without any settings we should get all-matched.com hostname */ @@ -531,6 +535,19 @@ static void torture_config_match(void **state) assert_ssh_return_code(session, ret); assert_string_equal(session->opts.host, "canonical.com"); + localuser = ssh_get_local_username(); + assert_non_null(localuser); + snprintf(config, sizeof(config), + "Match localuser %s\n" + "\tHostName otherhost\n" + "", localuser); + free(localuser); + torture_write_file(LIBSSH_TESTCONFIG10, config); + torture_reset_config(session); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_ssh_return_code(session, ret); + assert_string_equal(session->opts.host, "otherhost"); + /* Try to create some invalid configurations */ /* Missing argument to Match*/ torture_write_file(LIBSSH_TESTCONFIG10, @@ -550,7 +567,7 @@ static void torture_config_match(void **state) ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); assert_ssh_return_code_equal(session, ret, SSH_ERROR); - /* Missing argument to unsupported option localuser */ + /* Missing argument to option localuser */ torture_write_file(LIBSSH_TESTCONFIG10, "Match localuser\n" "\tUser localuser2\n" @@ -559,7 +576,7 @@ static void torture_config_match(void **state) ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); assert_ssh_return_code_equal(session, ret, SSH_ERROR); - /* Missing argument to option user*/ + /* Missing argument to option user */ torture_write_file(LIBSSH_TESTCONFIG10, "Match user\n" "\tUser user2\n" @@ -892,6 +909,89 @@ static void torture_config_pubkeyacceptedkeytypes(void **state) } } +/* match_pattern() sanity tests + */ +static void torture_config_match_pattern(void **state) +{ + int rv = 0; + + (void) state; + + /* Simple test "a" matches "a" */ + rv = match_pattern("a", "a", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + + /* Simple test "a" does not match "b" */ + rv = match_pattern("a", "b", MAX_MATCH_RECURSION); + assert_int_equal(rv, 0); + + /* NULL arguments are correctly handled */ + rv = match_pattern("a", NULL, MAX_MATCH_RECURSION); + assert_int_equal(rv, 0); + rv = match_pattern(NULL, "a", MAX_MATCH_RECURSION); + assert_int_equal(rv, 0); + + /* Simple wildcard ? is handled in pattern */ + rv = match_pattern("a", "?", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("aa", "?", MAX_MATCH_RECURSION); + assert_int_equal(rv, 0); + rv = match_pattern("?", "a", MAX_MATCH_RECURSION); /* Wildcard in search string */ + assert_int_equal(rv, 0); + rv = match_pattern("?", "?", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + + /* Simple wildcard * is handled in pattern */ + rv = match_pattern("a", "*", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("aa", "*", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("*", "a", MAX_MATCH_RECURSION); /* Wildcard in search string */ + assert_int_equal(rv, 0); + rv = match_pattern("*", "*", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + + /* More complicated patterns */ + rv = match_pattern("a", "*a", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("a", "a*", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("abababc", "*abc", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("ababababca", "*abc", MAX_MATCH_RECURSION); + assert_int_equal(rv, 0); + rv = match_pattern("ababababca", "*abc*", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + + /* Multiple wildcards in row */ + rv = match_pattern("aa", "??", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("bba", "??a", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("aaa", "**a", MAX_MATCH_RECURSION); + assert_int_equal(rv, 1); + rv = match_pattern("bbb", "**a", MAX_MATCH_RECURSION); + assert_int_equal(rv, 0); + + /* Consecutive asterisks do not make sense and do not need to recurse */ + rv = match_pattern("hostname", "**********pattern", 5); + assert_int_equal(rv, 0); + rv = match_pattern("hostname", "pattern**********", 5); + assert_int_equal(rv, 0); + rv = match_pattern("pattern", "***********pattern", 5); + assert_int_equal(rv, 1); + rv = match_pattern("pattern", "pattern***********", 5); + assert_int_equal(rv, 1); + + /* Limit the maximum recursion */ + rv = match_pattern("hostname", "*p*a*t*t*e*r*n*", 5); + assert_int_equal(rv, 0); + rv = match_pattern("pattern", "*p*a*t*t*e*r*n*", 5); /* Too much recursion */ + assert_int_equal(rv, 0); + +} + + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -905,6 +1005,7 @@ int torture_run_tests(void) { cmocka_unit_test(torture_config_proxyjump), cmocka_unit_test(torture_config_rekey), cmocka_unit_test(torture_config_pubkeyacceptedkeytypes), + cmocka_unit_test(torture_config_match_pattern), }; diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c index ac8d7f3..1c2ccc1 100644 --- a/tests/unittests/torture_knownhosts_parsing.c +++ b/tests/unittests/torture_knownhosts_parsing.c @@ -28,6 +28,8 @@ #define TMP_FILE_NAME "/tmp/known_hosts_XXXXXX" +const char template[] = "temp_dir_XXXXXX"; + static int setup_knownhosts_file(void **state) { char *tmp_file = NULL; @@ -369,6 +371,140 @@ static void torture_knownhosts_read_file(void **state) ssh_list_free(entry_list); } +static void torture_knownhosts_get_algorithms_names(void **state) +{ + const char *knownhosts_file = *state; + ssh_session session; + const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + char *names = NULL; + bool process_config = false; + + session = ssh_new(); + assert_non_null(session); + + /* This makes sure the global configuration file is not processed */ + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); + + names = ssh_known_hosts_get_algorithms_names(session); + assert_non_null(names); + assert_string_equal(names, expect); + + SAFE_FREE(names); + ssh_free(session); +} + +static void torture_knownhosts_algorithms_wanted(void **state) +{ + const char *knownhosts_file = *state; + char *algo_list = NULL; + ssh_session session; + bool process_config = false; + const char *wanted = "ecdsa-sha2-nistp384,ecdsa-sha2-nistp256," + "rsa-sha2-256,ecdsa-sha2-nistp521"; + const char *expect = "rsa-sha2-256,ecdsa-sha2-nistp384," + "ecdsa-sha2-nistp256,ecdsa-sha2-nistp521"; + int verbose = 4; + + session = ssh_new(); + assert_non_null(session); + + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose); + + /* This makes sure the global configuration file is not processed */ + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + + /* Set the wanted list of hostkeys, ordered by preference */ + ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); + + algo_list = ssh_client_select_hostkeys(session); + assert_non_null(algo_list); + assert_string_equal(algo_list, expect); + free(algo_list); + + ssh_free(session); +} + +static void torture_knownhosts_algorithms_negative(UNUSED_PARAM(void **state)) +{ + const char *wanted = NULL; + const char *expect = NULL; + + char *algo_list = NULL; + + char *cwd = NULL; + char *tmp_dir = NULL; + + bool process_config = false; + int verbose = 4; + int rc = 0; + + ssh_session session; + /* Create temporary directory */ + cwd = torture_get_current_working_dir(); + assert_non_null(cwd); + + tmp_dir = torture_make_temp_dir(template); + assert_non_null(tmp_dir); + + rc = torture_change_dir(tmp_dir); + assert_int_equal(rc, 0); + + session = ssh_new(); + assert_non_null(session); + + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose); + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + + /* Test with unknown key type in known_hosts */ + wanted = "rsa-sha2-256"; + ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); + torture_write_file("unknown_key_type", "localhost unknown AAAABBBBCCCC"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "unknown_key_type"); + algo_list = ssh_client_select_hostkeys(session); + assert_non_null(algo_list); + assert_string_equal(algo_list, wanted); + SAFE_FREE(algo_list); + + /* Test with unsupported, but existing types */ + wanted = "rsa-sha2-256-cert-v01@openssh.com," + "rsa-sha2-512-cert-v01@openssh.com"; + ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); + algo_list = ssh_client_select_hostkeys(session); + assert_null(algo_list); + + /* In FIPS mode, test filtering keys not allowed */ + if (ssh_fips_mode()) { + wanted = "ssh-ed25519,rsa-sha2-256,ssh-rsa"; + expect = "rsa-sha2-256"; + ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); + torture_write_file("no_fips", LOCALHOST_DEFAULT_ED25519); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "no_fips"); + algo_list = ssh_client_select_hostkeys(session); + assert_non_null(algo_list); + assert_string_equal(algo_list, expect); + SAFE_FREE(algo_list); + } + + ssh_free(session); + + /* Teardown */ + rc = torture_change_dir(cwd); + assert_int_equal(rc, 0); + + rc = torture_rmdirs(tmp_dir); + assert_int_equal(rc, 0); + + SAFE_FREE(tmp_dir); + SAFE_FREE(cwd); +} + #ifndef _WIN32 /* There is no /dev/null on Windows */ static void torture_knownhosts_host_exists(void **state) { @@ -384,22 +520,19 @@ static void torture_knownhosts_host_exists(void **state) /* This makes sure the system's known_hosts are not used */ ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, "/dev/null"); - found = ssh_session_has_known_hosts_entry(session); assert_int_equal(found, SSH_KNOWN_HOSTS_OK); - assert_true(found == SSH_KNOWN_HOSTS_OK); /* This makes sure the check will not fail when the system's known_hosts is * not accessible*/ ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, "./unaccessible"); - found = ssh_session_has_known_hosts_entry(session); assert_int_equal(found, SSH_KNOWN_HOSTS_OK); - assert_true(found == SSH_KNOWN_HOSTS_OK); + /* This makes sure the check will fail for an unknown host */ ssh_options_set(session, SSH_OPTIONS_HOST, "wurstbrot"); found = ssh_session_has_known_hosts_entry(session); - assert_true(found == SSH_KNOWN_HOSTS_UNKNOWN); + assert_int_equal(found, SSH_KNOWN_HOSTS_UNKNOWN); ssh_free(session); } @@ -414,27 +547,33 @@ static void torture_knownhosts_host_exists_global(void **state) assert_non_null(session); ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, knownhosts_file); + /* This makes sure the user's known_hosts are not used */ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "/dev/null"); - ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, knownhosts_file); + found = ssh_session_has_known_hosts_entry(session); + assert_int_equal(found, SSH_KNOWN_HOSTS_OK); + /* This makes sure the check will not fail when the user's known_hosts is + * not accessible*/ + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "./unaccessible"); found = ssh_session_has_known_hosts_entry(session); assert_int_equal(found, SSH_KNOWN_HOSTS_OK); - assert_true(found == SSH_KNOWN_HOSTS_OK); + /* This makes sure the check will fail for an unknown host */ ssh_options_set(session, SSH_OPTIONS_HOST, "wurstbrot"); found = ssh_session_has_known_hosts_entry(session); - assert_true(found == SSH_KNOWN_HOSTS_UNKNOWN); + assert_int_equal(found, SSH_KNOWN_HOSTS_UNKNOWN); ssh_free(session); } -static void -torture_knownhosts_algorithms(void **state) +static void torture_knownhosts_algorithms(void **state) { const char *knownhosts_file = *state; char *algo_list = NULL; ssh_session session; + bool process_config = false; const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa," "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384," "ecdsa-sha2-nistp256" @@ -442,10 +581,15 @@ torture_knownhosts_algorithms(void **state) ",ssh-dss" #endif ; + const char *expect_fips = "rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521," + "ecdsa-sha2-nistp384,ecdsa-sha2-nistp256"; session = ssh_new(); assert_non_null(session); + /* This makes sure the global configuration file is not processed */ + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); /* This makes sure the system's known_hosts are not used */ @@ -453,18 +597,22 @@ torture_knownhosts_algorithms(void **state) algo_list = ssh_client_select_hostkeys(session); assert_non_null(algo_list); - assert_string_equal(algo_list, expect); + if (ssh_fips_mode()) { + assert_string_equal(algo_list, expect_fips); + } else { + assert_string_equal(algo_list, expect); + } free(algo_list); ssh_free(session); } -static void -torture_knownhosts_algorithms_global(void **state) +static void torture_knownhosts_algorithms_global(void **state) { const char *knownhosts_file = *state; char *algo_list = NULL; ssh_session session; + bool process_config = false; const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa," "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384," "ecdsa-sha2-nistp256" @@ -472,10 +620,15 @@ torture_knownhosts_algorithms_global(void **state) ",ssh-dss" #endif ; + const char *expect_fips = "rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521," + "ecdsa-sha2-nistp384,ecdsa-sha2-nistp256"; session = ssh_new(); assert_non_null(session); + /* This makes sure the global configuration file is not processed */ + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); /* This makes sure the current-user's known hosts are not used */ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "/dev/null"); @@ -483,12 +636,17 @@ torture_knownhosts_algorithms_global(void **state) algo_list = ssh_client_select_hostkeys(session); assert_non_null(algo_list); - assert_string_equal(algo_list, expect); + if (ssh_fips_mode()) { + assert_string_equal(algo_list, expect_fips); + } else { + assert_string_equal(algo_list, expect); + } free(algo_list); ssh_free(session); } -#endif + +#endif /* _WIN32 There is no /dev/null on Windows */ int torture_run_tests(void) { int rc; @@ -507,6 +665,13 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_knownhosts_read_file, setup_knownhosts_file_duplicate, teardown_knownhosts_file), + cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names, + setup_knownhosts_file, + teardown_knownhosts_file), + cmocka_unit_test_setup_teardown(torture_knownhosts_algorithms_wanted, + setup_knownhosts_file, + teardown_knownhosts_file), + cmocka_unit_test(torture_knownhosts_algorithms_negative), #ifndef _WIN32 cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists, setup_knownhosts_file, diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c index 26da536..ef06d65 100644 --- a/tests/unittests/torture_misc.c +++ b/tests/unittests/torture_misc.c @@ -19,6 +19,7 @@ #define TORTURE_TEST_DIR "/usr/local/bin/truc/much/.." +const char template[] = "temp_dir_XXXXXX"; static int setup(void **state) { @@ -192,6 +193,18 @@ static void torture_path_expand_known_hosts(void **state) { free(tmp); } +static void torture_path_expand_percent(void **state) { + ssh_session session = *state; + char *tmp; + + session->opts.sshdir = strdup("/home/guru/.ssh"); + + tmp = ssh_path_expand_escape(session, "%d/config%%1"); + assert_non_null(tmp); + assert_string_equal(tmp, "/home/guru/.ssh/config%1"); + free(tmp); +} + static void torture_timeout_elapsed(void **state){ struct ssh_timestamp ts; (void) state; @@ -351,6 +364,297 @@ static void torture_ssh_analyze_banner(void **state) { ssh_free(session); } +static void torture_ssh_dir_writeable(UNUSED_PARAM(void **state)) +{ + char *tmp_dir = NULL; + int rc = 0; + FILE *file = NULL; + char buffer[256]; + + tmp_dir = torture_make_temp_dir(template); + assert_non_null(tmp_dir); + + rc = ssh_dir_writeable(tmp_dir); + assert_int_equal(rc, 1); + + /* Create a file */ + snprintf(buffer, sizeof(buffer), "%s/a", tmp_dir); + + file = fopen(buffer, "w"); + assert_non_null(file); + + fprintf(file, "Hello world!\n"); + fclose(file); + + /* Negative test for checking a normal file */ + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 0); + + /* Negative test for non existent file */ + snprintf(buffer, sizeof(buffer), "%s/b", tmp_dir); + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 0); + +#ifndef _WIN32 + /* Negative test for directory without write permission */ + rc = ssh_mkdir(buffer, 0400); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 0); +#endif + + torture_rmdirs(tmp_dir); + + SAFE_FREE(tmp_dir); +} + +static void torture_ssh_mkdirs(UNUSED_PARAM(void **state)) +{ + char *tmp_dir = NULL; + char *cwd = NULL; + char buffer[256]; + + ssize_t count = 0; + + int rc; + + /* Get current working directory */ + cwd = torture_get_current_working_dir(); + assert_non_null(cwd); + + /* Create a base disposable directory */ + tmp_dir = torture_make_temp_dir(template); + assert_non_null(tmp_dir); + + /* Create a single directory */ + count = snprintf(buffer, sizeof(buffer), "%s/a", tmp_dir); + assert_return_code(count, errno); + + rc = ssh_mkdirs(buffer, 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 1); + + /* Create directories recursively */ + count = snprintf(buffer, sizeof(buffer), "%s/b/c/d", tmp_dir); + assert_return_code(count, errno); + + rc = ssh_mkdirs(buffer, 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable(buffer); + assert_int_equal(rc, 1); + + /* Change directory */ + rc = torture_change_dir(tmp_dir); + assert_return_code(rc, errno); + + /* Create single local directory */ + rc = ssh_mkdirs("e", 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable("e"); + assert_int_equal(rc, 1); + + /* Create local directories recursively */ + rc = ssh_mkdirs("f/g/h", 0700); + assert_return_code(rc, errno); + + rc = ssh_dir_writeable("f/g/h"); + assert_int_equal(rc, 1); + + /* Negative test for creating "." directory */ + rc = ssh_mkdirs(".", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating "/" directory */ + rc = ssh_mkdirs("/", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating "" directory */ + rc = ssh_mkdirs("", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating NULL directory */ + rc = ssh_mkdirs(NULL, 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EINVAL); + + /* Negative test for creating existing directory */ + rc = ssh_mkdirs("a", 0700); + assert_int_equal(rc, -1); + assert_int_equal(errno, EEXIST); + + /* Return to original directory */ + rc = torture_change_dir(cwd); + assert_return_code(rc, errno); + + /* Cleanup */ + torture_rmdirs(tmp_dir); + + SAFE_FREE(tmp_dir); + SAFE_FREE(cwd); +} + +static void torture_ssh_quote_file_name(UNUSED_PARAM(void **state)) +{ + char buffer[2048]; + int rc; + + /* Only ordinary chars */ + rc = ssh_quote_file_name("a b", buffer, 2048); + assert_int_equal(rc, 5); + assert_string_equal(buffer, "'a b'"); + + /* Single quote in file name */ + rc = ssh_quote_file_name("a'b", buffer, 2048); + assert_int_equal(rc, 9); + assert_string_equal(buffer, "'a'\"'\"'b'"); + + /* Exclamation in file name */ + rc = ssh_quote_file_name("a!b", buffer, 2048); + assert_int_equal(rc, 8); + assert_string_equal(buffer, "'a'\\!'b'"); + + /* All together */ + rc = ssh_quote_file_name("'a!b'", buffer, 2048); + assert_int_equal(rc, 14); + assert_string_equal(buffer, "\"'\"'a'\\!'b'\"'\""); + + rc = ssh_quote_file_name("a'!b", buffer, 2048); + assert_int_equal(rc, 11); + assert_string_equal(buffer, "'a'\"'\"\\!'b'"); + + rc = ssh_quote_file_name("a'$b", buffer, 2048); + assert_int_equal(rc, 10); + assert_string_equal(buffer, "'a'\"'\"'$b'"); + + rc = ssh_quote_file_name("a'`b", buffer, 2048); + assert_int_equal(rc, 10); + assert_string_equal(buffer, "'a'\"'\"'`b'"); + + + rc = ssh_quote_file_name(" ", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "' '"); + + rc = ssh_quote_file_name(" ", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "' '"); + + + rc = ssh_quote_file_name("\r", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "'\r'"); + + rc = ssh_quote_file_name("\n", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "'\n'"); + + rc = ssh_quote_file_name("\r\n", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "'\r\n'"); + + + rc = ssh_quote_file_name("\\r", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "'\\r'"); + + rc = ssh_quote_file_name("\\n", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "'\\n'"); + + rc = ssh_quote_file_name("\\r\\n", buffer, 2048); + assert_int_equal(rc, 6); + assert_string_equal(buffer, "'\\r\\n'"); + + + rc = ssh_quote_file_name("\t", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "'\t'"); + + rc = ssh_quote_file_name("\v", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "'\v'"); + + rc = ssh_quote_file_name("\t\v", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "'\t\v'"); + + + rc = ssh_quote_file_name("'", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "\"'\""); + + rc = ssh_quote_file_name("''", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "\"''\""); + + + rc = ssh_quote_file_name("\"", buffer, 2048); + assert_int_equal(rc, 3); + assert_string_equal(buffer, "'\"'"); + + rc = ssh_quote_file_name("\"\"", buffer, 2048); + assert_int_equal(rc, 4); + assert_string_equal(buffer, "'\"\"'"); + + rc = ssh_quote_file_name("'\"", buffer, 2048); + assert_int_equal(rc, 6); + assert_string_equal(buffer, "\"'\"'\"'"); + + rc = ssh_quote_file_name("\"'", buffer, 2048); + assert_int_equal(rc, 6); + assert_string_equal(buffer, "'\"'\"'\""); + + + /* Worst case */ + rc = ssh_quote_file_name("a'b'", buffer, 3 * 4 + 1); + assert_int_equal(rc, 12); + assert_string_equal(buffer, "'a'\"'\"'b'\"'\""); + + /* Negative tests */ + + /* NULL params */ + rc = ssh_quote_file_name(NULL, buffer, 3 * 4 + 1); + assert_int_equal(rc, SSH_ERROR); + + /* NULL params */ + rc = ssh_quote_file_name("a b", NULL, 3 * 4 + 1); + assert_int_equal(rc, SSH_ERROR); + + /* Small buffer size */ + rc = ssh_quote_file_name("a b", buffer, 0); + assert_int_equal(rc, SSH_ERROR); + + /* Worst case and small buffer size */ + rc = ssh_quote_file_name("a'b'", buffer, 3 * 4); + assert_int_equal(rc, SSH_ERROR); +} + +static void torture_ssh_newline_vis(UNUSED_PARAM(void **state)) +{ + int rc; + char buffer[1024]; + + rc = ssh_newline_vis("\n", buffer, 1024); + assert_int_equal(rc, 2); + assert_string_equal(buffer, "\\n"); + + rc = ssh_newline_vis("\n\n\n\n", buffer, 1024); + assert_int_equal(rc, 8); + assert_string_equal(buffer, "\\n\\n\\n\\n"); + + rc = ssh_newline_vis("a\nb\n", buffer, 1024); + assert_int_equal(rc, 6); + assert_string_equal(buffer, "a\\nb\\n"); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -365,9 +669,14 @@ int torture_run_tests(void) { #endif cmocka_unit_test_setup_teardown(torture_path_expand_escape, setup, teardown), cmocka_unit_test_setup_teardown(torture_path_expand_known_hosts, setup, teardown), + cmocka_unit_test_setup_teardown(torture_path_expand_percent, setup, teardown), cmocka_unit_test(torture_timeout_elapsed), cmocka_unit_test(torture_timeout_update), cmocka_unit_test(torture_ssh_analyze_banner), + cmocka_unit_test(torture_ssh_dir_writeable), + cmocka_unit_test(torture_ssh_newline_vis), + cmocka_unit_test(torture_ssh_mkdirs), + cmocka_unit_test(torture_ssh_quote_file_name), }; ssh_init(); diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c index 6ae99e7..d0fdaed 100644 --- a/tests/unittests/torture_options.c +++ b/tests/unittests/torture_options.c @@ -109,18 +109,21 @@ static void torture_options_set_key_exchange(void **state) "curve25519-sha256,curve25519-sha256@libssh.org," "ecdh-sha2-nistp256,diffie-hellman-group16-sha512," "diffie-hellman-group18-sha512," + "diffie-hellman-group14-sha256," "diffie-hellman-group14-sha1"); assert_true(rc == 0); assert_non_null(session->opts.wanted_methods[SSH_KEX]); if (ssh_fips_mode()) { assert_string_equal(session->opts.wanted_methods[SSH_KEX], "ecdh-sha2-nistp256,diffie-hellman-group16-sha512," - "diffie-hellman-group18-sha512"); + "diffie-hellman-group18-sha512," + "diffie-hellman-group14-sha256"); } else { assert_string_equal(session->opts.wanted_methods[SSH_KEX], "curve25519-sha256,curve25519-sha256@libssh.org," "ecdh-sha2-nistp256,diffie-hellman-group16-sha512," "diffie-hellman-group18-sha512," + "diffie-hellman-group14-sha256," "diffie-hellman-group14-sha1"); } @@ -571,6 +574,7 @@ static void torture_options_config_host(void **state) { static void torture_options_config_match(void **state) { ssh_session session = *state; + char *localuser = NULL; FILE *config = NULL; int rv; @@ -665,11 +669,16 @@ static void torture_options_config_match(void **state) session->opts.port = 0; - /* The Match localuser keyword is ignored */ + /* The Match localuser keyword */ torture_reset_config(session); config = fopen("test_config", "w"); assert_non_null(config); - fputs("Match originalhost origin\n" + fputs("Match localuser ", config); + localuser = ssh_get_local_username(); + assert_non_null(localuser); + fputs(localuser, config); + free(localuser); + fputs("\n" "\tPort 33\n" "Match all\n" "\tPort 34\n", @@ -678,7 +687,7 @@ static void torture_options_config_match(void **state) rv = ssh_options_parse_config(session, "test_config"); assert_ssh_return_code(session, rv); - assert_int_equal(session->opts.port, 34); + assert_int_equal(session->opts.port, 33); session->opts.port = 0; @@ -810,7 +819,7 @@ static void torture_options_copy(void **state) assert_string_equal(session->opts.knownhosts, new->opts.knownhosts); assert_string_equal(session->opts.global_knownhosts, new->opts.global_knownhosts); - for (i = 0; i < 10; i++) { + for (i = 0; i < SSH_KEX_METHODS; i++) { if (session->opts.wanted_methods[i] == NULL) { assert_null(new->opts.wanted_methods[i]); } else { @@ -1321,18 +1330,21 @@ static void torture_bind_options_set_key_exchange(void **state) "curve25519-sha256,curve25519-sha256@libssh.org," "ecdh-sha2-nistp256,diffie-hellman-group16-sha512," "diffie-hellman-group18-sha512," + "diffie-hellman-group14-sha256," "diffie-hellman-group14-sha1"); assert_int_equal(rc, 0); assert_non_null(bind->wanted_methods[SSH_KEX]); if (ssh_fips_mode()) { assert_string_equal(bind->wanted_methods[SSH_KEX], "ecdh-sha2-nistp256,diffie-hellman-group16-sha512," - "diffie-hellman-group18-sha512"); + "diffie-hellman-group18-sha512," + "diffie-hellman-group14-sha256"); } else { assert_string_equal(bind->wanted_methods[SSH_KEX], "curve25519-sha256,curve25519-sha256@libssh.org," "ecdh-sha2-nistp256,diffie-hellman-group16-sha512," "diffie-hellman-group18-sha512," + "diffie-hellman-group14-sha256," "diffie-hellman-group14-sha1"); } diff --git a/tests/unittests/torture_pki.c b/tests/unittests/torture_pki.c index 0e4ea1a..d886245 100644 --- a/tests/unittests/torture_pki.c +++ b/tests/unittests/torture_pki.c @@ -331,7 +331,7 @@ static void torture_pki_verify_mismatch(void **state) assert_int_equal(import_sig->type, key->type); assert_string_equal(import_sig->type_c, skey_attrs.sig_type_c); - rc = pki_signature_verify(session, + rc = ssh_pki_signature_verify(session, import_sig, pubkey, INPUT, @@ -374,7 +374,7 @@ static void torture_pki_verify_mismatch(void **state) assert_non_null(verify_pubkey); /* Should gracefully fail, but not crash */ - rc = pki_signature_verify(session, + rc = ssh_pki_signature_verify(session, sign, verify_pubkey, INPUT, @@ -382,7 +382,7 @@ static void torture_pki_verify_mismatch(void **state) assert_true(rc != SSH_OK); /* Try the same with the imported signature */ - rc = pki_signature_verify(session, + rc = ssh_pki_signature_verify(session, import_sig, verify_pubkey, INPUT, @@ -401,7 +401,7 @@ static void torture_pki_verify_mismatch(void **state) assert_string_equal(new_sig->type_c, skey_attrs.sig_type_c); /* The verification should not work */ - rc = pki_signature_verify(session, + rc = ssh_pki_signature_verify(session, new_sig, verify_pubkey, INPUT, diff --git a/tests/unittests/torture_pki_dsa.c b/tests/unittests/torture_pki_dsa.c index bb86c51..b555e48 100644 --- a/tests/unittests/torture_pki_dsa.c +++ b/tests/unittests/torture_pki_dsa.c @@ -199,6 +199,37 @@ static void torture_pki_dsa_import_privkey_base64_comment(void **state) SSH_KEY_FREE(key); } +static void torture_pki_dsa_import_privkey_base64_whitespace(void **state) +{ + int rc, file_str_len; + ssh_key key = NULL; + const char *passphrase = torture_get_testkey_passphrase(); + const char *whitespace_str = " \n\t\t\t\t\t\n\n\n\n\n"; + const char *key_str = NULL; + char *file_str = NULL; + + (void) state; /* unused */ + + key_str = torture_get_testkey(SSH_KEYTYPE_DSS, 0); + assert_non_null(key_str); + + file_str_len = strlen(whitespace_str) + strlen(key_str) + 1; + file_str = malloc(file_str_len); + assert_non_null(file_str); + rc = snprintf(file_str, file_str_len, "%s%s", whitespace_str, key_str); + assert_int_equal(rc, file_str_len - 1); + + rc = ssh_pki_import_privkey_base64(file_str, + passphrase, + NULL, + NULL, + &key); + assert_true(rc == 0); + + free(file_str); + SSH_KEY_FREE(key); +} + static int test_sign_verify_data(ssh_key key, enum ssh_digest_e hash_type, const unsigned char *input, @@ -778,7 +809,7 @@ static void torture_pki_dsa_generate_key(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(key); @@ -792,7 +823,7 @@ static void torture_pki_dsa_generate_key(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(key); @@ -806,7 +837,7 @@ static void torture_pki_dsa_generate_key(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(key); @@ -837,7 +868,7 @@ static void torture_pki_dsa_cert_verify(void **state) sign = pki_do_sign(privkey, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1); assert_non_null(sign); - rc = pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(privkey); @@ -867,6 +898,9 @@ int torture_run_tests(void) cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64_comment, setup_dsa_key, teardown), + cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64_whitespace, + setup_dsa_key, + teardown), cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64, setup_openssh_dsa_key, teardown), diff --git a/tests/unittests/torture_pki_ecdsa.c b/tests/unittests/torture_pki_ecdsa.c index 13b2a89..22f06a4 100644 --- a/tests/unittests/torture_pki_ecdsa.c +++ b/tests/unittests/torture_pki_ecdsa.c @@ -272,6 +272,38 @@ static void torture_pki_ecdsa_import_privkey_base64_comment(void **state) SSH_KEY_FREE(key); } +static void torture_pki_ecdsa_import_privkey_base64_whitespace(void **state) +{ + int rc, file_str_len; + const char *whitespace_str = " \n\t\t\t\t\t\n\n\n\n\n"; + char *key_str = NULL, *file_str = NULL; + ssh_key key = NULL; + const char *passphrase = torture_get_testkey_passphrase(); + + (void) state; /* unused */ + + key_str = torture_pki_read_file(LIBSSH_ECDSA_TESTKEY); + assert_non_null(key_str); + + file_str_len = strlen(whitespace_str) + strlen(key_str) + 1; + file_str = malloc(file_str_len); + assert_non_null(file_str); + rc = snprintf(file_str, file_str_len, "%s%s", whitespace_str, key_str); + assert_int_equal(rc, file_str_len - 1); + + rc = ssh_pki_import_privkey_base64(file_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + assert_non_null(key); + + rc = ssh_key_is_private(key); + assert_true(rc == 1); + + free(key_str); + free(file_str); + SSH_KEY_FREE(key); +} + + static void torture_pki_ecdsa_publickey_from_privatekey(void **state) { int rc; @@ -514,7 +546,7 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ECDSA_P256); @@ -536,7 +568,7 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ECDSA_P256); @@ -557,7 +589,7 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA384); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ECDSA_P384); @@ -579,7 +611,7 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA384); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ECDSA_P384); @@ -600,7 +632,7 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA512); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ECDSA_P521); @@ -622,7 +654,7 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA512); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ECDSA_P521); @@ -664,7 +696,7 @@ static void torture_pki_ecdsa_cert_verify(void **state) sign = pki_do_sign(privkey, INPUT, sizeof(INPUT), hash_type); assert_non_null(sign); - rc = pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(privkey); @@ -944,6 +976,15 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_comment, setup_ecdsa_key_521, teardown), + cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_whitespace, + setup_ecdsa_key_521, + teardown), + cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_whitespace, + setup_ecdsa_key_521, + teardown), + cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_whitespace, + setup_ecdsa_key_521, + teardown), cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64, setup_openssh_ecdsa_key_256, teardown), diff --git a/tests/unittests/torture_pki_ed25519.c b/tests/unittests/torture_pki_ed25519.c index 0ace82c..07ccfd6 100644 --- a/tests/unittests/torture_pki_ed25519.c +++ b/tests/unittests/torture_pki_ed25519.c @@ -149,6 +149,84 @@ static void torture_pki_ed25519_import_privkey_base64(void **state) } +static void torture_pki_ed25519_import_privkey_base64_comment(void **state) +{ + int rc, file_str_len; + const char *comment_str = "#this is line-comment\n#this is another\n"; + char *key_str = NULL, *file_str = NULL; + ssh_key key = NULL; + const char *passphrase = torture_get_testkey_passphrase(); + enum ssh_keytypes_e type; + + (void) state; /* unused */ + + key_str = torture_pki_read_file(LIBSSH_ED25519_TESTKEY); + assert_non_null(key_str); + + file_str_len = strlen(comment_str) + strlen(key_str) + 1; + file_str = malloc(file_str_len); + assert_non_null(file_str); + rc = snprintf(file_str, file_str_len, "%s%s", comment_str, key_str); + assert_int_equal(rc, file_str_len - 1); + + rc = ssh_pki_import_privkey_base64(file_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + assert_non_null(key); + + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_ED25519); + + rc = ssh_key_is_private(key); + assert_true(rc == 1); + + rc = ssh_key_is_public(key); + assert_true(rc == 1); + + free(key_str); + free(file_str); + SSH_KEY_FREE(key); + +} + +static void torture_pki_ed25519_import_privkey_base64_whitespace(void **state) +{ + int rc, file_str_len; + const char *whitespace_str = " \n\t\t\t\t\t\n\n\n\n\n"; + char *key_str = NULL, *file_str = NULL; + ssh_key key = NULL; + const char *passphrase = torture_get_testkey_passphrase(); + enum ssh_keytypes_e type; + + (void) state; /* unused */ + + key_str = torture_pki_read_file(LIBSSH_ED25519_TESTKEY); + assert_non_null(key_str); + + file_str_len = strlen(whitespace_str) + strlen(key_str) + 1; + file_str = malloc(file_str_len); + assert_non_null(file_str); + rc = snprintf(file_str, file_str_len, "%s%s", whitespace_str, key_str); + assert_int_equal(rc, file_str_len - 1); + + rc = ssh_pki_import_privkey_base64(file_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + assert_non_null(key); + + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_ED25519); + + rc = ssh_key_is_private(key); + assert_true(rc == 1); + + rc = ssh_key_is_public(key); + assert_true(rc == 1); + + free(key_str); + free(file_str); + SSH_KEY_FREE(key); + +} + static void torture_pki_ed25519_import_export_privkey_base64(void **state) { char *b64_key = NULL; @@ -339,19 +417,30 @@ static void torture_pki_ed25519_generate_pubkey_from_privkey(void **state) static void torture_pki_ed25519_generate_key(void **state) { int rc; - ssh_key key = NULL; + ssh_key key = NULL, pubkey = NULL; ssh_signature sign = NULL; enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN; const char *type_char = NULL; ssh_session session=ssh_new(); + uint8_t *raw_sig_data = NULL; (void) state; + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + assert_non_null(session); + rc = ssh_pki_generate(SSH_KEYTYPE_ED25519, 256, &key); assert_true(rc == SSH_OK); assert_non_null(key); + rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); + assert_int_equal(rc, SSH_OK); + assert_non_null(pubkey); sign = pki_do_sign(key, HASH, 20, SSH_DIGEST_AUTO); assert_non_null(sign); - rc = pki_signature_verify(session,sign,key,HASH,20); + rc = ssh_pki_signature_verify(session, sign, pubkey, HASH, 20); assert_true(rc == SSH_OK); type = ssh_key_type(key); assert_true(type == SSH_KEYTYPE_ED25519); @@ -359,12 +448,19 @@ static void torture_pki_ed25519_generate_key(void **state) assert_true(strcmp(type_char, "ssh-ed25519") == 0); /* try an invalid signature */ - (*sign->ed25519_sig)[3]^= 0xff; - rc = pki_signature_verify(session,sign,key,HASH,20); +#ifdef HAVE_OPENSSL_ED25519 + raw_sig_data = ssh_string_data(sign->raw_sig); +#else + raw_sig_data = (uint8_t *)sign->ed25519_sig; +#endif + assert_non_null(raw_sig_data); + (raw_sig_data)[3]^= 0xff; + rc = ssh_pki_signature_verify(session, sign, pubkey, HASH, 20); assert_true(rc == SSH_ERROR); ssh_signature_free(sign); SSH_KEY_FREE(key); + SSH_KEY_FREE(pubkey); ssh_free(session); } @@ -377,6 +473,13 @@ static void torture_pki_ed25519_cert_verify(void **state) ssh_session session=ssh_new(); (void) state; + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + assert_non_null(session); + rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY, NULL, NULL, @@ -391,7 +494,7 @@ static void torture_pki_ed25519_cert_verify(void **state) sign = pki_do_sign(privkey, HASH, 20, SSH_DIGEST_AUTO); assert_non_null(sign); - rc = pki_signature_verify(session, sign, cert, HASH, 20); + rc = ssh_pki_signature_verify(session, sign, cert, HASH, 20); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(privkey); @@ -517,10 +620,12 @@ static void torture_pki_ed25519_sign(void **state) const char *keystring = NULL; int rc; - (void)state; + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } - sig = ssh_signature_new(); - assert_non_null(sig); + (void)state; keystring = torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0); rc = ssh_pki_import_privkey_base64(keystring, @@ -531,31 +636,157 @@ static void torture_pki_ed25519_sign(void **state) assert_true(rc == SSH_OK); assert_non_null(privkey); - sig->type = SSH_KEYTYPE_ED25519; - rc = pki_ed25519_sign(privkey, sig, HASH, sizeof(HASH)); + sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO); + assert_non_null(sig); + + blob = pki_signature_to_blob(sig); + assert_non_null(blob); + + assert_int_equal(ssh_string_len(blob), sizeof(ref_signature)); + assert_memory_equal(ssh_string_data(blob), ref_signature, + sizeof(ref_signature)); + + ssh_signature_free(sig); + SSH_KEY_FREE(privkey); + SSH_STRING_FREE(blob); + +} + +static void torture_pki_ed25519_sign_openssh_privkey_passphrase(void **state) +{ + ssh_key privkey = NULL; + ssh_signature sig = NULL; + ssh_string blob = NULL; + const char *keystring = NULL; + int rc; + + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + (void)state; + + keystring = torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 1); + rc = ssh_pki_import_privkey_base64(keystring, + torture_get_testkey_passphrase(), + NULL, + NULL, + &privkey); assert_true(rc == SSH_OK); + assert_non_null(privkey); + + sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO); + assert_non_null(sig); blob = pki_signature_to_blob(sig); assert_non_null(blob); + assert_int_equal(ssh_string_len(blob), sizeof(ref_signature)); + assert_memory_equal(ssh_string_data(blob), ref_signature, + sizeof(ref_signature)); + + ssh_signature_free(sig); + SSH_KEY_FREE(privkey); + SSH_STRING_FREE(blob); +} + +#ifdef HAVE_OPENSSL_ED25519 +static void torture_pki_ed25519_sign_pkcs8_privkey(void **state) +{ + ssh_key privkey = NULL; + ssh_signature sig = NULL; + ssh_string blob = NULL; + const char *keystring = NULL; + int rc; + + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + (void)state; + + keystring = torture_get_testkey(SSH_KEYTYPE_ED25519, 0); + rc = ssh_pki_import_privkey_base64(keystring, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == SSH_OK); + assert_non_null(privkey); + sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO); + assert_non_null(sig); + + blob = pki_signature_to_blob(sig); + assert_non_null(blob); assert_int_equal(ssh_string_len(blob), sizeof(ref_signature)); - assert_memory_equal(ssh_string_data(blob), ref_signature, sizeof(ref_signature)); - /* ssh_print_hexa("signature", ssh_string_data(blob), ssh_string_len(blob)); */ + assert_memory_equal(ssh_string_data(blob), ref_signature, + sizeof(ref_signature)); + ssh_signature_free(sig); SSH_KEY_FREE(privkey); SSH_STRING_FREE(blob); +} + +static void torture_pki_ed25519_sign_pkcs8_privkey_passphrase(void **state) +{ + ssh_key privkey = NULL; + ssh_signature sig = NULL; + ssh_string blob = NULL; + const char *keystring = NULL; + int rc; + + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + (void)state; + keystring = torture_get_testkey(SSH_KEYTYPE_ED25519, 1); + rc = ssh_pki_import_privkey_base64(keystring, + torture_get_testkey_passphrase(), + NULL, + NULL, + &privkey); + assert_true(rc == SSH_OK); + assert_non_null(privkey); + + sig = pki_do_sign(privkey, HASH, sizeof(HASH), SSH_DIGEST_AUTO); + assert_non_null(sig); + + blob = pki_signature_to_blob(sig); + assert_non_null(blob); + assert_int_equal(ssh_string_len(blob), sizeof(ref_signature)); + assert_memory_equal(ssh_string_data(blob), ref_signature, + sizeof(ref_signature)); + + ssh_signature_free(sig); + SSH_KEY_FREE(privkey); + SSH_STRING_FREE(blob); } +#endif /* HAVE_OPENSSL_ED25519 */ static void torture_pki_ed25519_verify(void **state){ ssh_key pubkey = NULL; ssh_signature sig = NULL; + ssh_session session = NULL; ssh_string blob = ssh_string_new(ED25519_SIG_LEN); char *pkey_ptr = strdup(strchr(torture_get_testkey_pub(SSH_KEYTYPE_ED25519), ' ') + 1); char *ptr = NULL; + uint8_t *raw_sig_data = NULL; int rc; (void) state; + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + session = ssh_new(); + assert_non_null(session); + /* remove trailing comment */ ptr = strchr(pkey_ptr, ' '); if(ptr != NULL){ @@ -569,20 +800,32 @@ static void torture_pki_ed25519_verify(void **state){ sig = pki_signature_from_blob(pubkey, blob, SSH_KEYTYPE_ED25519, SSH_DIGEST_AUTO); assert_non_null(sig); - rc = pki_ed25519_verify(pubkey, sig, HASH, sizeof(HASH)); + rc = ssh_pki_signature_verify(session, sig, pubkey, HASH, sizeof(HASH)); assert_true(rc == SSH_OK); + /* Alter signature and expect verification error */ +#if defined(HAVE_OPENSSL_ED25519) + raw_sig_data = ssh_string_data(sig->raw_sig); +#else + raw_sig_data = (uint8_t *)sig->ed25519_sig; +#endif + assert_non_null(raw_sig_data); + (raw_sig_data)[3]^= 0xff; + rc = ssh_pki_signature_verify(session, sig, pubkey, HASH, sizeof(HASH)); + assert_true(rc == SSH_ERROR); + ssh_signature_free(sig); - /* alter signature and expect false result */ SSH_KEY_FREE(pubkey); SSH_STRING_FREE(blob); free(pkey_ptr); + ssh_free(session); } static void torture_pki_ed25519_verify_bad(void **state){ ssh_key pubkey = NULL; ssh_signature sig = NULL; + ssh_session session = NULL; ssh_string blob = ssh_string_new(ED25519_SIG_LEN); char *pkey_ptr = strdup(strchr(torture_get_testkey_pub(SSH_KEYTYPE_ED25519), ' ') + 1); char *ptr = NULL; @@ -590,6 +833,14 @@ static void torture_pki_ed25519_verify_bad(void **state){ int i; (void) state; + /* Skip test if in FIPS mode */ + if (ssh_fips_mode()) { + skip(); + } + + session = ssh_new(); + assert_non_null(session); + /* remove trailing comment */ ptr = strchr(pkey_ptr, ' '); if(ptr != NULL){ @@ -607,7 +858,7 @@ static void torture_pki_ed25519_verify_bad(void **state){ sig = pki_signature_from_blob(pubkey, blob, SSH_KEYTYPE_ED25519, SSH_DIGEST_AUTO); assert_non_null(sig); - rc = pki_ed25519_verify(pubkey, sig, HASH, sizeof(HASH)); + rc = ssh_pki_signature_verify(session, sig, pubkey, HASH, sizeof(HASH)); assert_true(rc == SSH_ERROR); ssh_signature_free(sig); @@ -615,6 +866,7 @@ static void torture_pki_ed25519_verify_bad(void **state){ SSH_KEY_FREE(pubkey); SSH_STRING_FREE(blob); free(pkey_ptr); + ssh_free(session); } static void torture_pki_ed25519_import_privkey_base64_passphrase(void **state) @@ -730,6 +982,12 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_pki_ed25519_import_privkey_base64, setup_ed25519_key, teardown), + cmocka_unit_test_setup_teardown(torture_pki_ed25519_import_privkey_base64_comment, + setup_ed25519_key, + teardown), + cmocka_unit_test_setup_teardown(torture_pki_ed25519_import_privkey_base64_whitespace, + setup_ed25519_key, + teardown), cmocka_unit_test_setup_teardown(torture_pki_ed25519_import_export_privkey_base64, setup_ed25519_key, teardown), @@ -754,6 +1012,11 @@ int torture_run_tests(void) { teardown), cmocka_unit_test(torture_pki_ed25519_import_privkey_base64_passphrase), cmocka_unit_test(torture_pki_ed25519_sign), + cmocka_unit_test(torture_pki_ed25519_sign_openssh_privkey_passphrase), +#ifdef HAVE_OPENSSL_ED25519 + cmocka_unit_test(torture_pki_ed25519_sign_pkcs8_privkey), + cmocka_unit_test(torture_pki_ed25519_sign_pkcs8_privkey_passphrase), +#endif cmocka_unit_test(torture_pki_ed25519_verify), cmocka_unit_test(torture_pki_ed25519_verify_bad), cmocka_unit_test(torture_pki_ed25519_privkey_dup), diff --git a/tests/unittests/torture_pki_rsa.c b/tests/unittests/torture_pki_rsa.c index c330524..a986706 100644 --- a/tests/unittests/torture_pki_rsa.c +++ b/tests/unittests/torture_pki_rsa.c @@ -251,6 +251,44 @@ static void torture_pki_rsa_import_privkey_base64_comment(void **state) SSH_KEY_FREE(key); } +static void torture_pki_rsa_import_privkey_base64_whitespace(void **state) +{ + int rc, file_str_len; + const char *whitespace_str = " \n\t\t\t\t\t\n\n\n\n\n"; + char *key_str = NULL, *file_str = NULL; + ssh_key key = NULL; + const char *passphrase = torture_get_testkey_passphrase(); + enum ssh_keytypes_e type; + + (void) state; /* unused */ + + key_str = torture_pki_read_file(LIBSSH_RSA_TESTKEY); + assert_non_null(key_str); + + file_str_len = strlen(whitespace_str) + strlen(key_str) + 1; + file_str = malloc(file_str_len); + assert_non_null(file_str); + rc = snprintf(file_str, file_str_len, "%s%s", whitespace_str, key_str); + assert_int_equal(rc, file_str_len - 1); + + rc = ssh_pki_import_privkey_base64(file_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + assert_non_null(key); + + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_RSA); + + rc = ssh_key_is_private(key); + assert_true(rc == 1); + + rc = ssh_key_is_public(key); + assert_true(rc == 1); + + free(key_str); + free(file_str); + SSH_KEY_FREE(key); +} + static void torture_pki_rsa_publickey_from_privatekey(void **state) { int rc; @@ -515,7 +553,7 @@ static void torture_pki_rsa_generate_key(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(key); @@ -532,7 +570,7 @@ static void torture_pki_rsa_generate_key(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(key); @@ -548,7 +586,7 @@ static void torture_pki_rsa_generate_key(void **state) assert_non_null(pubkey); sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_true(rc == SSH_OK); ssh_signature_free(sign); SSH_KEY_FREE(key); @@ -587,9 +625,9 @@ static void torture_pki_rsa_sha2(void **state) /* Sign using old SHA1 digest */ sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_ssh_return_code(session, rc); - rc = pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); assert_ssh_return_code(session, rc); ssh_signature_free(sign); } @@ -597,18 +635,18 @@ static void torture_pki_rsa_sha2(void **state) /* Sign using new SHA256 digest */ sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_ssh_return_code(session, rc); - rc = pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); assert_ssh_return_code(session, rc); ssh_signature_free(sign); /* Sign using rsa-sha2-512 algorithm */ sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA512); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT)); assert_ssh_return_code(session, rc); - rc = pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); + rc = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT)); assert_ssh_return_code(session, rc); ssh_signature_free(sign); @@ -920,6 +958,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64_comment, setup_rsa_key, teardown), + cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64_whitespace, + setup_rsa_key, + teardown), cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64, setup_openssh_rsa_key, teardown), diff --git a/tests/unittests/torture_session_keys.c b/tests/unittests/torture_session_keys.c index e7ef2e4..f220e01 100644 --- a/tests/unittests/torture_session_keys.c +++ b/tests/unittests/torture_session_keys.c @@ -81,7 +81,7 @@ static void torture_session_keys(UNUSED_PARAM(void **state)) assert_memory_equal(test_crypto.encryptMAC, eMAC, 32); assert_memory_equal(test_crypto.decryptMAC, dMAC, 32); - ssh_string_free(k_string); + SSH_STRING_FREE(k_string); } int torture_run_tests(void) { diff --git a/tests/unittests/torture_threads_pki_rsa.c b/tests/unittests/torture_threads_pki_rsa.c index 5a841ee..a04a746 100644 --- a/tests/unittests/torture_threads_pki_rsa.c +++ b/tests/unittests/torture_threads_pki_rsa.c @@ -571,23 +571,25 @@ static void *thread_pki_rsa_generate_key(void *threadid) session = ssh_new(); assert_non_null(session); - rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key); - assert_ssh_return_code(session, rc); - assert_non_null(key); + if (!ssh_fips_mode()) { + rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key); + assert_ssh_return_code(session, rc); + assert_non_null(key); - rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); - assert_int_equal(rc, SSH_OK); - assert_non_null(pubkey); + rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); + assert_int_equal(rc, SSH_OK); + assert_non_null(pubkey); - sign = pki_do_sign(key, RSA_HASH, 20, SSH_DIGEST_SHA256); - assert_non_null(sign); + sign = pki_do_sign(key, RSA_HASH, 20, SSH_DIGEST_SHA256); + assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); - assert_ssh_return_code(session, rc); + rc = ssh_pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); + assert_ssh_return_code(session, rc); - ssh_signature_free(sign); - SSH_KEY_FREE(key); - SSH_KEY_FREE(pubkey); + ssh_signature_free(sign); + SSH_KEY_FREE(key); + SSH_KEY_FREE(pubkey); + } rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key); assert_ssh_return_code(session, rc); @@ -600,7 +602,7 @@ static void *thread_pki_rsa_generate_key(void *threadid) sign = pki_do_sign(key, RSA_HASH, 20, SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); + rc = ssh_pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); assert_ssh_return_code(session, rc); ssh_signature_free(sign); @@ -618,7 +620,7 @@ static void *thread_pki_rsa_generate_key(void *threadid) sign = pki_do_sign(key, RSA_HASH, 20, SSH_DIGEST_SHA256); assert_non_null(sign); - rc = pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); + rc = ssh_pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); assert_true(rc == SSH_OK); ssh_signature_free(sign); diff --git a/tests/unittests/torture_tokens.c b/tests/unittests/torture_tokens.c index e192842..6b52b84 100644 --- a/tests/unittests/torture_tokens.c +++ b/tests/unittests/torture_tokens.c @@ -146,6 +146,126 @@ static void torture_tokens_sanity(UNUSED_PARAM(void **state)) tokenize_compare_expected(",", single_colon, 1); } +static void torture_remove_duplicate(UNUSED_PARAM(void **state)) +{ + + const char *simple[] = {"a,a,b,b,c,c", + "a,b,c,a,b,c", + "a,b,c,c,b,a", + "a,a,,b,b,,c,c", + ",a,a,b,b,c,c", + "a,a,b,b,c,c,"}; + const char *empty[] = {"", + ",,,,,,,,,", + NULL}; + char *ret = NULL; + int i; + + for (i = 0; i < 6; i++) { + ret = ssh_remove_duplicates(simple[i]); + assert_non_null(ret); + assert_string_equal("a,b,c", ret); + printf("simple[%d] resulted in '%s'\n", i, ret); + SAFE_FREE(ret); + } + + for (i = 0; i < 3; i++) { + ret = ssh_remove_duplicates(empty[i]); + if (ret != NULL) { + printf("empty[%d] resulted in '%s'\n", i, ret); + } + assert_null(ret); + } + + ret = ssh_remove_duplicates("a"); + assert_non_null(ret); + assert_string_equal("a", ret); + SAFE_FREE(ret); +} + +static void torture_append_without_duplicate(UNUSED_PARAM(void **state)) +{ + const char *s1[] = {"a,a,b,b,c,c", + "a,b,c,a,b,c", + "a,b,c,c,b,a", + "a,a,,b,b,,c,c", + ",a,a,b,b,c,c", + "a,a,b,b,c,c,"}; + const char *s2[] = {"a,a,b,b,c,c,d,d", + "a,b,c,d,a,b,c,d", + "a,b,c,d,d,c,b,a", + "a,a,,b,b,,c,c,,d,d", + ",a,a,b,b,c,c,d,d", + "a,a,b,b,c,c,d,d,", + "d"}; + const char *empty[] = {"", + ",,,,,,,,,", + NULL, + NULL}; + char *ret = NULL; + int i, j; + + ret = ssh_append_without_duplicates("a", "a"); + assert_non_null(ret); + assert_string_equal("a", ret); + SAFE_FREE(ret); + + ret = ssh_append_without_duplicates("a", "b"); + assert_non_null(ret); + assert_string_equal("a,b", ret); + SAFE_FREE(ret); + + ret = ssh_append_without_duplicates("a", NULL); + assert_non_null(ret); + assert_string_equal("a", ret); + SAFE_FREE(ret); + + ret = ssh_append_without_duplicates(NULL, "b"); + assert_non_null(ret); + assert_string_equal("b", ret); + SAFE_FREE(ret); + + for (i = 0; i < 6; i++) { + for (j = 0; j < 7; j++) { + ret = ssh_append_without_duplicates(s1[i], s2[j]); + assert_non_null(ret); + printf("s1[%d] + s2[%d] resulted in '%s'\n", i, j, ret); + assert_string_equal("a,b,c,d", ret); + SAFE_FREE(ret); + } + } + + for (i = 0; i < 6; i++) { + for (j = 0; j < 3; j++) { + ret = ssh_append_without_duplicates(s1[i], empty[j]); + assert_non_null(ret); + printf("s1[%d] + empty[%d] resulted in '%s'\n", i, j, ret); + assert_string_equal("a,b,c", ret); + SAFE_FREE(ret); + } + } + + for (i = 0; i < 3; i++) { + for (j = 0; j < 6; j++) { + ret = ssh_append_without_duplicates(empty[i], s1[j]); + assert_non_null(ret); + printf("empty[%d] + s1[%d] resulted in '%s'\n", i, j, ret); + assert_string_equal("a,b,c", ret); + SAFE_FREE(ret); + } + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + ret = ssh_append_without_duplicates(empty[i], empty[j]); + if (ret != NULL) { + printf("empty[%d] + empty[%d] resulted in '%s'\n", i, j, ret); + } + assert_null(ret); + } + } +} + + int torture_run_tests(void) { int rc; @@ -153,6 +273,8 @@ int torture_run_tests(void) cmocka_unit_test(torture_tokens_sanity), cmocka_unit_test(torture_find_matching), cmocka_unit_test(torture_find_all_matching), + cmocka_unit_test(torture_remove_duplicate), + cmocka_unit_test(torture_append_without_duplicate), }; ssh_init();