diff --git a/.travis.yml b/.travis.yml index 9d6c0bec60..6d2d28d813 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,11 @@ # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY # OF SUCH DAMAGE. +sudo: required + +services: + - docker + language: c compiler: @@ -59,12 +64,17 @@ env: - ADDRESS_SIZE=32 CRYPTO_BACKEND=Libgcrypt BUILD_SHARED_LIBS=ON ENABLE_ZLIB_COMPRESSION=ON before_install: - - sudo add-apt-repository --yes ppa:kalakris/cmake - - sudo apt-get update - - sudo apt-get -y install cmake - - if [ $ADDRESS_SIZE = '64' ]; then sudo apt-get install -y libgcrypt11-dev libssl-dev zlib1g-dev; fi - - if [ $ADDRESS_SIZE = '32' ]; then sudo apt-get install -y linux-libc-dev linux-libc-dev:i386; fi - - if [ $ADDRESS_SIZE = '32' ]; then sudo apt-get install -y gcc-multilib libgcrypt11-dev:i386 libssl-dev:i386 zlib1g-dev:i386; fi + # Fix issue with chrome and 32-bit multilib + # See http://www.omgubuntu.co.uk/2016/03/fix-failed-to-fetch-google-chrome-apt-error-ubuntu + - sudo sed -i -e 's/deb http/deb [arch=amd64] http/' "/etc/apt/sources.list.d/google-chrome.list" + - sudo sed -i -e 's/deb http/deb [arch=amd64] http/' "/opt/google/chrome/cron/google-chrome" + - if [ $ADDRESS_SIZE = '32' ]; then sudo dpkg --add-architecture i386; fi + - if [ $ADDRESS_SIZE = '32' ]; then sudo apt-get update -qq; fi + - if [ $ADDRESS_SIZE = '32' ]; then sudo apt-get install -y gcc-multilib; fi + - if [ $ADDRESS_SIZE = '32' ]; then sudo apt-get install -y libssl-dev:i386 libgcrypt11-dev:i386 build-essential gcc-multilib; fi + - if [ $ADDRESS_SIZE = '32' ]; then sudo dpkg --purge --force-depends gcc-multilib && sudo dpkg --purge --force-depends libssl-dev; fi + - if [ $ADDRESS_SIZE = '64' ]; then sudo apt-get install -y libssl-dev; fi + - if [ $ADDRESS_SIZE = '64' ]; then sudo apt-get install -y libgcrypt11-dev; fi - if [ $ADDRESS_SIZE = '32' ]; then export TOOLCHAIN_OPTION="-DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-Linux-32.cmake"; fi install: diff --git a/CMakeLists.txt b/CMakeLists.txt index dc585cd4d5..ae55942892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,14 @@ project(libssh2 C) set(PROJECT_URL "https://www.libssh2.org/") set(PROJECT_DESCRIPTION "The SSH library") +if (CMAKE_VERSION VERSION_LESS "3.1") + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + set (CMAKE_C_FLAGS "--std=gnu90 ${CMAKE_C_FLAGS}") + endif() +else() + set (CMAKE_C_STANDARD 90) +endif() + option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) # Parse version diff --git a/appveyor.yml b/appveyor.yml index eb2017ad01..5537f60af5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -57,6 +57,9 @@ environment: - GENERATOR: "Visual Studio 9 2008" BUILD_SHARED_LIBS: OFF + digitalocean_access_token: + secure: 8qRitvrj69Xhf0Tmu27xnz5drmL2YhmOJLGpXIkYyTCC0JNtBoXW6fMcF3u4Uj1+pIQ+TjegQOwYimlz0oivKTro3v3EXro+osAMNJG6NKc= + platform: - x86 - x64 @@ -66,19 +69,34 @@ configuration: - Release matrix: + fast_finish: true allow_failures: - GENERATOR: "Visual Studio 9 2008" platform: x64 +install: + - choco install -y docker + - choco install -y docker-machine + build_script: - ps: if($env:PLATFORM -eq "x64") { $env:CMAKE_GEN_SUFFIX=" Win64" } - cmake "-G%GENERATOR%%CMAKE_GEN_SUFFIX%" -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% -H. -B_builds - cmake --build _builds --config "%CONFIGURATION%" +before_test: + - set DOCKER_MACHINE_NAME=appveyor-%APPVEYOR_PROJECT_SLUG%-%APPVEYOR_JOB_ID% + - ps: if($env:digitalocean_access_token) { echo "Using DigitalOcean for testing." } else { echo "DigitalOcean not available. Skipping testing." } + - ps: if($env:digitalocean_access_token) { docker-machine create --driver digitalocean --digitalocean-access-token $($env:digitalocean_access_token) $($env:DOCKER_MACHINE_NAME) } + - ps: if($env:digitalocean_access_token) { docker-machine env $($env:DOCKER_MACHINE_NAME) --shell powershell | Invoke-Expression } + test_script: - ps: cd _builds - - ctest -VV -C "%CONFIGURATION%" -E ssh2 --output-on-failure + - ps: if($env:digitalocean_access_token) { ctest -VV -C $($env:CONFIGURATION) --output-on-failure } + +after_test: + - ps: if($env:digitalocean_access_token) { docker-machine rm -y $($env:DOCKER_MACHINE_NAME) } on_failure: - - ps: if (Test-Path _builds/CMakeFiles/CMakeOutput.log) { cat _builds/CMakeFiles/CMakeOutput.log } - - ps: if (Test-Path _builds/CMakeFiles/CMakeError.log) { cat _builds/CMakeFiles/CMakeError.log } + - ps: if($env:digitalocean_access_token) { docker-machine rm -y $($env:DOCKER_MACHINE_NAME) } + - ps: if(Test-Path _builds/CMakeFiles/CMakeOutput.log) { cat _builds/CMakeFiles/CMakeOutput.log } + - ps: if(Test-Path _builds/CMakeFiles/CMakeError.log) { cat _builds/CMakeFiles/CMakeError.log } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 90733eaf33..bd0f903e28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014, 2015 Alexander Lamaison +# Copyright (c) 2014-2016 Alexander Lamaison # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided @@ -47,22 +47,45 @@ check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) check_include_files(arpa/inet.h HAVE_ARPA_INET_H) check_include_files(windows.h HAVE_WINDOWS_H) check_include_files(winsock2.h HAVE_WINSOCK2_H) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/libssh2_config_cmake.h.in - ${CMAKE_CURRENT_BINARY_DIR}/libssh2_config.h) + "${CMAKE_CURRENT_SOURCE_DIR}/libssh2_config_cmake.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/libssh2_config.h") +append_needed_socket_libraries(LIBRARIES) set(TESTS - simple - ssh2) + hostkey + hostkey_hash + password_auth_succeeds_with_correct_credentials + password_auth_fails_with_wrong_password + password_auth_fails_with_wrong_username + public_key_auth_fails_with_wrong_key + public_key_auth_succeeds_with_correct_rsa_key + public_key_auth_succeeds_with_correct_dsa_key + keyboard_interactive_auth_fails_with_wrong_response + keyboard_interactive_auth_succeeds_with_correct_response + ) -append_needed_socket_libraries(LIBRARIES) +add_library(openssh_fixture STATIC openssh_fixture.h openssh_fixture.c) +target_link_libraries(openssh_fixture ${LIBRARIES}) +target_include_directories(openssh_fixture PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + +add_library(session_fixture STATIC session_fixture.h session_fixture.c) +target_link_libraries(session_fixture ${LIBRARIES} openssh_fixture libssh2) +target_include_directories(session_fixture PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + +add_library(runner STATIC runner.c) +target_link_libraries(runner session_fixture) +target_include_directories(runner PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") foreach(test ${TESTS}) - add_executable(test-${test} ${test}.c) - target_link_libraries(test-${test} libssh2 ${LIBRARIES}) - target_include_directories(test-${test} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - list(APPEND TEST_TARGETS test-${test}) + add_executable(test_${test} test_${test}.c) + target_link_libraries(test_${test} libssh2 runner ${LIBRARIES}) + target_include_directories(test_${test} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + list(APPEND TEST_TARGETS test_${test}) + + add_test( + NAME test_${test} COMMAND $ + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endforeach() add_target_to_copy_dependencies( @@ -70,6 +93,7 @@ add_target_to_copy_dependencies( DEPENDENCIES ${RUNTIME_DEPENDENCIES} BEFORE_TARGETS ${TEST_TARGETS}) + # TODO convert mansyntax.sh into CMake script. # XXX Just because we can find all three programs, doesn't mean sh can # find man and grep @@ -82,30 +106,3 @@ if(SH_EXECUTABLE AND MAN_EXECUTABLE AND GREP_EXECUTABLE) set(cmd "${cmd} ${CMAKE_CURRENT_SOURCE_DIR}/mansyntax.sh") add_test(mansyntax ${SH_EXECUTABLE} -c "${cmd}") endif() - -add_test(simple test-simple) - -find_program(SSHD_EXECUTABLE sshd) -find_program(CHMOD_EXECUTABLE chmod) -find_program(KILL_EXECUTABLE kill) -mark_as_advanced(SSHD_EXECUTABLE CHMOD_EXECUTABLE KILL_EXECUTABLE) -if(SSHD_EXECUTABLE AND CHMOD_EXECUTABLE AND KILL_EXECUTABLE) - set(SSHD_TEST_CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}) - set(TEST_NAME ssh2) - - add_custom_command( - TARGET test-${TEST_NAME} - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/etc - ${SSHD_TEST_CONFIG_DIR}/etc) - - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/sshd_fixture.sh.in - ${CMAKE_CURRENT_BINARY_DIR}/test-${TEST_NAME}_fixture.sh - @ONLY) - - add_test(NAME ssh2 COMMAND ${SH_EXECUTABLE} - ${CMAKE_CURRENT_BINARY_DIR}/test-${TEST_NAME}_fixture.sh - $) - -endif() diff --git a/tests/Makefile.am b/tests/Makefile.am index 67854ccc05..3c3745c680 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -19,3 +19,15 @@ TESTS_ENVIRONMENT += srcdir=$(top_srcdir)/tests builddir=$(top_builddir)/tests EXTRA_DIST = ssh2.sh mansyntax.sh EXTRA_DIST += etc/host etc/host.pub etc/user etc/user.pub EXTRA_DIST += CMakeLists.txt libssh2_config_cmake.h.in sshd_fixture.sh.in +EXTRA_DIST += key_dsa key_dsa.pub key_dsa_wrong key_dsa_wrong.pub key_rsa key_rsa.pub +EXTRA_DIST += openssh_server/authorized_keys openssh_server/Dockerfile openssh_server/ssh_host_rsa_key +EXTRA_DIST += openssh_fixture.c openssh_fixture.h runner.c session_fixture.c session_fixture.h +EXTRA_DIST += test_hostkey.c test_hostkey_hash.c +EXTRA_DIST += test_keyboard_interactive_auth_fails_with_wrong_response.c +EXTRA_DIST += test_keyboard_interactive_auth_succeeds_with_correct_response.c +EXTRA_DIST += test_password_auth_fails_with_wrong_password.c +EXTRA_DIST += test_password_auth_fails_with_wrong_username.c +EXTRA_DIST += test_password_auth_succeeds_with_correct_credentials.c +EXTRA_DIST += test_public_key_auth_fails_with_wrong_key.c +EXTRA_DIST += test_public_key_auth_succeeds_with_correct_dsa_key.c +EXTRA_DIST += test_public_key_auth_succeeds_with_correct_rsa_key.c diff --git a/tests/key_dsa b/tests/key_dsa new file mode 100644 index 0000000000..c8bc98df8e --- /dev/null +++ b/tests/key_dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBvAIBAAKBgQCtiYdgpPvFtfi7Ba44DiB+1x8kojjT0nRvn2hU2aa4p4fXI8kd +6Hc57VQO/lLhR9eFpxjP7m+jGwF468Q6NU8xiC71ucep0OoXS7u8RcoIoWfLDtZi +DDlahnZTW04mB5fFxo2y7dYl31vE4TPdSxhqpkvnIBIstMFh2M7Dl0w8/QIVAP95 +u6dg1OW6gGsRgiircsy1A9tzAoGBAIzwc5FCnJnzAJm9Hjv0AFV5l/i/DQulZ9pu +EILkNiHCfDR+lTJ8VxAR7J3pgjmvYzeeRvi519ez1YriktDt66kIknQOcHB8ghyg +U+dff79SkDcpg8LnX5xb3cVMgABujA0sSpaW1wwm64RXdvmoQvWu6ympUT0l0dEd +oYVkb4ytAoGAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6 +FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbU +frESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7GooCFQCyaA6T +tkJunHP+F0Xg/WAUV6tcqA== +-----END DSA PRIVATE KEY----- diff --git a/tests/key_dsa.pub b/tests/key_dsa.pub new file mode 100644 index 0000000000..6a247a514b --- /dev/null +++ b/tests/key_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAK2Jh2Ck+8W1+LsFrjgOIH7XHySiONPSdG+faFTZprinh9cjyR3odzntVA7+UuFH14WnGM/ub6MbAXjrxDo1TzGILvW5x6nQ6hdLu7xFygihZ8sO1mIMOVqGdlNbTiYHl8XGjbLt1iXfW8ThM91LGGqmS+cgEiy0wWHYzsOXTDz9AAAAFQD/ebunYNTluoBrEYIoq3LMtQPbcwAAAIEAjPBzkUKcmfMAmb0eO/QAVXmX+L8NC6Vn2m4QguQ2IcJ8NH6VMnxXEBHsnemCOa9jN55G+LnX17PViuKS0O3rqQiSdA5wcHyCHKBT519/v1KQNymDwudfnFvdxUyAAG6MDSxKlpbXDCbrhFd2+ahC9a7rKalRPSXR0R2hhWRvjK0AAACAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbUfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7Goo= awl03@bounty diff --git a/tests/key_dsa_wrong b/tests/key_dsa_wrong new file mode 100644 index 0000000000..5700502289 --- /dev/null +++ b/tests/key_dsa_wrong @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQCE1v/lL1VvjlJMyG7q0wAgl2tqVMzy5h1RVOtDS8bTlXLJg7ks +T63wTmXlp2HedgKkfHCu7AKsjPyg1CTrvRBa8BFEvMoUDARonMwql34aiKVMy/t0 +/ehnmCQV+ZMFpsVFnphJpZuXLTW1F3pnEbSNud5sACjbWb51uly5AUynuwIVAOhj +rbNOaAtC1oYki8CVwpkQ8rHhAoGAYSepXRF3GJSjseYgJ2bCgcJS0L9agcvKAf+F +dc+ZDJOchhnZC/hGHsjAfg62KowwKuOYsbcR3S4LJxiERcmRabww+kUIL1E8bLaQ +RbOygNsHU8LyBdSx3WqC2WEOpVkTAjYDWTkbN+qkb53IBoI0GwFt5P9GHvQcAGkj +GJQAWWYCgYAt7vxpDC5Xs6GxbaUupfIP95ZTMx2LqqFjqfT/81nypIHVyIlCnWMi +a0mWGe4qXmHSyk6ZYnsk7Ll6WxdwUrFhd75qERyXlRK2x/v/Q3h9IOwChpHdSFx/ +Tq1Zl9vMx3tmS1H0YF9tUdN7g8S5XTUSvYA+0Lzxs/9zOU5fa55+pAIVAKV45RLf +hg2GNXvO68Q4tt3F6kSP +-----END DSA PRIVATE KEY----- diff --git a/tests/key_dsa_wrong.pub b/tests/key_dsa_wrong.pub new file mode 100644 index 0000000000..107046a637 --- /dev/null +++ b/tests/key_dsa_wrong.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAITW/+UvVW+OUkzIburTACCXa2pUzPLmHVFU60NLxtOVcsmDuSxPrfBOZeWnYd52AqR8cK7sAqyM/KDUJOu9EFrwEUS8yhQMBGiczCqXfhqIpUzL+3T96GeYJBX5kwWmxUWemEmlm5ctNbUXemcRtI253mwAKNtZvnW6XLkBTKe7AAAAFQDoY62zTmgLQtaGJIvAlcKZEPKx4QAAAIBhJ6ldEXcYlKOx5iAnZsKBwlLQv1qBy8oB/4V1z5kMk5yGGdkL+EYeyMB+DrYqjDAq45ixtxHdLgsnGIRFyZFpvDD6RQgvUTxstpBFs7KA2wdTwvIF1LHdaoLZYQ6lWRMCNgNZORs36qRvncgGgjQbAW3k/0Ye9BwAaSMYlABZZgAAAIAt7vxpDC5Xs6GxbaUupfIP95ZTMx2LqqFjqfT/81nypIHVyIlCnWMia0mWGe4qXmHSyk6ZYnsk7Ll6WxdwUrFhd75qERyXlRK2x/v/Q3h9IOwChpHdSFx/Tq1Zl9vMx3tmS1H0YF9tUdN7g8S5XTUSvYA+0Lzxs/9zOU5fa55+pA== awl03@bounty diff --git a/tests/key_rsa b/tests/key_rsa new file mode 100644 index 0000000000..d9643c44df --- /dev/null +++ b/tests/key_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJ +evi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBp +vceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5 +RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNX +b33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYIL +jzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3QIBIwKCAQB1Hpyhoi2LXCIVfXPM +AU6AtWvRY12PtdSl8uqr+nX2JATNBZlUCTaUE6qQJNxEZyDeMNvzZdxV5gkzQ2Fi +TpQIyRddbH01fJKoTxzlHzbLfAeCj9mFqicahOkHAMN8K6Ddqxe89zhD60w0SgjW +91tLzZQ2sxE70RxBdPQOpbaZLxmUZSVxRgf5djotyZqB4CcGblKCEZYJ9ZemgCnF +gEcSsqcn0Jxfu+aEJ4WinN2orWs+okfgsUu9G9Ozwcy9Ptq1LkIzcwwTIpL7TTDd +LMvhql39a07SysepjFRHxjvXh8Gv+SsLvKQPJHheVv8XoG0dZd+9/Eden9rHKoVm +vGPLAoGBANGDQtv5K/md/3sRGeJ6Ir3/Ao+WMe8C5onck+hW4y/2yQqm3ZLzyZon +KdWRj2q4dnxFZyoyDgX0UHLpM4aSsMRjn4C6vcPLcYaZ9CGB5FWPGZrq+q6vuMGK +V9/fo4ZNFkNK3wo4WCSgxC1Y8XUJc3klOvPVjsmVxZaeZnkukkAFAoGBAMCkqe/S +hrKITzjZuyGN90a2Nq+3xMNGuc400Qvoi27D1OcSn7SJ/K3tVWbENOH3CAlkmlZT +46IM2SRRmM0bxF3aThEwnsD5yPqgz+tcweX+gK3nXnP5JZfYF1kArXk80/eYhNE9 +PwnJNXDQMoxaM0/X6BVgQyt03/Q12lH9u0j5AoGAR9U7fp6Su/uoDO/rnhs/HJHy +P9u5WULSsuyKe4uBF8JTjp+cbOXeuIJ0vkCI8WPQ2iZsg37gPI5Hd9rtGDJLPATm +OsOuxslowG9MY0J6K/aMb6EFfbiXHckIL3/gS02hO6SkPgSwgZY0odVaGX+VThtk +q18ppDNZr/vLXL+CmZsCgYEAlJxIlG80tZxaXw5dKIN1nPL2/JUUIZz1vFShQ7Nk +P4EglP+9B52lqr5mc9kwHAe1vhpobns6kvP393IlawbKrsz6ZQg/8/PkLw5XQIli +YPeH1pyKsTyKtvcn9DO5BcE1zaGLB9ApULEpOcUuTwPBLvcDfjRREuUhywT44Coi +w0MCgYAX5yc7/Z3R6M30rGsrgb1Y2siHYsi2LCygUj7TDGQYpaZN4afPJOT5H/Nr +7x7dgZkbOR6PQFm00VgML0XxKih59t0dcQ+2qk1LX5JDKRF/1kER3np6dpceteDu +cC+MEHB/KvijnviAtBZGvD0O7oZgvbkKHESu2igXpAnfXPZFvw== +-----END RSA PRIVATE KEY----- diff --git a/tests/key_rsa.pub b/tests/key_rsa.pub new file mode 100644 index 0000000000..0417e8e6f4 --- /dev/null +++ b/tests/key_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBpvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNXb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYILjzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3Q== awl03@bounty diff --git a/tests/libssh2_config_cmake.h.in b/tests/libssh2_config_cmake.h.in index 35625af0d4..9bab1d5ae8 100644 --- a/tests/libssh2_config_cmake.h.in +++ b/tests/libssh2_config_cmake.h.in @@ -41,3 +41,30 @@ #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_WINDOWS_H #cmakedefine HAVE_WINSOCK2_H +#cmakedefine HAVE_SNPRINTF + +/* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible. + We provide a safe wrapper if snprintf not found */ +#ifndef HAVE_SNPRINTF +#include +#include +/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1 +* then assume cp is pointing to a null char and do nothing. Returns number +* number of chars placed in cp excluding the trailing null char. So for +* cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len +* <= 0 the return value is 0 (and no chars are written to cp). */ +static int snprintf(char *cp, int cp_max_len, const char *fmt, ...) +{ + va_list args; + int n; + + if (cp_max_len < 2) + return 0; + va_start(args, fmt); + n = vsnprintf(cp, cp_max_len, fmt, args); + va_end(args); + return (n < cp_max_len) ? n : (cp_max_len - 1); +} + +#define HAVE_SNPRINTF +#endif diff --git a/tests/openssh_fixture.c b/tests/openssh_fixture.c new file mode 100644 index 0000000000..1d791e8809 --- /dev/null +++ b/tests/openssh_fixture.c @@ -0,0 +1,315 @@ +/* Copyright (C) 2016 Alexander Lamaison + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "openssh_fixture.h" +#include "libssh2_config.h" + +#ifdef HAVE_WINSOCK2_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include + +static int run_command(const char *command, char **output) +{ + FILE *pipe; + char command_buf[BUFSIZ]; + int ret; + if (output) { + *output = NULL; + } + + /* Rewrite the command to redirect stderr to stdout to we can output it */ + ret = snprintf(command_buf, sizeof(command_buf), "%s 2>&1", command); + if (ret < 0 || ret >= BUFSIZ) { + fprintf(stderr, "Unable to format command (%s)\n", command); + return -1; + } + + fprintf(stdout, "Command: %s\n", command); +#ifdef WIN32 + pipe = _popen(command_buf, "r"); +#else + pipe = popen(command_buf, "r"); +#endif + if (pipe) { + char buf[BUFSIZ]; + char *p = buf; + while (fgets(p, sizeof(buf) - (p - buf), pipe) != NULL) + ; + +#ifdef WIN32 + ret = _pclose(pipe); +#else + ret = pclose(pipe); +#endif + if (ret == 0) { + if (output) { + /* command output may contain a trailing newline, so we trim + * whitespace here */ + size_t end = strlen(buf) - 1; + while (end > 0 && isspace(buf[end])) { + buf[end] = '\0'; + } + + *output = strdup(buf); + } + } + else { + fprintf(stderr, "Error running command '%s' (exit %d): %s\n", + command, ret, buf); + } + return ret; + } + else { + fprintf(stderr, "Unable to execute command '%s'\n", command); + return -1; + } +} + +static int build_openssh_server_docker_image() +{ + return run_command("docker build -t libssh2/openssh_server openssh_server", + NULL); +} + +static int start_openssh_server(char **container_id_out) +{ + return run_command("docker run --detach -P libssh2/openssh_server", + container_id_out); +} + +static int stop_openssh_server(char *container_id) +{ + char command_buf[BUFSIZ]; + int rc = snprintf(command_buf, sizeof(command_buf), "docker stop %s", + container_id); + if (rc > -1 && rc < BUFSIZ) { + return run_command(command_buf, NULL); + } + else { + return rc; + } +} + +static const char *docker_machine_name() +{ + return getenv("DOCKER_MACHINE_NAME"); +} + +static int ip_address_from_container(char *container_id, char **ip_address_out) +{ + const char *active_docker_machine = docker_machine_name(); + if (active_docker_machine != NULL) { + + // This can be flaky when tests run in parallel (see + // https://github.com/docker/machine/issues/2612), so we retry a few + // times with exponential backoff if it fails + int attempt_no = 0; + int wait_time = 500; + for (;;) { + char command_buf[BUFSIZ]; + int rc = snprintf(command_buf, sizeof(command_buf), + "docker-machine ip %s", active_docker_machine); + if (rc > -1 && rc < BUFSIZ) { + return run_command(command_buf, ip_address_out); + } + + if (attempt_no > 5) { + fprintf( + stderr, + "Unable to get IP from docker-machine after %d attempts\n", + attempt_no); + return -1; + } + else { +#ifdef WIN32 +#pragma warning(push) +#pragma warning(disable : 4996) + _sleep(wait_time); +#pragma warning(pop) +#else + sleep(wait_time); +#endif + ++attempt_no; + wait_time *= 2; + } + } + } + else { + char command_buf[BUFSIZ]; + int rc = snprintf( + command_buf, sizeof(command_buf), + "docker inspect --format \"{{ index (index (index " + ".NetworkSettings.Ports \\\"22/tcp\\\") 0) \\\"HostIp\\\" }}\" %s", + container_id); + if (rc > -1 && rc < BUFSIZ) { + return run_command(command_buf, ip_address_out); + } + else { + return rc; + } + } +} + +static int port_from_container(char *container_id, char **port_out) +{ + char command_buf[BUFSIZ]; + int rc = snprintf( + command_buf, sizeof(command_buf), + "docker inspect --format \"{{ index (index (index " + ".NetworkSettings.Ports \\\"22/tcp\\\") 0) \\\"HostPort\\\" }}\" %s", + container_id); + if (rc > -1 && rc < BUFSIZ) { + return run_command(command_buf, port_out); + } + else { + return rc; + } +} + +static int open_socket_to_container(char *container_id) +{ + char *ip_address = NULL; + + int ret = ip_address_from_container(container_id, &ip_address); + if (ret == 0) { + char *port_string = NULL; + ret = port_from_container(container_id, &port_string); + if (ret == 0) { + unsigned long hostaddr = inet_addr(ip_address); + if (hostaddr != (unsigned long)(-1)) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock > -1) { + struct sockaddr_in sin; + + sin.sin_family = AF_INET; + sin.sin_port = htons((short)strtol(port_string, NULL, 0)); + sin.sin_addr.s_addr = hostaddr; + + if (connect(sock, (struct sockaddr *)(&sin), + sizeof(struct sockaddr_in)) == 0) { + ret = sock; + } + else { + fprintf(stderr, "Failed to connect to %s:%s\n", + ip_address, port_string); + ret = -1; + } + } + else { + fprintf(stderr, "Failed to open socket (%d)\n", sock); + ret = -1; + } + } + else { + fprintf(stderr, "Failed to convert %s host address\n", + ip_address); + ret = -1; + } + + free(port_string); + } + else { + fprintf(stderr, "Failed to get port for container %s\n", + container_id); + ret = -1; + } + + free(ip_address); + } + else { + fprintf(stderr, "Failed to get IP address for container %s\n", + container_id); + ret = -1; + } + + return ret; +} + +static char *running_container_id = NULL; + +int start_openssh_fixture() +{ + int ret; +#ifdef HAVE_WINSOCK2_H + WSADATA wsadata; + + ret = WSAStartup(MAKEWORD(2, 0), &wsadata); + if (ret != 0) { + fprintf(stderr, "WSAStartup failed with error: %d\n", ret); + return 1; + } +#endif + + ret = build_openssh_server_docker_image(); + if (ret == 0) { + return start_openssh_server(&running_container_id); + } + else { + fprintf(stderr, "Failed to build docker image\n"); + return ret; + } +} + +void stop_openssh_fixture() +{ + if (running_container_id) { + stop_openssh_server(running_container_id); + free(running_container_id); + running_container_id = NULL; + } + else { + fprintf(stderr, "Cannot stop container - none started"); + } +} + +int open_socket_to_openssh_server() +{ + return open_socket_to_container(running_container_id); +} diff --git a/tests/openssh_fixture.h b/tests/openssh_fixture.h new file mode 100644 index 0000000000..2d2bb0adce --- /dev/null +++ b/tests/openssh_fixture.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2016 Alexander Lamaison + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef LIBSSH2_TESTS_OPENSSH_FIXTURE_H +#define LIBSSH2_TESTS_OPENSSH_FIXTURE_H + +int start_openssh_fixture(); +void stop_openssh_fixture(); +int open_socket_to_openssh_server(); + +#endif diff --git a/tests/openssh_server/Dockerfile b/tests/openssh_server/Dockerfile new file mode 100644 index 0000000000..2848106263 --- /dev/null +++ b/tests/openssh_server/Dockerfile @@ -0,0 +1,74 @@ +# Copyright (c) 2016 Alexander Lamaison +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# Redistributions of source code must retain the above +# copyright notice, this list of conditions and the +# following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials +# provided with the distribution. +# +# Neither the name of the copyright holder nor the names +# of any other contributors may be used to endorse or +# promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. + +FROM debian:jessie + +RUN apt-get update \ + && apt-get install -y openssh-server \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +RUN mkdir /var/run/sshd + +# Chmodding because, when building on Windows, files are copied in with +# -rwxr-xr-x permissions. +# +# Copying to a temp location, then moving because chmodding the copied file has +# no effect (Docker AUFS-related bug maybe?) +COPY ssh_host_rsa_key /tmp/etc/ssh/ssh_host_rsa_key +RUN mv /tmp/etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key +RUN chmod 600 /etc/ssh/ssh_host_rsa_key + +RUN adduser --disabled-password --gecos 'Test user for libssh2 integration tests' libssh2 +RUN echo 'libssh2:my test password' | chpasswd + +RUN sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config + +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + +USER libssh2 + +RUN mkdir -p /home/libssh2/.ssh +RUN mkdir -p /home/libssh2/sandbox + +COPY authorized_keys /tmp/libssh2/.ssh/authorized_keys +RUN cp /tmp/libssh2/.ssh/authorized_keys /home/libssh2/.ssh/authorized_keys +RUN chmod 600 /home/libssh2/.ssh/authorized_keys + +USER root + +EXPOSE 22 +# -e gives logs via 'docker logs' +CMD ["/usr/sbin/sshd", "-D", "-e"] diff --git a/tests/openssh_server/authorized_keys b/tests/openssh_server/authorized_keys new file mode 100644 index 0000000000..3ae35e6d4b --- /dev/null +++ b/tests/openssh_server/authorized_keys @@ -0,0 +1,2 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAK2Jh2Ck+8W1+LsFrjgOIH7XHySiONPSdG+faFTZprinh9cjyR3odzntVA7+UuFH14WnGM/ub6MbAXjrxDo1TzGILvW5x6nQ6hdLu7xFygihZ8sO1mIMOVqGdlNbTiYHl8XGjbLt1iXfW8ThM91LGGqmS+cgEiy0wWHYzsOXTDz9AAAAFQD/ebunYNTluoBrEYIoq3LMtQPbcwAAAIEAjPBzkUKcmfMAmb0eO/QAVXmX+L8NC6Vn2m4QguQ2IcJ8NH6VMnxXEBHsnemCOa9jN55G+LnX17PViuKS0O3rqQiSdA5wcHyCHKBT519/v1KQNymDwudfnFvdxUyAAG6MDSxKlpbXDCbrhFd2+ahC9a7rKalRPSXR0R2hhWRvjK0AAACAJ+CGwV/1S4j1GVwa6pSP0nj4V86GWXosTTBg7GT+rKWu8lrxIcr6FzLWgFi/gHoMrgnKWGxO1yF7vkoYM5Yfo84oBYiH+MgpiBuOrZrgzacHsA66JJbUfrESRFWZl2blIPr6Gyjj6cVGgMabK3yCiTRi0v7hwffpm0rKyKv7Goo= awl03@bounty +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAnak1T7zHJ+hVRFBDQ9pf1KVzmd5gaNc7y7NPmL13aOG3sYeJevi1x1WM/R3tb8XnUnzZUX9GJN0MYovvZsw9bknG1mDP72LFbGp/gzPddGIKHBBpvceDaJ85sM/ME3XOtD7uuXQuNAuEHwEzSMMiSIEMcQS+lXIcMLr5xPLEkyNvqsO5RqSjMTLHKHgY8gLWx7oQ1avokhwuDxF7P3Pqtj+rW2Te6vR0i1H6EyFPsBkzkgNXb33cus8M1CnTmYTSgJgmHO2LLcGpjQ5sL8T/PWIWHaSqTnkrFXEMysgoteXnAYILjzyBaqq2WV4KA3TluGdAP2p8gC32QtKmIuis3Q== awl03@bounty diff --git a/tests/openssh_server/ssh_host_rsa_key b/tests/openssh_server/ssh_host_rsa_key new file mode 100644 index 0000000000..ba0088c4ec --- /dev/null +++ b/tests/openssh_server/ssh_host_rsa_key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKUUoR6/Om6 +cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7BAm99 +fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB +eNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMA +wQU9i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQK +ypq7J4iQgkIIbCU1A31+4ExvcIVoxLQw/aTSbwIBIwKCAQAd9Cu9heWrs+UAinvv +Iwmq/EhnDGQijJoOt1zEMrpXSekyq7mQDgN0SZdJLPeSlSRQ5nVq5/dZroYB3A5i +E7N3F7nibcJskWq5rcMyGjQHwod8wqfMiGcL6mjeZu2jLXprm0NDpJ3DyicbCA2G +EhnpoHmktIBE5FsslI/nHer2o6OA/kVWSEjak+pvI1pm22T8QOBBfY0yAX7B0ebk +8o4lB4cdLf3In7Q0ahpHNOwIPdRvQ2c4Tm/DcfUBkTW2ZYGUd45cFsyHqXZscNNy +GX2Wcy/FLEvQ6zBFJsNLpxCYsUyBxfSDygn9dx9RQfiWFXjdRaRPpyRAr+BTXkLU +yvabAoGBANt7sxfjvu/SLkRc7TnBoJ0h/AL7Mcuu9PJmOnis4boyF9ZxqbiRiS3J +yK+EKxfC0S+xf5WJ5uf7dVGnOXHXKaRl4xH90iRtryNlvtILZwHw1DTqRFxv9jtz +tTRrYMEHAnMKzadgDfV/lv4iJ6nwFzK76GQ7RQNZYiGTMEh3pUNjAoGBAMvNLGpz +FxhpIh+fVvRjawKgGVP87T482WOUdsF18EEPFMe6D7DO5xpLuJi+C7QkvMI8WjvD +/3RGvaSh9Wt7ikLZpeogiSJy121HsEqheTR5hTx2t72ClrjZvIhLbQMRu6PqGPu/ +HOC2urEGGYm7O2vnftwpuG3zIVVLM2KstPCFAoGBAM7w+VEJ7opYdMQdGi8kRvqN +wbmrAxCAY0ryrCijALbexgS0T5DDu9q28Gr49W4stpquq35dc0/BNBnJje7+EVHc +aGFrqOCErHHU9b66Sy23LnsIxBykFAwrRHNAq66u1mx35nk9Tv1pq58nhHun21u4 +fAa7ijZblwm2qd3tJsqBAoGAEXf8ficfPJtMEVbM8GBLADmbxV7Sga1xuBQKLdbo +tR6MwKmMUPvKqnuE2eRnZzZZUnoznrkHRHsXkcS9Q7ohyzc6G2Hf3mGdb8RQ8HQ9 +lsiWZESwqdf+SlvOVNND27EQFV01V2gnC/JnxgfWTaJVjOf07ky4CWycdQZyHmaT +Ko8CgYB58jOyXMdo2ggOCG/HX2H92KPPpFUBFCX27fCue8BZLD5quIltpXupx5oj +EyltgvPcmNDgvdSadkHvP5s6nykS+n5we+d9yIIJF/BfETWsXjR3ooip+trqiirw +0aHqUDFcYn9unm2wtrMYYViiDLRijNwLZ2sG0JIU4JHyseh+NA== +-----END RSA PRIVATE KEY----- diff --git a/tests/runner.c b/tests/runner.c new file mode 100644 index 0000000000..efd05ef4da --- /dev/null +++ b/tests/runner.c @@ -0,0 +1,51 @@ +/* Copyright (C) 2016 Alexander Lamaison + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "session_fixture.h" + +extern int test(LIBSSH2_SESSION *session); + +int main() +{ + int exit_code = 1; + LIBSSH2_SESSION *session = start_session_fixture(); + if (session != NULL) { + exit_code = (test(session) == 0) ? 0 : 1; + } + stop_session_fixture(); + return exit_code; +} diff --git a/tests/session_fixture.c b/tests/session_fixture.c new file mode 100644 index 0000000000..6985275e60 --- /dev/null +++ b/tests/session_fixture.c @@ -0,0 +1,133 @@ +/* Copyright (C) 2016 Alexander Lamaison + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "session_fixture.h" +#include "libssh2_config.h" +#include "openssh_fixture.h" + +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif +#ifdef HAVE_WINSOCK2_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +LIBSSH2_SESSION *connected_session = NULL; +int connected_socket = -1; + +static int connect_to_server() +{ + connected_socket = open_socket_to_openssh_server(); + if (connected_socket > -1) { + int rc = libssh2_session_handshake(connected_session, connected_socket); + if (rc == 0) { + return 0; + } + else { + print_last_session_error("libssh2_session_handshake"); + return -1; + } + } + else { + return -1; + } +} + +LIBSSH2_SESSION *start_session_fixture() +{ + int rc = start_openssh_fixture(); + if (rc == 0) { + rc = libssh2_init(0); + if (rc == 0) { + connected_session = libssh2_session_init_ex(NULL, NULL, NULL, NULL); + libssh2_session_set_blocking(connected_session, 1); + if (connected_session != NULL) { + rc = connect_to_server(); + if (rc == 0) { + return connected_session; + } + else { + return NULL; + } + } + else { + fprintf(stderr, "libssh2_session_init_ex failed\n"); + return NULL; + } + } + else { + fprintf(stderr, "libssh2_init failed (%d)\n", rc); + return NULL; + } + } + else { + return NULL; + } +} + +void print_last_session_error(const char *function) +{ + if (connected_session) { + char *message; + int rc = + libssh2_session_last_error(connected_session, &message, NULL, 0); + fprintf(stderr, "%s failed (%d): %s\n", function, rc, message); + } + else { + fprintf(stderr, "No session"); + } +} + +void stop_session_fixture() +{ + if (connected_session) { + libssh2_session_disconnect(connected_session, "test ended"); + libssh2_session_free(connected_session); + shutdown(connected_socket, 2); + connected_session = NULL; + } + else { + fprintf(stderr, "Cannot stop session - none started"); + } + + stop_openssh_fixture(); +} diff --git a/tests/session_fixture.h b/tests/session_fixture.h new file mode 100644 index 0000000000..d3824b7974 --- /dev/null +++ b/tests/session_fixture.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2016 Alexander Lamaison + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef LIBSSH2_TESTS_SESSION_FIXTURE_H +#define LIBSSH2_TESTS_SESSION_FIXTURE_H + +#include + +LIBSSH2_SESSION *start_session_fixture(); +void stop_session_fixture(); +void print_last_session_error(const char *function); + +#endif diff --git a/tests/test_hostkey.c b/tests/test_hostkey.c new file mode 100644 index 0000000000..63c2063f2b --- /dev/null +++ b/tests/test_hostkey.c @@ -0,0 +1,54 @@ +#include "session_fixture.h" + +#include + +#include + +const char *EXPECTED_HOSTKEY = + "AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKU" + "UoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7B" + "Am99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB" + "eNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMAwQU9" + "i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQ" + "gkIIbCU1A31+4ExvcIVoxLQw/aTSbw=="; + +int test(LIBSSH2_SESSION *session) +{ + int rc; + size_t len; + int type; + unsigned int expected_len = 0; + char *expected_hostkey = NULL; + + const char *hostkey = libssh2_session_hostkey(session, &len, &type); + if (hostkey == NULL) { + print_last_session_error("libssh2_session_hostkey"); + return 1; + } + + if (type != LIBSSH2_HOSTKEY_TYPE_RSA) { + /* Hostkey configured in docker container is RSA */ + fprintf(stderr, "Wrong type of hostkey\n"); + return 1; + } + + rc = libssh2_base64_decode(session, &expected_hostkey, &expected_len, + EXPECTED_HOSTKEY, strlen(EXPECTED_HOSTKEY)); + if (rc != 0) { + print_last_session_error("libssh2_base64_decode"); + return 1; + } + + if (len != expected_len) { + fprintf(stderr, "Hostkey does not have the expected length %ld != %d\n", + len, expected_len); + return 1; + } + + if (memcmp(hostkey, expected_hostkey, len) != 0) { + fprintf(stderr, "Hostkeys do not match\n"); + return 1; + } + + return 0; +} diff --git a/tests/test_hostkey_hash.c b/tests/test_hostkey_hash.c new file mode 100644 index 0000000000..6fb78d9e2e --- /dev/null +++ b/tests/test_hostkey_hash.c @@ -0,0 +1,74 @@ +#include "session_fixture.h" +#include "libssh2_config.h" + +#include + +#include + +const char *EXPECTED_HOSTKEY = + "AAAAB3NzaC1yc2EAAAABIwAAAQEArrr/JuJmaZligyfS8vcNur+mWR2ddDQtVdhHzdKU" + "UoR6/Om6cvxpe61H1YZO1xCpLUBXmkki4HoNtYOpPB2W4V+8U4BDeVBD5crypEOE1+7B" + "Am99fnEDxYIOZq2/jTP0yQmzCpWYS3COyFmkOL7sfX1wQMeW5zQT2WKcxC6FSWbhDqrB" + "eNEGi687hJJoJ7YXgY/IdiYW5NcOuqRSWljjGS3dAJsHHWk4nJbhjEDXbPaeduMAwQU9" + "i6ELfP3r+q6wdu0P4jWaoo3De1aYxnToV/ldXykpipON4NPamsb6Ph2qlJQKypq7J4iQ" + "gkIIbCU1A31+4ExvcIVoxLQw/aTSbw=="; + +const char *EXPECTED_MD5_HASH_DIGEST = "0C0ED1A5BB10275F76924CE187CE5C5E"; + +const char *EXPECTED_SHA1_HASH_DIGEST = + "F3CD59E2913F4422B80F7B0A82B2B89EAE449387"; + +const int MD5_HASH_SIZE = 16; +const int SHA1_HASH_SIZE = 20; + +static void calculate_digest(const char *hash, size_t hash_len, char *buffer, + size_t buffer_len) +{ + size_t i; + char *p = buffer; + char *end = buffer + buffer_len; + + for (i = 0; i < hash_len && p < end; ++i) { + p += snprintf(p, end - p, "%02X", (unsigned char)hash[i]); + } +} + +int test(LIBSSH2_SESSION *session) +{ + char buf[BUFSIZ]; + + const char *md5_hash; + const char *sha1_hash; + + md5_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5); + if (md5_hash == NULL) { + print_last_session_error( + "libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_MD5)"); + return 1; + } + + calculate_digest(md5_hash, MD5_HASH_SIZE, buf, BUFSIZ); + + if (strcmp(buf, EXPECTED_MD5_HASH_DIGEST) != 0) { + fprintf(stderr, "MD5 hash not as expected - digest %s != %s\n", buf, + EXPECTED_MD5_HASH_DIGEST); + return 1; + } + + sha1_hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); + if (sha1_hash == NULL) { + print_last_session_error( + "libssh2_hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1)"); + return 1; + } + + calculate_digest(sha1_hash, SHA1_HASH_SIZE, buf, BUFSIZ); + + if (strcmp(buf, EXPECTED_SHA1_HASH_DIGEST) != 0) { + fprintf(stderr, "SHA1 hash not as expected - digest %s != %s\n", buf, + EXPECTED_SHA1_HASH_DIGEST); + return 1; + } + + return 0; +} diff --git a/tests/test_keyboard_interactive_auth_fails_with_wrong_response.c b/tests/test_keyboard_interactive_auth_fails_with_wrong_response.c new file mode 100644 index 0000000000..beb6608ee9 --- /dev/null +++ b/tests/test_keyboard_interactive_auth_fails_with_wrong_response.c @@ -0,0 +1,59 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *WRONG_PASSWORD = "i'm not the password"; + +static void kbd_callback(const char *name, int name_len, + const char *instruction, int instruction_len, + int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract) +{ + int i; + (void)abstract; + fprintf(stdout, "Kb-int name: %.*s\n", name_len, name); + fprintf(stdout, "Kb-int instruction: %.*s\n", instruction_len, instruction); + for (i = 0; i < num_prompts; ++i) { + fprintf(stdout, "Kb-int prompt %d: %.*s\n", i, prompts[i].length, + prompts[i].text); + } + + if (num_prompts == 1) { + responses[0].text = strdup(WRONG_PASSWORD); + responses[0].length = strlen(WRONG_PASSWORD); + } +} + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "keyboard-interactive") == NULL) { + fprintf(stderr, + "'keyboard-interactive' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_keyboard_interactive_ex( + session, USERNAME, strlen(USERNAME), kbd_callback); + if (rc == 0) { + fprintf(stderr, + "Keyboard-interactive auth succeeded with wrong response\n"); + return 1; + } + + return 0; +} diff --git a/tests/test_keyboard_interactive_auth_succeeds_with_correct_response.c b/tests/test_keyboard_interactive_auth_succeeds_with_correct_response.c new file mode 100644 index 0000000000..aec1dd496d --- /dev/null +++ b/tests/test_keyboard_interactive_auth_succeeds_with_correct_response.c @@ -0,0 +1,59 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *PASSWORD = "my test password"; /* configured in Dockerfile */ + +static void kbd_callback(const char *name, int name_len, + const char *instruction, int instruction_len, + int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract) +{ + int i; + (void)abstract; + + fprintf(stdout, "Kb-int name: %.*s\n", name_len, name); + fprintf(stdout, "Kb-int instruction: %.*s\n", instruction_len, instruction); + for (i = 0; i < num_prompts; ++i) { + fprintf(stdout, "Kb-int prompt %d: %.*s\n", i, prompts[i].length, + prompts[i].text); + } + + if (num_prompts == 1) { + responses[0].text = strdup(PASSWORD); + responses[0].length = strlen(PASSWORD); + } +} + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "keyboard-interactive") == NULL) { + fprintf(stderr, + "'keyboard-interactive' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_keyboard_interactive_ex( + session, USERNAME, strlen(USERNAME), kbd_callback); + if (rc != 0) { + print_last_session_error("libssh2_userauth_keyboard_interactive_ex"); + return 1; + } + + return 0; +} diff --git a/tests/test_password_auth_fails_with_wrong_password.c b/tests/test_password_auth_fails_with_wrong_password.c new file mode 100644 index 0000000000..dc65c1320c --- /dev/null +++ b/tests/test_password_auth_fails_with_wrong_password.c @@ -0,0 +1,36 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *WRONG_PASSWORD = "i'm not the password"; + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "password") == NULL) { + fprintf(stderr, "'password' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_password_ex(session, USERNAME, strlen(USERNAME), + WRONG_PASSWORD, strlen(WRONG_PASSWORD), + NULL); + if (rc == 0) { + fprintf(stderr, "Password auth succeeded with wrong password\n"); + return 1; + } + + return 0; +} diff --git a/tests/test_password_auth_fails_with_wrong_username.c b/tests/test_password_auth_fails_with_wrong_username.c new file mode 100644 index 0000000000..6ea27d42ea --- /dev/null +++ b/tests/test_password_auth_fails_with_wrong_username.c @@ -0,0 +1,36 @@ +#include "session_fixture.h" + +#include + +#include + +const char *PASSWORD = "my test password"; /* configured in Dockerfile */ +const char *WRONG_USERNAME = "i dont exist"; + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, WRONG_USERNAME, strlen(WRONG_USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "password") == NULL) { + fprintf(stderr, "'password' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_password_ex(session, WRONG_USERNAME, + strlen(WRONG_USERNAME), PASSWORD, + strlen(PASSWORD), NULL); + if (rc == 0) { + fprintf(stderr, "Password auth succeeded with wrong username\n"); + return 1; + } + + return 0; +} diff --git a/tests/test_password_auth_succeeds_with_correct_credentials.c b/tests/test_password_auth_succeeds_with_correct_credentials.c new file mode 100644 index 0000000000..aaf9c2a385 --- /dev/null +++ b/tests/test_password_auth_succeeds_with_correct_credentials.c @@ -0,0 +1,41 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *PASSWORD = "my test password"; /* configured in Dockerfile */ + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "password") == NULL) { + fprintf(stderr, "'password' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_password_ex(session, USERNAME, strlen(USERNAME), + PASSWORD, strlen(PASSWORD), NULL); + if (rc != 0) { + print_last_session_error("libssh2_userauth_password_ex"); + return 1; + } + + if (libssh2_userauth_authenticated(session) == 0) { + fprintf(stderr, "Password auth appeared to succeed but " + "libssh2_userauth_authenticated returned 0\n"); + return 1; + } + + return 0; +} diff --git a/tests/test_public_key_auth_fails_with_wrong_key.c b/tests/test_public_key_auth_fails_with_wrong_key.c new file mode 100644 index 0000000000..6e12abf4e2 --- /dev/null +++ b/tests/test_public_key_auth_fails_with_wrong_key.c @@ -0,0 +1,37 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *KEY_FILE_PRIVATE = "key_dsa_wrong"; +const char *KEY_FILE_PUBLIC = "key_dsa_wrong.pub"; + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "publickey") == NULL) { + fprintf(stderr, "'publickey' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_publickey_fromfile_ex( + session, USERNAME, strlen(USERNAME), KEY_FILE_PUBLIC, KEY_FILE_PRIVATE, + NULL); + if (rc == 0) { + fprintf(stderr, "Public-key auth succeeded with wrong key\n"); + return 1; + } + + return 0; +} diff --git a/tests/test_public_key_auth_succeeds_with_correct_dsa_key.c b/tests/test_public_key_auth_succeeds_with_correct_dsa_key.c new file mode 100644 index 0000000000..4e5b46d042 --- /dev/null +++ b/tests/test_public_key_auth_succeeds_with_correct_dsa_key.c @@ -0,0 +1,37 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *KEY_FILE_PRIVATE = "key_dsa"; +const char *KEY_FILE_PUBLIC = "key_dsa.pub"; /* configured in Dockerfile */ + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "publickey") == NULL) { + fprintf(stderr, "'publickey' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_publickey_fromfile_ex( + session, USERNAME, strlen(USERNAME), KEY_FILE_PUBLIC, KEY_FILE_PRIVATE, + NULL); + if (rc != 0) { + print_last_session_error("libssh2_userauth_publickey_fromfile_ex"); + return 1; + } + + return 0; +} diff --git a/tests/test_public_key_auth_succeeds_with_correct_rsa_key.c b/tests/test_public_key_auth_succeeds_with_correct_rsa_key.c new file mode 100644 index 0000000000..b02a6425d9 --- /dev/null +++ b/tests/test_public_key_auth_succeeds_with_correct_rsa_key.c @@ -0,0 +1,37 @@ +#include "session_fixture.h" + +#include + +#include + +const char *USERNAME = "libssh2"; /* configured in Dockerfile */ +const char *KEY_FILE_PRIVATE = "key_rsa"; +const char *KEY_FILE_PUBLIC = "key_rsa.pub"; /* configured in Dockerfile */ + +int test(LIBSSH2_SESSION *session) +{ + int rc; + + const char *userauth_list = + libssh2_userauth_list(session, USERNAME, strlen(USERNAME)); + if (userauth_list == NULL) { + print_last_session_error("libssh2_userauth_list"); + return 1; + } + + if (strstr(userauth_list, "publickey") == NULL) { + fprintf(stderr, "'publickey' was expected in userauth list: %s\n", + userauth_list); + return 1; + } + + rc = libssh2_userauth_publickey_fromfile_ex( + session, USERNAME, strlen(USERNAME), KEY_FILE_PUBLIC, KEY_FILE_PRIVATE, + NULL); + if (rc != 0) { + print_last_session_error("libssh2_userauth_publickey_fromfile_ex"); + return 1; + } + + return 0; +}