aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format29
-rw-r--r--.editorconfig18
-rw-r--r--.gitignore3
-rw-r--r--.gitlab-ci.yml366
-rwxr-xr-x.gitlab-ci/clang-format-check.sh12
-rwxr-xr-x.gitlab-ci/git-check-signoff-trailer.sh36
-rwxr-xr-x.gitlab-ci/shellcheck.sh56
-rw-r--r--.gitleaks.toml10
-rw-r--r--CHANGELOG (renamed from ChangeLog)122
-rw-r--r--CMakeLists.txt86
-rw-r--r--CONTRIBUTING.md43
-rw-r--r--CPackConfig.cmake2
-rw-r--r--CompilerChecks.cmake18
-rw-r--r--ConfigureChecks.cmake105
-rw-r--r--DefineOptions.cmake6
-rw-r--r--INSTALL13
-rw-r--r--cmake/Modules/AddCMockaTest.cmake4
-rw-r--r--cmake/Modules/CodeCoverage.cmake750
-rw-r--r--cmake/Modules/DefineCMakeDefaults.cmake2
-rw-r--r--cmake/Modules/ExtractSymbols.cmake21
-rw-r--r--cmake/Modules/FindABIMap.cmake7
-rw-r--r--cmake/Modules/FindArgp.cmake6
-rw-r--r--cmake/Modules/FindGSSAPI.cmake2
-rw-r--r--config.h.cmake37
-rw-r--r--doc/CMakeLists.txt44
-rw-r--r--doc/authentication.dox3
-rw-r--r--doc/curve25519-sha256@libssh.org.txt12
-rwxr-xr-xdoc/doc_coverage.sh52
-rw-r--r--doc/forwarding.dox10
-rw-r--r--doc/guided_tour.dox4
-rw-r--r--doc/introduction.dox4
-rw-r--r--doc/mainpage.dox16
-rw-r--r--doc/pkcs11.dox30
-rw-r--r--doc/sftp.dox114
-rw-r--r--doc/sftp_aio.dox705
-rw-r--r--doc/shell.dox46
-rw-r--r--examples/CMakeLists.txt28
-rw-r--r--examples/exec.c2
-rw-r--r--examples/keygen.c6
-rw-r--r--examples/keygen2.c235
-rw-r--r--examples/libssh_scp.c2
-rw-r--r--examples/proxy.c20
-rw-r--r--examples/sample_sftpserver.c515
-rw-r--r--examples/samplesftp.c91
-rw-r--r--examples/samplesshd-cb.c42
-rw-r--r--examples/samplesshd-kbdint.c29
-rw-r--r--examples/senddata.c2
-rw-r--r--examples/ssh_X11_client.c950
-rw-r--r--examples/ssh_client.c29
-rw-r--r--examples/ssh_server.c190
-rw-r--r--examples/sshd_direct-tcpip.c47
-rw-r--r--examples/sshnetcat.c5
-rw-r--r--include/libssh/CMakeLists.txt7
-rw-r--r--include/libssh/agent.h8
-rw-r--r--include/libssh/auth.h8
-rw-r--r--include/libssh/bignum.h7
-rw-r--r--include/libssh/bind.h10
-rw-r--r--include/libssh/bind_config.h18
-rw-r--r--include/libssh/blf.h9
-rw-r--r--include/libssh/buffer.h14
-rw-r--r--include/libssh/callbacks.h103
-rw-r--r--include/libssh/chacha.h8
-rw-r--r--include/libssh/channels.h12
-rw-r--r--include/libssh/config.h8
-rw-r--r--include/libssh/config_parser.h19
-rw-r--r--include/libssh/crypto.h20
-rw-r--r--include/libssh/curve25519.h9
-rw-r--r--include/libssh/dh-gex.h9
-rw-r--r--include/libssh/dh.h22
-rw-r--r--include/libssh/ecdh.h9
-rw-r--r--include/libssh/ed25519.h12
-rw-r--r--include/libssh/fe25519.h8
-rw-r--r--include/libssh/ge25519.h8
-rw-r--r--include/libssh/gssapi.h8
-rw-r--r--include/libssh/kex.h15
-rw-r--r--include/libssh/keys.h18
-rw-r--r--include/libssh/knownhosts.h8
-rw-r--r--include/libssh/legacy.h26
-rw-r--r--include/libssh/libcrypto.h19
-rw-r--r--include/libssh/libgcrypt.h11
-rw-r--r--include/libssh/libmbedcrypto.h16
-rw-r--r--include/libssh/libssh.h123
-rw-r--r--include/libssh/libsshpp.hpp15
-rw-r--r--include/libssh/messages.h9
-rw-r--r--include/libssh/misc.h36
-rw-r--r--include/libssh/options.h11
-rw-r--r--include/libssh/packet.h13
-rw-r--r--include/libssh/pcap.h8
-rw-r--r--include/libssh/pki.h59
-rw-r--r--include/libssh/pki_priv.h35
-rw-r--r--include/libssh/poll.h8
-rw-r--r--include/libssh/poly1305.h8
-rw-r--r--include/libssh/priv.h40
-rw-r--r--include/libssh/sc25519.h8
-rw-r--r--include/libssh/scp.h8
-rw-r--r--include/libssh/server.h68
-rw-r--r--include/libssh/session.h70
-rw-r--r--include/libssh/sftp.h315
-rw-r--r--include/libssh/sftp_priv.h53
-rw-r--r--include/libssh/sftpserver.h73
-rw-r--r--include/libssh/socket.h5
-rw-r--r--include/libssh/string.h8
-rw-r--r--include/libssh/threads.h8
-rw-r--r--include/libssh/token.h13
-rw-r--r--include/libssh/wrapper.h63
-rw-r--r--libssh.pc.cmake14
-rw-r--r--src/ABI/current2
-rw-r--r--src/ABI/libssh-4.9.0.symbols427
-rw-r--r--src/CMakeLists.txt93
-rw-r--r--src/agent.c74
-rw-r--r--src/auth.c626
-rw-r--r--src/bignum.c15
-rw-r--r--src/bind.c276
-rw-r--r--src/bind_config.c147
-rw-r--r--src/buffer.c47
-rw-r--r--src/chachapoly.c2
-rw-r--r--src/channels.c829
-rw-r--r--src/client.c335
-rw-r--r--src/config.c302
-rw-r--r--src/config_parser.c45
-rw-r--r--src/connect.c51
-rw-r--r--src/connector.c65
-rw-r--r--src/crypto_common.c16
-rw-r--r--src/curve25519.c42
-rw-r--r--src/dh-gex.c55
-rw-r--r--src/dh.c59
-rw-r--r--src/dh_crypto.c341
-rw-r--r--src/dh_key.c4
-rw-r--r--src/ecdh.c15
-rw-r--r--src/ecdh_crypto.c463
-rw-r--r--src/ecdh_gcrypt.c16
-rw-r--r--src/ecdh_mbedcrypto.c67
-rw-r--r--src/error.c8
-rw-r--r--src/external/bcrypt_pbkdf.c4
-rw-r--r--src/gcrypt_missing.c2
-rw-r--r--src/getpass.c21
-rw-r--r--src/getrandom_crypto.c64
-rw-r--r--src/getrandom_gcrypt.c38
-rw-r--r--src/getrandom_mbedcrypto.c52
-rw-r--r--src/gssapi.c166
-rw-r--r--src/gzip.c340
-rw-r--r--src/init.c32
-rw-r--r--src/kdf.c129
-rw-r--r--src/kex.c776
-rw-r--r--src/known_hosts.c38
-rw-r--r--src/knownhosts.c110
-rw-r--r--src/legacy.c76
-rw-r--r--src/libcrypto-compat.c305
-rw-r--r--src/libcrypto-compat.h44
-rw-r--r--src/libcrypto.c807
-rw-r--r--src/libgcrypt.c252
-rw-r--r--src/libmbedcrypto.c488
-rw-r--r--src/libssh.map31
-rw-r--r--src/log.c2
-rw-r--r--src/match.c123
-rw-r--r--src/mbedcrypto-compat.h39
-rw-r--r--src/mbedcrypto_missing.c6
-rw-r--r--src/md_crypto.c324
-rw-r--r--src/md_gcrypt.c246
-rw-r--r--src/md_mbedcrypto.c407
-rw-r--r--src/messages.c131
-rw-r--r--src/misc.c646
-rw-r--r--src/options.c841
-rw-r--r--src/packet.c221
-rw-r--r--src/packet_cb.c271
-rw-r--r--src/packet_crypt.c136
-rw-r--r--src/pcap.c277
-rw-r--r--src/pki.c818
-rw-r--r--src/pki_container_openssh.c126
-rw-r--r--src/pki_crypto.c2158
-rw-r--r--src/pki_ed25519.c2
-rw-r--r--src/pki_ed25519_common.c73
-rw-r--r--src/pki_gcrypt.c644
-rw-r--r--src/pki_mbedcrypto.c937
-rw-r--r--src/poll.c302
-rw-r--r--src/scp.c85
-rw-r--r--src/server.c395
-rw-r--r--src/session.c263
-rw-r--r--src/sftp.c1818
-rw-r--r--src/sftp_aio.c500
-rw-r--r--src/sftp_common.c913
-rw-r--r--src/sftpserver.c2028
-rw-r--r--src/socket.c163
-rw-r--r--src/string.c8
-rw-r--r--src/threads.c2
-rw-r--r--src/threads/libcrypto.c85
-rw-r--r--src/token.c143
-rw-r--r--src/ttyopts.c449
-rw-r--r--src/wrapper.c120
-rw-r--r--tests/CMakeLists.txt107
-rw-r--r--tests/benchmarks/CMakeLists.txt10
-rwxr-xr-xtests/benchmarks/bench1.sh11
-rwxr-xr-xtests/benchmarks/bench2.sh11
-rw-r--r--tests/benchmarks/bench_raw.c16
-rw-r--r--tests/benchmarks/bench_sftp.c397
-rw-r--r--tests/benchmarks/benchmarks.c670
-rw-r--r--tests/benchmarks/benchmarks.h6
-rw-r--r--tests/chown_wrapper.c21
-rwxr-xr-x[-rw-r--r--]tests/client/CMakeLists.txt15
-rw-r--r--tests/client/torture_algorithms.c22
-rw-r--r--tests/client/torture_auth.c619
-rw-r--r--tests/client/torture_auth_cert.c1072
-rw-r--r--tests/client/torture_auth_common.c94
-rw-r--r--tests/client/torture_auth_pkcs11.c11
-rw-r--r--tests/client/torture_client_callbacks.c261
-rw-r--r--tests/client/torture_client_config.c2
-rw-r--r--tests/client/torture_connect.c47
-rw-r--r--tests/client/torture_forward.c4
-rw-r--r--tests/client/torture_hostkey.c28
-rw-r--r--tests/client/torture_proxycommand.c74
-rw-r--r--tests/client/torture_rekey.c414
-rwxr-xr-xtests/client/torture_request_pty_modes.c258
-rw-r--r--tests/client/torture_scp.c9
-rw-r--r--tests/client/torture_session.c273
-rw-r--r--tests/client/torture_sftp_aio.c534
-rw-r--r--tests/client/torture_sftp_expand_path.c125
-rw-r--r--tests/client/torture_sftp_hardlink.c114
-rw-r--r--tests/client/torture_sftp_limits.c177
-rw-r--r--tests/client/torture_sftp_read.c2
-rw-r--r--tests/client/torture_sftp_rename.c120
-rw-r--r--tests/ctest-default.cmake6
-rw-r--r--tests/etc/hosts.in3
-rw-r--r--tests/etc/pam_matrix_passdb.in1
-rw-r--r--tests/etc/passwd.in1
-rw-r--r--tests/etc/shadow.in1
-rw-r--r--tests/external_override/CMakeLists.txt74
-rw-r--r--tests/external_override/torture_override.c10
-rw-r--r--tests/fuzz/CMakeLists.txt17
-rw-r--r--tests/fuzz/README.md4
-rw-r--r--tests/fuzz/ssh_bind_config_fuzzer.c52
-rw-r--r--tests/fuzz/ssh_client_config_fuzzer.c55
-rw-r--r--tests/fuzz/ssh_client_fuzzer.c (renamed from tests/fuzz/ssh_client_fuzzer.cpp)22
-rw-r--r--tests/fuzz/ssh_known_hosts_fuzzer.c82
-rw-r--r--tests/fuzz/ssh_known_hosts_fuzzer_corpus/d7c0eade3f3b70d94b1a7090e09eb8607da0ace4bin0 -> 189 bytes
-rw-r--r--tests/fuzz/ssh_privkey_fuzzer.c52
-rw-r--r--tests/fuzz/ssh_privkey_fuzzer_corpus/855ce609b52aec530bf631a78da7038bed99040a8
-rw-r--r--tests/fuzz/ssh_pubkey_fuzzer.c68
-rw-r--r--tests/fuzz/ssh_pubkey_fuzzer_corpus/b2c9f01394a2835b2cd7c520395a4977143e8d231
-rw-r--r--tests/fuzz/ssh_server_fuzzer.c (renamed from tests/fuzz/ssh_server_fuzzer.cpp)18
-rw-r--r--tests/keys/certauth/id_rsa54
-rw-r--r--tests/keys/certauth/id_rsa-cert.pub2
-rw-r--r--tests/keys/certauth/id_rsa.pub2
-rw-r--r--tests/keys/id_rsa_protected58
-rw-r--r--tests/keys/ssh_host_dsa_key12
-rw-r--r--tests/keys/ssh_host_dsa_key.pub1
-rwxr-xr-xtests/pkcs11/setup-softhsm-tokens.sh97
-rw-r--r--tests/pkd/CMakeLists.txt23
-rw-r--r--tests/pkd/pkd_daemon.c15
-rw-r--r--tests/pkd/pkd_daemon.h11
-rw-r--r--tests/pkd/pkd_hello.c322
-rw-r--r--tests/pkd/pkd_keyutil.c54
-rw-r--r--tests/pkd/pkd_keyutil.h12
-rw-r--r--tests/pkd/pkd_util.c1
-rw-r--r--tests/server/CMakeLists.txt1
-rw-r--r--tests/server/test_server/CMakeLists.txt14
-rw-r--r--tests/server/test_server/default_cb.c42
-rw-r--r--tests/server/test_server/default_cb.h4
-rw-r--r--tests/server/test_server/main.c51
-rw-r--r--tests/server/test_server/sftpserver_cb.c403
-rw-r--r--tests/server/test_server/test_server.c124
-rw-r--r--tests/server/test_server/test_server.h8
-rw-r--r--tests/server/torture_server.c130
-rw-r--r--tests/server/torture_server_auth_kbdint.c36
-rw-r--r--tests/server/torture_server_config.c27
-rw-r--r--tests/server/torture_sftpserver.c1012
-rw-r--r--tests/ssh_ping.c2
-rw-r--r--tests/suppressions/lsan.supp1
-rw-r--r--tests/tests_config.h.cmake7
-rw-r--r--tests/torture.c191
-rw-r--r--tests/torture.h29
-rw-r--r--tests/torture_key.c1566
-rw-r--r--tests/torture_pki.c7
-rw-r--r--tests/unittests/CMakeLists.txt37
-rwxr-xr-xtests/unittests/hello world.sh2
-rw-r--r--tests/unittests/torture_bignum.c106
-rw-r--r--tests/unittests/torture_bind_config.c1305
-rw-r--r--tests/unittests/torture_callbacks.c2
-rw-r--r--tests/unittests/torture_config.c876
-rw-r--r--tests/unittests/torture_keyfiles.c73
-rw-r--r--tests/unittests/torture_knownhosts_parsing.c67
-rw-r--r--tests/unittests/torture_misc.c421
-rw-r--r--tests/unittests/torture_options.c677
-rw-r--r--tests/unittests/torture_packet.c15
-rw-r--r--tests/unittests/torture_packet_filter.c2
-rw-r--r--tests/unittests/torture_pki.c47
-rw-r--r--tests/unittests/torture_pki_dsa.c766
-rw-r--r--tests/unittests/torture_pki_ecdsa.c346
-rw-r--r--tests/unittests/torture_pki_ecdsa_uri.c187
-rw-r--r--tests/unittests/torture_pki_ed25519.c162
-rw-r--r--tests/unittests/torture_pki_rsa.c336
-rw-r--r--tests/unittests/torture_pki_rsa_uri.c64
-rw-r--r--tests/unittests/torture_session_keys.c12
-rw-r--r--tests/unittests/torture_threads_pki_rsa.c2
-rw-r--r--tests/unittests/torture_tokens.c64
-rw-r--r--tests/unittests/torture_unit_server.c195
-rw-r--r--tests/valgrind.supp11
296 files changed, 33633 insertions, 12866 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..2880be88
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,29 @@
+---
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+AllowShortIfStatementsOnASingleLine: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterEnum: false
+ AfterFunction: true
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeElse: false
+ BeforeWhile: false
+IndentCaseLabels: false
+IndentCaseBlocks: false
+ColumnLimit: 80
+AlignAfterOpenBracket: Align
+AllowAllParametersOfDeclarationOnNextLine: false
+BinPackArguments: false
+BinPackParameters: false
+AllowAllArgumentsOnNextLine: false
+AllowShortFunctionsOnASingleLine: Empty
+# TODO with Clang 19, replace the below with
+# BreakAfterReturnType: ExceptShortType
+AlwaysBreakAfterReturnType: AllDefinitions
+AlignEscapedNewlines: Left
+ForEachMacros: ['ssh_callbacks_iterate']
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..59eb92bc
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+max_line_length = 80
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{c,h}]
+indent_style = space
+indent_size = 4
+tab_width = 4
+
+[{CMakeLists.txt,*.cmake}]
+indent_style = space
+indent_size = 4
+tab_width = 4 \ No newline at end of file
diff --git a/.gitignore b/.gitignore
index da6827ca..831f32f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,12 @@
*.a
*.o
-.*
*.swp
*~$
cscope.*
compile_commands.json
+/.cache
/.clangd
tags
/build
/obj*
+doc/tags.xml
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 509fb1d4..c47e6d45 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,24 +1,39 @@
+---
variables:
BUILD_IMAGES_PROJECT: libssh/build-images
+ CENTOS8_BUILD: buildenv-c8s
+ CENTOS9_BUILD: buildenv-c9s
FEDORA_BUILD: buildenv-fedora
- UBUNTU_BUILD: buildenv-ubuntu
- CENTOS7_BUILD: buildenv-centos7
- TUMBLEWEED_BUILD: buildenv-tumbleweed
MINGW_BUILD: buildenv-mingw
+ TUMBLEWEED_BUILD: buildenv-tumbleweed
+ UBUNTU_BUILD: buildenv-ubuntu
+ ALPINE_BUILD: buildenv-alpine
stages:
+ - review
- build
- test
- analysis
+# This is some black magic to select between branch pipelines and
+# merge request pipelines to avoid running same pipelines in twice
+workflow:
+ rules:
+ - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
+ when: never
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
+ when: never
+ - if: '$CI_COMMIT_BRANCH'
+
.build:
stage: build
variables:
- CMAKE_DEFAULT_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON"
- CMAKE_BUILD_OPTIONS: "-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 -DWITH_DSA=ON"
- CMAKE_TEST_OPTIONS: "-DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON"
- CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS $CMAKE_BUILD_OPTIONS $CMAKE_TEST_OPTIONS
- before_script:
+ CMAKE_DEFAULT_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON"
+ CMAKE_BUILD_OPTIONS: "-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"
+ CMAKE_TEST_OPTIONS: "-DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DWITH_BENCHMARKS=ON -DFUZZ_TESTING=ON"
+ CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS $CMAKE_BUILD_OPTIONS $CMAKE_TEST_OPTIONS
+ before_script: &build
- uname -a
- cat /etc/os-release
- mount
@@ -33,6 +48,10 @@ stages:
# Do not use after_script as it does not make the targets fail
tags:
- shared
+ only:
+ - merge_requests
+ - branches
+
except:
- tags
artifacts:
@@ -50,32 +69,97 @@ stages:
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
ctest --output-on-failure
- # Do not use after_script as it does not make the targets fail
+ # Do not use after_script as it does not make the targets fail
.fedora:
extends: .tests
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
variables:
- CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
.tumbleweed:
extends: .tests
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD
+.fips:
+ extends: .tests
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
+ before_script:
+ - *build
+ - echo "# userspace fips" > /etc/system-fips
+ # We do not need the kernel part, but in case we ever do:
+ # mkdir -p /var/tmp/userspace-fips
+ # echo 1 > /var/tmp/userspace-fips/fips_enabled
+ # mount --bind /var/tmp/userspace-fips/fips_enabled \
+ # /proc/sys/crypto/fips_enabled
+ - update-crypto-policies --show
+ - update-crypto-policies --set FIPS
+ - update-crypto-policies --show
+###############################################################################
+# Review #
+###############################################################################
+review:
+ variables:
+ GIT_DEPTH: 100
+ stage: review
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ script:
+ - ERROR=0
+ codespell --ignore-words-list=keypair,sorce,ned,nd,ue || ERROR=1;
+ ./.gitlab-ci/clang-format-check.sh || ERROR=1;
+ ./.gitlab-ci/git-check-signoff-trailer.sh ${CI_MERGE_REQUEST_DIFF_BASE_SHA} || ERROR=1;
+ ./.gitlab-ci/git-check-signoff-trailer.sh ${CI_MERGE_REQUEST_DIFF_BASE_SHA} || ERROR=1;
+ ./.gitlab-ci/shellcheck.sh || ERROR=1;
+ exit $ERROR
+ # the format is not always matching our intentions
+ allow_failure: true
+ tags:
+ - shared
+ only:
+ - merge_requests
###############################################################################
# CentOS builds #
###############################################################################
-# pkd tests fail on CentOS7 docker images, so we don't use -DSERVER_TESTING=ON
-centos7/openssl_1.0.x/x86_64:
- image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS7_BUILD
+centos9s/openssl_3.0.x/x86_64:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
extends: .tests
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
script:
- - cmake3 $CMAKE_OPTIONS .. &&
+ - export OPENSSL_ENABLE_SHA1_SIGNATURES=1
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
ctest --output-on-failure
+centos9s/openssl_3.0.x/x86_64/fips:
+ extends: .fips
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
+ script:
+ - export OPENSSL_ENABLE_SHA1_SIGNATURES=1
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
+
+centos8s/openssl_1.1.1/x86_64:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
+ extends: .tests
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
+ script:
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure
+
+centos8s/openssl_1.1.1/x86_64/fips:
+ extends: .fips
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
+ script:
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
###############################################################################
# Fedora builds #
@@ -88,41 +172,42 @@ fedora/docs:
extends: .build
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
script:
- - cmake .. && make docs
+ - cmake .. && make docs_coverage && make docs
+ coverage: '/^Documentation coverage is \d+.\d+%/'
fedora/ninja:
extends: .fedora
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
script:
- - cmake -G Ninja $CMAKE_OPTIONS ../ && ninja && ninja test
-
-fedora/openssl_1.1.x/x86_64:
- extends: .fedora
+ - cmake -G Ninja $CMAKE_OPTIONS ../ && ninja && CTEST_OUTPUT_ON_FAILURE=1 ninja test
-fedora/openssl_1.1.x/x86_64/fips:
+fedora/coverage:
extends: .fedora
- before_script:
- - echo "# userspace fips" > /etc/system-fips
- # We do not need the kernel part, but in case we ever do:
- # mkdir -p /var/tmp/userspace-fips
- # echo 1 > /var/tmp/userspace-fips/fips_enabled
- # mount --bind /var/tmp/userspace-fips/fips_enabled /proc/sys/crypto/fips_enabled
- - update-crypto-policies --show
- - update-crypto-policies --set FIPS
- - update-crypto-policies --show
- - mkdir -p obj && cd obj && cmake
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -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 -DWITH_DSA=ON
- -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON ..
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DWITH_COVERAGE=ON"
script:
- - cmake $CMAKE_OPTIONS .. &&
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
- OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
+ make coverage_xml
+ coverage: /^\s*lines:\s*\d+.\d+\%/
+ artifacts:
+ name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
+ expire_in: 1 week
+ reports:
+ coverage_report:
+ coverage_format: cobertura
+ path: obj/coverage_xml.xml
+
+fedora/openssl_3.0.x/x86_64:
+ extends: .fedora
+
+fedora/openssl_3.0.x/x86_64/pkcs11-provider:
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
+ extends: .fedora
-fedora/openssl_1.1.x/x86_64/minimal:
+fedora/openssl_3.0.x/x86_64/minimal:
extends: .fedora
variables:
script:
@@ -131,12 +216,24 @@ fedora/openssl_1.1.x/x86_64/minimal:
-DWITH_SERVER=OFF
-DWITH_ZLIB=OFF
-DWITH_PCAP=OFF
- -DWITH_DSA=OFF
-DUNIT_TESTING=ON
-DCLIENT_TESTING=ON
-DWITH_GEX=OFF .. &&
make -j$(nproc)
+# The PKCS#11 support is turned off as it brings dozens of memory issues from
+# engine_pkcs11 or openssl itself
+fedora/valgrind:
+ variables:
+ CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=OFF
+ extends: .fedora
+ stage: analysis
+ script:
+ - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
+ make -j$(nproc) &&
+ make test_memcheck
+ - cat Testing/Temporary/MemoryChecker.*.log | wc -l | grep "^0$"
+
# Address sanitizer doesn't mix well with LD_PRELOAD used in the testsuite
# so, this is only enabled for unit tests right now.
# TODO: add -DCLIENT_TESTING=ON -DSERVER_TESTING=ON
@@ -190,12 +287,12 @@ fedora/undefined-sanitizer:
fedora/libgcrypt/x86_64:
extends: .fedora
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DWITH_GCRYPT=ON -DWITH_DEBUG_CRYPTO=ON"
+ CMAKE_ADDITIONAL_OPTIONS: "-DWITH_GCRYPT=ON -DWITH_DEBUG_CRYPTO=ON"
fedora/mbedtls/x86_64:
extends: .fedora
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_DSA=OFF"
+ CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON "
# Unit testing only, no client and pkd testing, because cwrap is not available
# for MinGW
@@ -232,87 +329,107 @@ fedora/mingw32:
ctest --output-on-failure
-
###############################################################################
# Fedora csbuild #
###############################################################################
.csbuild:
stage: analysis
variables:
- GIT_DEPTH: "100"
+ GIT_DEPTH: "100"
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
before_script:
- - |
- if [[ -z "$CI_COMMIT_BEFORE_SHA" ]]; then
- export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
- fi
-
- # Check if the commit exists in this branch
- # This is not the case for a force push
- git branch --contains $CI_COMMIT_BEFORE_SHA 2>/dev/null || export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
-
- export CI_COMMIT_RANGE="$CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA"
+ - |
+ # for merge requests
+ if [[ -n "$CI_MERGE_REQUEST_DIFF_BASE_SHA" ]]; then
+ export CI_COMMIT_BEFORE_SHA="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
+ fi
+ # for branches run
+ if [[ -z "$CI_COMMIT_BEFORE_SHA" ]]; then
+ export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
+ fi
+
+ # Check if the commit exists in this branch
+ # This is not the case for a force push
+ git branch --contains $CI_COMMIT_BEFORE_SHA 2>/dev/null || export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
+
+ export CI_COMMIT_RANGE="$CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA"
tags:
- - shared
+ - shared
except:
- - tags
+ - tags
+ only:
+ - merge_requests
artifacts:
expire_in: 1 week
when: on_failure
paths:
- obj-csbuild/
-fedora/csbuild/openssl_1.1.x:
+fedora/csbuild/openssl_3.0.x:
extends: .csbuild
script:
- - csbuild
- --build-dir=obj-csbuild
- --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 -DWITH_DSA=ON @SRCDIR@ && make clean && make -j$(nproc)"
- --git-commit-range $CI_COMMIT_RANGE
- --color
- --print-current --print-fixed
+ - csbuild
+ --build-dir=obj-csbuild
+ --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
fedora/csbuild/libgcrypt:
extends: .csbuild
script:
- - csbuild
- --build-dir=obj-csbuild
- --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 -DWITH_GCRYPT=ON -DWITH_DSA=ON @SRCDIR@ && make clean && make -j$(nproc)"
- --git-commit-range $CI_COMMIT_RANGE
- --color
- --print-current --print-fixed
+ - csbuild
+ --build-dir=obj-csbuild
+ --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 -DWITH_GCRYPT=ON @SRCDIR@ && make clean && make -j$(nproc)"
+ --git-commit-range $CI_COMMIT_RANGE
+ --color
+ --print-current --print-fixed
fedora/csbuild/mbedtls:
extends: .csbuild
script:
- - csbuild
- --build-dir=obj-csbuild
- --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 -DWITH_MBEDTLS=ON @SRCDIR@ && make clean && make -j$(nproc)"
- --git-commit-range $CI_COMMIT_RANGE
- --color
- --print-current --print-fixed
-
-
+ - csbuild
+ --build-dir=obj-csbuild
+ --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 -DWITH_MBEDTLS=ON @SRCDIR@ && make clean && make -j$(nproc)"
+ --git-commit-range $CI_COMMIT_RANGE
+ --color
+ --print-current --print-fixed
###############################################################################
# Ubuntu builds #
###############################################################################
-ubuntu/openssl_1.1.x/x86_64:
+ubuntu/openssl_3.0.x/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$UBUNTU_BUILD
extends: .tests
+###############################################################################
+# Alpine builds #
+###############################################################################
+alpine/openssl_3.0.x/musl:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$ALPINE_BUILD
+ extends: .tests
+ script:
+ - cmake $CMAKE_DEFAULT_OPTIONS
+ -DWITH_SFTP=ON
+ -DWITH_SERVER=ON
+ -DWITH_ZLIB=ON
+ -DWITH_PCAP=ON
+ -DUNIT_TESTING=ON .. &&
+ make -j$(nproc) &&
+ ctest --output-on-failure
+
###############################################################################
# Tumbleweed builds #
###############################################################################
-tumbleweed/openssl_1.1.x/x86_64/gcc:
+tumbleweed/openssl_3.0.x/x86_64/gcc:
extends: .tumbleweed
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
+ CMAKE_ADDITIONAL_OPTIONS: "-DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
-tumbleweed/openssl_1.1.x/x86/gcc:
+tumbleweed/openssl_3.0.x/x86/gcc:
extends: .tumbleweed
script:
- cmake
@@ -322,30 +439,30 @@ tumbleweed/openssl_1.1.x/x86/gcc:
-DWITH_SERVER=ON
-DWITH_ZLIB=ON
-DWITH_PCAP=ON
- -DWITH_DSA=ON
- -DUNIT_TESTING=ON ..
+ -DUNIT_TESTING=ON .. &&
+ make -j$(nproc)
-tumbleweed/openssl_1.1.x/x86_64/gcc7:
+tumbleweed/openssl_3.0.x/x86_64/gcc7:
extends: .tumbleweed
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7 -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7 -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
-tumbleweed/openssl_1.1.x/x86/gcc7:
+tumbleweed/openssl_3.0.x/x86/gcc7:
extends: .tumbleweed
script:
- cmake
-DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-cross-m32.cmake
-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7
$CMAKE_DEFAULT_OPTIONS
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DWITH_DSA=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
-DUNIT_TESTING=ON .. &&
make -j$(nproc) &&
ctest --output-on-failure
-tumbleweed/openssl_1.1.x/x86_64/clang:
+tumbleweed/openssl_3.0.x/x86_64/clang:
extends: .tumbleweed
variables:
- CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
+ CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config"
tumbleweed/static-analysis:
extends: .tests
@@ -369,21 +486,20 @@ tumbleweed/static-analysis:
- obj/scan
-
###############################################################################
# FreeBSD builds #
###############################################################################
# That is a specific runner that we cannot enable universally.
# We restrict it to builds under the $BUILD_IMAGES_PROJECT project.
-freebsd/x86_64:
+freebsd/openssl_1.1.1/x86_64:
image:
extends: .tests
before_script:
- - mkdir -p obj && cd obj && cmake
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DPICKY_DEVELOPER=ON
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
- -DUNIT_TESTING=ON ..
+ - mkdir -p obj && cd obj && cmake
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ -DPICKY_DEVELOPER=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
+ -DUNIT_TESTING=ON ..
script:
- cmake $CMAKE_DEFAULT_OPTIONS
-DWITH_SFTP=ON
@@ -394,12 +510,13 @@ freebsd/x86_64:
make &&
ctest --output-on-failure
tags:
- - private
- - freebsd
+ - private
+ - freebsd
only:
- - branches@libssh/libssh-mirror
- - branches@cryptomilk/libssh-mirror
- - branches@jjelen/libssh-mirror
+ - branches@libssh/libssh-mirror
+ - branches@cryptomilk/libssh-mirror
+ - branches@jjelen/libssh-mirror
+ - branches@marco.fortina/libssh-mirror
###############################################################################
@@ -414,36 +531,37 @@ freebsd/x86_64:
variables:
ErrorActionPreference: STOP
script:
- - cmake --build .
- - ctest --output-on-failure
+ - cmake --build .
+ - ctest --output-on-failure
tags:
- - windows
- - shared-windows
+ - windows
+ - shared-windows
+ only:
+ - merge_requests
+ - branches
except:
- - tags
+ - tags
artifacts:
expire_in: 1 week
when: on_failure
paths:
- obj/
before_script:
- - choco install --no-progress -y cmake
- - $env:Path += ';C:\Program Files\CMake\bin'
- - If (!(test-path .vcpkg\archives)) { mkdir -p .vcpkg\archives }
- - $env:VCPKG_DEFAULT_BINARY_CACHE="$PWD\.vcpkg\archives"
- - echo $env:VCPKG_DEFAULT_BINARY_CACHE
- - $env:VCPKG_DEFAULT_TRIPLET="$TRIPLET-windows"
- - vcpkg install cmocka
- - vcpkg install openssl
- - vcpkg install zlib
- - vcpkg integrate install
- - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
- - cmake
- -A $PLATFORM
- -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
- -DPICKY_DEVELOPER=ON
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
- -DUNIT_TESTING=ON ..
+ - If (!(test-path .vcpkg\archives)) { mkdir -p .vcpkg\archives }
+ - $env:VCPKG_DEFAULT_BINARY_CACHE="$PWD\.vcpkg\archives"
+ - echo $env:VCPKG_DEFAULT_BINARY_CACHE
+ - $env:VCPKG_DEFAULT_TRIPLET="$TRIPLET-windows"
+ - vcpkg install cmocka
+ - vcpkg install openssl
+ - vcpkg install zlib
+ - vcpkg integrate install
+ - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
+ - cmake
+ -A $PLATFORM
+ -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
+ -DPICKY_DEVELOPER=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
+ -DUNIT_TESTING=ON ..
visualstudio/x86_64:
extends: .vs
@@ -468,7 +586,7 @@ visualstudio/x86:
coverity:
stage: analysis
- image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
script:
- mkdir obj && cd obj
- wget https://scan.coverity.com/download/linux64 --post-data "token=$COVERITY_SCAN_TOKEN&project=$COVERITY_SCAN_PROJECT_NAME" -O /tmp/coverity_tool.tgz
diff --git a/.gitlab-ci/clang-format-check.sh b/.gitlab-ci/clang-format-check.sh
new file mode 100755
index 00000000..261918ae
--- /dev/null
+++ b/.gitlab-ci/clang-format-check.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Based on Github Action
+# https://github.com/yshui/git-clang-format-lint
+
+diff=$(git-clang-format --diff --commit "$CI_MERGE_REQUEST_DIFF_BASE_SHA")
+[ "$diff" = "no modified files to format" ] && exit 0
+[ "$diff" = "clang-format did not modify any files" ] && exit 0
+
+printf "You have introduced coding style breakages, suggested changes:\n\n"
+
+echo "${diff}" | colordiff
+exit 1
diff --git a/.gitlab-ci/git-check-signoff-trailer.sh b/.gitlab-ci/git-check-signoff-trailer.sh
new file mode 100755
index 00000000..e3819662
--- /dev/null
+++ b/.gitlab-ci/git-check-signoff-trailer.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+if [ $# != 1 ]; then
+ echo "Usage: $0 UPSTREAM_COMMIT_SHA"
+ exit 1
+fi
+
+failed=0
+
+if [ -z "$CI_COMMIT_SHA" ]; then
+ echo "CI_COMMIT_SHA is not set"
+ exit 1
+fi
+
+CI_COMMIT_RANGE="$1..$CI_COMMIT_SHA"
+
+red='\033[0;31m'
+blue='\033[0;34m'
+
+echo -e "${blue}Checking commit range: $CI_COMMIT_RANGE"
+echo
+echo
+
+for commit in $(git rev-list "$CI_COMMIT_RANGE"); do
+ git show -s --format=%B "$commit" | grep "^Signed-off-by: " >/dev/null 2>&1
+ ret=$?
+ if [ $ret -eq 1 ]; then
+ echo -e "${red} >>> Missing Signed-off-by trailer in commit $commit"
+ failed=$(("$failed" + 1))
+ fi
+done
+
+echo
+echo
+
+exit $failed
diff --git a/.gitlab-ci/shellcheck.sh b/.gitlab-ci/shellcheck.sh
new file mode 100755
index 00000000..e7db0b63
--- /dev/null
+++ b/.gitlab-ci/shellcheck.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+# Simplified and de-github-ed version of
+# https://github.com/ludeeus/action-shellcheck/blob/master/action.yaml
+
+statuscode=0
+
+declare -a filepaths
+shebangregex="^#! */[^ ]*/(env *)?[abk]*sh"
+set -f # temporarily disable globbing so that globs in inputs aren't expanded
+
+while IFS= read -r -d '' file; do
+ filepaths+=("$file")
+done < <(find . \
+ -type f \
+ '(' \
+ -name '*.bash' \
+ -o -name '.bashrc' \
+ -o -name 'bashrc' \
+ -o -name '.bash_aliases' \
+ -o -name '.bash_completion' \
+ -o -name '.bash_login' \
+ -o -name '.bash_logout' \
+ -o -name '.bash_profile' \
+ -o -name 'bash_profile' \
+ -o -name '*.ksh' \
+ -o -name 'suid_profile' \
+ -o -name '*.zsh' \
+ -o -name '.zlogin' \
+ -o -name 'zlogin' \
+ -o -name '.zlogout' \
+ -o -name 'zlogout' \
+ -o -name '.zprofile' \
+ -o -name 'zprofile' \
+ -o -name '.zsenv' \
+ -o -name 'zsenv' \
+ -o -name '.zshrc' \
+ -o -name 'zshrc' \
+ -o -name '*.sh' \
+ -o -path '*/.profile' \
+ -o -path '*/profile' \
+ -o -name '*.shlib' \
+ ')' \
+ -print0)
+
+while IFS= read -r -d '' file; do
+ head -n1 "$file" | grep -Eqs "$shebangregex" || continue
+ filepaths+=("$file")
+done < <(find . \
+ -type f ! -name '*.*' -perm /111 \
+ -print0)
+
+shellcheck "${filepaths[@]}" || statuscode=$?
+
+set +f # re-enable globbing
+
+exit "$statuscode"
diff --git a/.gitleaks.toml b/.gitleaks.toml
new file mode 100644
index 00000000..a245e6e5
--- /dev/null
+++ b/.gitleaks.toml
@@ -0,0 +1,10 @@
+#
+# GitLeaks Repo Specific Configuration
+#
+# This allowlist is used to help Red Hat ignore false positives during its code
+# scans.
+
+[allowlist]
+ paths = [
+ '''tests/*''',
+ ]
diff --git a/ChangeLog b/CHANGELOG
index 8bc15d9a..52633dcf 100644
--- a/ChangeLog
+++ b/CHANGELOG
@@ -1,9 +1,97 @@
-ChangeLog
-==========
-
-version 0.10.0 (released 2020-xx-xx)
- * Support for Smart Cards
- * Support for chacha20-poly1305@openssh.com with libgcrypt
+CHANGELOG
+=========
+
+version 0.10.0 (released 2022-07-xx)
+ * Added support for OpenSSL 3.0
+ * Added support for mbedTLS 3
+ * Added support for Smart Cards (through openssl pkcs11 engine)
+ * Added support for chacha20-poly1305@openssh.com with libgcrypt
+ * Added support ed25519 keys in PEM files
+ * Added support for sk-ecdsa and sk-ed25519 (server side)
+ * Added support for limiting RSA key sizes and not accepting small one by
+ default
+ * Added support for ssh-agent on Windows
+ * Added ssh_userauth_publickey_auto_get_current_identity() API
+ * Added ssh_vlog() API
+ * Added ssh_send_issue_banner() API
+ * Added ssh_session_set_disconnect_message() API
+ * Added new configuration options:
+ + IdentityAgent
+ + ModuliFile
+ * Provided X11 client example
+ * Disabled DSA support at build time by default (will be removed in the next
+ release)
+ * Deprecated the SCP API!
+ * Deprecated old pubkey, privatekey API
+ * Avoided some needless large stack buffers to minimize memory footprint
+ * Removed support for OpenSSL < 1.0.1
+
+version 0.9.6 (released 2021-08-26)
+ * CVE-2021-3634: Fix possible heap-buffer overflow when rekeying with
+ different key exchange mechanism
+ * Fix several memory leaks on error paths
+ * Reset pending_call_state on disconnect
+ * Fix handshake bug with AEAD ciphers and no HMAC overlap
+ * Use OPENSSL_CRYPTO_LIBRARIES in CMake
+ * Ignore request success and failure message if they are not expected
+ * Support more identity files in configuration
+ * Avoid setting compiler flags directly in CMake
+ * Support build directories with special characters
+ * Include stdlib.h to avoid crash in Windows
+ * Fix sftp_new_channel constructs an invalid object
+ * Fix Ninja multiple rules error
+ * Several tests fixes
+
+version 0.9.5 (released 2020-09-10)
+ * CVE-2020-16135: Avoid null pointer dereference in sftpserver (T232)
+ * Improve handling of library initialization (T222)
+ * Fix parsing of subsecond times in SFTP (T219)
+ * Make the documentation reproducible
+ * Remove deprecated API usage in OpenSSL
+ * Fix regression of ssh_channel_poll_timeout() returning SSH_AGAIN
+ * Define version in one place (T226)
+ * Prevent invalid free when using different C runtimes than OpenSSL (T229)
+ * Compatibility improvements to testsuite
+
+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 several 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-02-xx)
* Added support for AES-GCM
@@ -120,7 +208,7 @@ version 0.6.1 (released 2014-02-08)
* Fixed DSA signature extraction.
* Fixed some memory leaks.
* Fixed read of non-connected socket.
- * Fixed thread dectection.
+ * Fixed thread detection.
version 0.6.0 (released 2014-01-08)
* Added new publicy key API.
@@ -145,7 +233,7 @@ version 0.6.0 (released 2014-01-08)
version 0.5.5 (released 2013-07-26)
* BUG 103: Fix ProxyCommand parsing.
* Fix setting -D_FORTIFY_SOURCE=2.
- * Fix pollset error return if emtpy.
+ * Fix pollset error return if empty.
* Fix NULL pointer checks in channel functions.
* Several bugfixes.
@@ -161,7 +249,7 @@ version 0.5.3 (released 2012-11-20)
* BUG #84 - Fix bug in sftp_mkdir not returning on error.
* BUG #85 - Fixed a possible channel infinite loop if the connection dropped.
* BUG #88 - Added missing channel request_state and set it to accepted.
- * BUG #89 - Reset error state to no error on successful SSHv1 authentiction.
+ * BUG #89 - Reset error state to no error on successful SSHv1 authentication.
* Fixed a possible use after free in ssh_free().
* Fixed multiple possible NULL pointer dereferences.
* Fixed multiple memory leaks in error paths.
@@ -222,7 +310,7 @@ version 0.4.7 (released 2010-12-28)
* Fixed a possible memory leak in ssh_get_user_home().
* Fixed a memory leak in sftp_xstat.
* Fixed uninitialized fd->revents member.
- * Fixed timout value in ssh_channel_accept().
+ * Fixed timeout value in ssh_channel_accept().
* Fixed length checks in ssh_analyze_banner().
* Fixed a possible data overread and crash bug.
* Fixed setting max_fd which breaks ssh_select().
@@ -245,7 +333,7 @@ version 0.4.5 (released 2010-07-13)
* Added option to bind a client to an ip address.
* Fixed the ssh socket polling function.
* Fixed Windows related bugs in bsd_poll().
- * Fixed serveral build warnings.
+ * Fixed several build warnings.
version 0.4.4 (released 2010-06-01)
* Fixed a bug in the expand function for escape sequences.
@@ -264,17 +352,17 @@ version 0.4.3 (released 2010-05-18)
* Fixed sftp_chown.
* Fixed sftp_rename on protocol version 3.
* Fixed a blocking bug in channel_poll.
- * Fixed config parsing wich has overwritten user specified values.
+ * Fixed config parsing which has overwritten user specified values.
* Fixed hashed [host]:port format in knownhosts
* Fixed Windows build.
- * Fixed doublefree happening after a negociation error.
+ * Fixed doublefree happening after a negotiation error.
* Fixed aes*-ctr with <= OpenSSL 0.9.7b.
* Fixed some documentation.
* Fixed exec example which has broken read usage.
* Fixed broken algorithm choice for server.
* Fixed a typo that we don't export all symbols.
* Removed the unneeded dependency to doxygen.
- * Build examples only on the Linux plattform.
+ * Build examples only on the Linux platform.
version 0.4.2 (released 2010-03-15)
* Added owner and group information in sftp attributes.
@@ -296,7 +384,7 @@ version 0.4.1 (released 2010-02-13)
* Added an example for exec.
* Added private key type detection feature in privatekey_from_file().
* Fixed zlib compression fallback.
- * Fixed kex bug that client preference should be prioritary
+ * Fixed kex bug that client preference should be priority
* Fixed known_hosts file set by the user.
* Fixed a memleak in channel_accept().
* Fixed underflow when leave_function() are unbalanced
@@ -434,7 +522,7 @@ version 0.11-dev
* Keyboard-interactive authentication working.
version 0.1 (released 2004-03-05)
- * Begining of sftp subsystem implementation.
+ * Beginning of sftp subsystem implementation.
* Some cleanup into channels implementation
* Now every channel functions is called by its CHANNEL handler.
* Added channel_poll() and channel_read().
@@ -455,7 +543,7 @@ version 0.0.4 (released 2003-10-10)
* Added a wrapper.c file. The goal is to provide a similar API to every
cryptographic functions. bignums and sha/md5 are wrapped now.
* More work than it first looks.
- * Support for other crypto libs planed (lighter libs)
+ * Support for other crypto libs planned (lighter libs)
* Fixed stupid select() bug.
* Libssh now compiles and links with openssl 0.9.6
* RSA pubkey authentication code now works !
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f1925ef..acc1e606 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,6 @@
-cmake_minimum_required(VERSION 3.3.0)
-cmake_policy(SET CMP0048 NEW)
+cmake_minimum_required(VERSION 3.12.0)
-# Specify search path for CMake modules to be loaded by include()
+# Specify search path for CMake modules to be loaded by include()
# and find_package()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
@@ -10,7 +9,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
include(DefineCMakeDefaults)
include(DefineCompilerFlags)
-project(libssh VERSION 0.8.90 LANGUAGES C)
+project(libssh VERSION 0.10.90 LANGUAGES C CXX)
# global needed variable
set(APPLICATION_NAME ${PROJECT_NAME})
@@ -22,7 +21,7 @@ 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.9.0")
set(LIBRARY_SOVERSION "4")
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
@@ -49,32 +48,11 @@ endif (WITH_ZLIB)
if (WITH_GCRYPT)
find_package(GCrypt 1.5.0 REQUIRED)
- if (NOT GCRYPT_FOUND)
- message(FATAL_ERROR "Could not find GCrypt")
- endif (NOT GCRYPT_FOUND)
elseif(WITH_MBEDTLS)
find_package(MbedTLS REQUIRED)
- if (NOT MBEDTLS_FOUND)
- message(FATAL_ERROR "Could not find mbedTLS")
- endif (NOT MBEDTLS_FOUND)
-else (WITH_GCRYPT)
- find_package(OpenSSL 1.0.1)
- if (OPENSSL_FOUND)
- # On CMake < 3.16, OPENSSL_CRYPTO_LIBRARIES is usually a synonym for OPENSSL_CRYPTO_LIBRARY, but is not defined
- # when building on Windows outside of Cygwin. We provide the synonym here, if FindOpenSSL didn't define it already.
- if (NOT DEFINED OPENSSL_CRYPTO_LIBRARIES)
- set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- endif (NOT DEFINED OPENSSL_CRYPTO_LIBRARIES)
- else (OPENSSL_FOUND)
- find_package(GCrypt)
- if (NOT GCRYPT_FOUND)
- find_package(MbedTLS)
- if (NOT MBEDTLS_FOUND)
- message(FATAL_ERROR "Could not find OpenSSL, GCrypt or mbedTLS")
- endif (NOT MBEDTLS_FOUND)
- endif (NOT GCRYPT_FOUND)
- endif (OPENSSL_FOUND)
-endif(WITH_GCRYPT)
+else()
+ find_package(OpenSSL 1.1.1 REQUIRED)
+endif()
if (UNIT_TESTING)
find_package(CMocka REQUIRED)
@@ -89,13 +67,6 @@ if (WITH_GSSAPI)
find_package(GSSAPI)
endif (WITH_GSSAPI)
-if (WITH_PKCS11_URI)
- find_package(softhsm)
- if (NOT SOFTHSM_FOUND)
- message(SEND_ERROR "Could not find softhsm module!")
- endif (NOT SOFTHSM_FOUND)
-endif (WITH_PKCS11_URI)
-
if (WITH_NACL)
find_package(NaCl)
if (NOT NACL_FOUND)
@@ -103,10 +74,6 @@ if (WITH_NACL)
endif (NOT NACL_FOUND)
endif (WITH_NACL)
-if (BSD OR SOLARIS OR OSX)
- find_package(Argp)
-endif (BSD OR SOLARIS OR OSX)
-
# Disable symbol versioning in non UNIX platforms
if (UNIX)
find_package(ABIMap 0.3.1)
@@ -118,14 +85,18 @@ endif (UNIX)
include(ConfigureChecks.cmake)
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+if (NOT HAVE_ARGP_PARSE)
+ find_package(Argp)
+endif (NOT HAVE_ARGP_PARSE)
+
# check subdirectories
add_subdirectory(doc)
add_subdirectory(include)
add_subdirectory(src)
# pkg-config file
-if (UNIX)
-configure_file(libssh.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc)
+if (UNIX OR MINGW)
+configure_file(libssh.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc @ONLY)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/libssh.pc
@@ -134,7 +105,7 @@ install(
COMPONENT
pkgconfig
)
-endif (UNIX)
+endif (UNIX OR MINGW)
# CMake config files
include(CMakePackageConfigHelpers)
@@ -216,16 +187,35 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
endif(UPDATE_ABI)
endif (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
+# Coverage
+if (WITH_COVERAGE)
+ include(CodeCoverage)
+ setup_target_for_coverage_lcov(
+ NAME "coverage"
+ EXECUTABLE make test
+ DEPENDENCIES ssh tests)
+ set(GCOVR_ADDITIONAL_ARGS --xml-pretty --exclude-unreachable-branches --print-summary)
+ setup_target_for_coverage_gcovr_xml(
+ NAME "coverage_xml"
+ EXECUTABLE make test
+ DEPENDENCIES ssh tests)
+endif (WITH_COVERAGE)
+
add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source DEPENDS ${_SYMBOL_TARGET} VERBATIM)
-# Link compile database for clangd
-execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
- "${CMAKE_BINARY_DIR}/compile_commands.json"
- "${CMAKE_SOURCE_DIR}/compile_commands.json")
+get_directory_property(hasParent PARENT_DIRECTORY)
+if(NOT(hasParent))
+ # Link compile database for clangd if we are the master project
+ execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+ "${CMAKE_BINARY_DIR}/compile_commands.json"
+ "${CMAKE_SOURCE_DIR}/compile_commands.json")
+endif()
message(STATUS "********************************************")
message(STATUS "********** ${PROJECT_NAME} build options : **********")
+message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+message(STATUS "Coverage: ${WITH_COVERAGE}")
message(STATUS "zlib support: ${WITH_ZLIB}")
message(STATUS "libgcrypt support: ${WITH_GCRYPT}")
message(STATUS "libmbedTLS support: ${WITH_MBEDTLS}")
@@ -241,7 +231,7 @@ message(STATUS "Unit testing: ${UNIT_TESTING}")
message(STATUS "Client code testing: ${CLIENT_TESTING}")
message(STATUS "Blowfish cipher support: ${WITH_BLOWFISH_CIPHER}")
message(STATUS "PKCS #11 URI support: ${WITH_PKCS11_URI}")
-message(STATUS "DSA support: ${WITH_DSA}")
+message(STATUS "With PKCS #11 provider support: ${WITH_PKCS11_PROVIDER}")
set(_SERVER_TESTING OFF)
if (WITH_SERVER)
set(_SERVER_TESTING ${SERVER_TESTING})
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 71c449b0..8e26015c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,7 +6,7 @@ For contributions we prefer Merge Requests on Gitlab:
https://gitlab.com/libssh/libssh-mirror/
-This way you get contintious integration which runs the complete libssh
+This way you get continuous integration which runs the complete libssh
testsuite for you.
For larger code changes, breaking the changes up into a set of simple
@@ -274,7 +274,7 @@ This is bad:
* This is a multi line comment,
* with some more words...*/
-### Indention & Whitespace & 80 columns
+### Indentation & Whitespace & 80 columns
To avoid confusion, indentations have to be 4 spaces. Do not use tabs!. When
wrapping parameters for function calls, align the parameter list with the first
@@ -478,6 +478,45 @@ Macros like `STATUS_NOT_OK_RETURN` that change control flow (return/goto/etc)
from within the macro are considered bad, because they look like function calls
that never change control flow. Please do not introduce them.
+### Switch/case indentation
+
+The `case` should not be indented to avoid wasting too much horizontal space.
+When the case block contains local variables that need to be wrapped in braces,
+they should not be indented again either.
+
+Good example:
+
+ switch (x) {
+ case 0:
+ do_stuff();
+ break;
+ case 1: {
+ int y;
+ do_stuff();
+ break;
+ }
+ default:
+ do_other_stuff();
+ break;
+ }
+
+Bad example:
+
+ switch (x) {
+ case 0:
+ do_stuff();
+ break;
+ case 1:
+ {
+ int y;
+ do_stuff();
+ break;
+ }
+ default:
+ do_other_stuff();
+ break;
+ }
+
Have fun and happy libssh hacking!
diff --git a/CPackConfig.cmake b/CPackConfig.cmake
index c4a3598a..5bd52c56 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/;/[.]clangd/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch")
+set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]git/;/[.]clangd/;/[.]cache/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
### NSIS INSTALLER
diff --git a/CompilerChecks.cmake b/CompilerChecks.cmake
index 5bdc05c3..7a25b592 100644
--- a/CompilerChecks.cmake
+++ b/CompilerChecks.cmake
@@ -43,6 +43,12 @@ if (UNIX)
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)
+ add_c_compiler_flag("-Wold-style-definition" SUPPORTED_COMPILER_FLAGS)
+ add_c_compiler_flag("-Werror=old-style-definition" SUPPORTED_COMPILER_FLAGS)
+ add_c_compiler_flag("-Wimplicit-int" SUPPORTED_COMPILER_FLAGS)
+ add_c_compiler_flag("-Werror=implicit-int" SUPPORTED_COMPILER_FLAGS)
+ add_c_compiler_flag("-Wint-conversion" SUPPORTED_COMPILER_FLAGS)
+ add_c_compiler_flag("-Werror=int-conversion" SUPPORTED_COMPILER_FLAGS)
check_c_compiler_flag("-Wformat" REQUIRED_FLAGS_WFORMAT)
if (REQUIRED_FLAGS_WFORMAT)
@@ -70,7 +76,7 @@ if (UNIX)
check_c_compiler_flag_ssp("-fstack-protector-strong" WITH_STACK_PROTECTOR_STRONG)
if (WITH_STACK_PROTECTOR_STRONG)
list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-protector-strong")
- # This is needed as Solaris has a seperate libssp
+ # This is needed as Solaris has a separate libssp
if (SOLARIS)
list(APPEND SUPPORTED_LINKER_FLAGS "-fstack-protector-strong")
endif()
@@ -78,16 +84,18 @@ if (UNIX)
check_c_compiler_flag_ssp("-fstack-protector" WITH_STACK_PROTECTOR)
if (WITH_STACK_PROTECTOR)
list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-protector")
- # This is needed as Solaris has a seperate libssp
+ # This is needed as Solaris has a separate libssp
if (SOLARIS)
list(APPEND SUPPORTED_LINKER_FLAGS "-fstack-protector")
endif()
endif()
endif (WITH_STACK_PROTECTOR_STRONG)
- check_c_compiler_flag_ssp("-fstack-clash-protection" WITH_STACK_CLASH_PROTECTION)
- if (WITH_STACK_CLASH_PROTECTION)
- list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-clash-protection")
+ if (NOT WINDOWS AND NOT CYGWIN)
+ check_c_compiler_flag_ssp("-fstack-clash-protection" WITH_STACK_CLASH_PROTECTION)
+ if (WITH_STACK_CLASH_PROTECTION)
+ list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-clash-protection")
+ endif()
endif()
if (PICKY_DEVELOPER)
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index 384e7856..64aaea07 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -44,6 +44,8 @@ int main(void){ return 0; }
endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2)
# HEADER FILES
+check_function_exists(argp_parse HAVE_ARGP_PARSE)
+
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${ARGP_INCLUDE_DIR})
check_include_file(argp.h HAVE_ARGP_H)
unset(CMAKE_REQUIRED_INCLUDES)
@@ -74,76 +76,32 @@ if (WIN32)
endif (WIN32)
if (OPENSSL_FOUND)
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+ set(CMAKE_REQUIRED_LIBRARIES OpenSSL::Crypto)
+
check_include_file(openssl/des.h HAVE_OPENSSL_DES_H)
if (NOT HAVE_OPENSSL_DES_H)
message(FATAL_ERROR "Could not detect openssl/des.h")
endif()
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/aes.h HAVE_OPENSSL_AES_H)
if (NOT HAVE_OPENSSL_AES_H)
message(FATAL_ERROR "Could not detect openssl/aes.h")
endif()
if (WITH_BLOWFISH_CIPHER)
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/blowfish.h HAVE_OPENSSL_BLOWFISH_H)
endif()
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/ecdh.h HAVE_OPENSSL_ECDH_H)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/ec.h HAVE_OPENSSL_EC_H)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
check_include_file(openssl/ecdsa.h HAVE_OPENSSL_ECDSA_H)
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_KDF_CTX_new_id HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
+ check_function_exists(EVP_KDF_CTX_new HAVE_OPENSSL_EVP_KDF_CTX_NEW)
check_function_exists(FIPS_mode HAVE_OPENSSL_FIPS_MODE)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
- check_function_exists(EVP_DigestSign HAVE_OPENSSL_EVP_DIGESTSIGN)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
- 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_LIBRARIES})
- check_symbol_exists(EVP_PKEY_ED25519 "openssl/evp.h" FOUND_OPENSSL_ED25519)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
- check_symbol_exists(EVP_PKEY_POLY1305 "openssl/evp.h" HAVE_OPENSSL_EVP_POLY1305)
-
- 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_LIBRARIES})
- check_symbol_exists(EVP_PKEY_X25519 "openssl/evp.h" HAVE_OPENSSL_X25519)
-
- unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
endif()
@@ -159,13 +117,12 @@ if (NOT WITH_GCRYPT AND NOT WITH_MBEDTLS)
if (HAVE_OPENSSL_ECC)
set(HAVE_ECC 1)
endif (HAVE_OPENSSL_ECC)
-endif ()
-if (WITH_DSA)
- if (NOT WITH_MBEDTLS)
- set(HAVE_DSA 1)
- endif (NOT WITH_MBEDTLS)
-endif()
+ if (HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID OR HAVE_OPENSSL_EVP_KDF_CTX_NEW)
+ set(HAVE_OPENSSL_EVP_KDF_CTX 1)
+ endif (HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID OR HAVE_OPENSSL_EVP_KDF_CTX_NEW)
+
+endif ()
# FUNCTIONS
@@ -311,7 +268,7 @@ int main(void) {
# For detecting attributes we need to treat warnings as
# errors
if (UNIX OR MINGW)
- # Get warnings for attributs
+ # Get warnings for attributes
check_c_compiler_flag("-Wattributes" REQUIRED_FLAGS_WERROR)
if (REQUIRED_FLAGS_WERROR)
string(APPEND CMAKE_REQUIRED_FLAGS "-Wattributes ")
@@ -366,6 +323,23 @@ int main(void) {
return 0;
}" HAVE_FALLTHROUGH_ATTRIBUTE)
+check_c_source_compiles("
+#define WEAK __attribute__((weak))
+
+WEAK int sum(int a, int b)
+{
+ return a + b;
+}
+
+int main(void)
+{
+ int i = sum(2, 2);
+
+ (void)i;
+
+ return 0;
+}" HAVE_WEAK_ATTRIBUTE)
+
if (NOT WIN32)
check_c_source_compiles("
#define __unused __attribute__((unused))
@@ -463,18 +437,21 @@ if (WITH_PKCS11_URI)
if (WITH_GCRYPT)
message(FATAL_ERROR "PKCS #11 is not supported for gcrypt.")
set(WITH_PKCS11_URI 0)
- endif()
- if (WITH_MBEDTLS)
+ elseif (WITH_MBEDTLS)
message(FATAL_ERROR "PKCS #11 is not supported for mbedcrypto")
set(WITH_PKCS11_URI 0)
- endif()
-endif()
-
-if (WITH_MBEDTLS)
- if (WITH_DSA)
- message(FATAL_ERROR "DSA is not supported with mbedTLS crypto")
- set(HAVE_DSA 0)
- endif()
+ elseif (OPENSSL_FOUND AND OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0")
+ find_library(PKCS11_PROVIDER
+ NAMES
+ pkcs11.so
+ PATH_SUFFIXES
+ ossl-modules
+ )
+ if (NOT PKCS11_PROVIDER)
+ set(WITH_PKCS11_PROVIDER 0)
+ message(WARNING "Could not find pkcs11 provider! Falling back to engines")
+ endif (NOT PKCS11_PROVIDER)
+ endif ()
endif()
# ENDIAN
diff --git a/DefineOptions.cmake b/DefineOptions.cmake
index 068db988..7401e86d 100644
--- a/DefineOptions.cmake
+++ b/DefineOptions.cmake
@@ -2,10 +2,9 @@ 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_DEBUG_CRYPTO "Build with cryto debug output" OFF)
+option(WITH_DEBUG_CRYPTO "Build with crypto debug output" OFF)
option(WITH_DEBUG_PACKET "Build with packet debug output" OFF)
option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON)
-option(WITH_DSA "Build with DSA" OFF)
option(WITH_GCRYPT "Compile against libgcrypt" OFF)
option(WITH_MBEDTLS "Compile against libmbedtls" OFF)
option(WITH_BLOWFISH_CIPHER "Compile with blowfish support" OFF)
@@ -13,10 +12,11 @@ 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(WITH_PKCS11_URI "Build with PKCS#11 URI support" OFF)
+option(WITH_PKCS11_PROVIDER "Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
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)
-option(WITH_BENCHMARKS "Build benchmarks tools" OFF)
+option(WITH_BENCHMARKS "Build benchmarks tools; enables unit testing and client tests" OFF)
option(WITH_EXAMPLES "Build examples" ON)
option(WITH_NACL "Build with libnacl (curve25519)" ON)
option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON)
diff --git a/INSTALL b/INSTALL
index c2eae34b..7ed4ff20 100644
--- a/INSTALL
+++ b/INSTALL
@@ -7,11 +7,13 @@
In order to build libssh, you need to install several components:
- A C compiler
-- [CMake](https://www.cmake.org) >= 3.3.0
-- [openssl](https://www.openssl.org) >= 1.0.1
-or
-- [gcrypt](https://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4
+- [CMake](https://www.cmake.org) >= 3.5.0
- [libz](https://www.zlib.net) >= 1.2
+- [openssl](https://www.openssl.org) >= 1.1.1
+or
+- [gcrypt](https://www.gnu.org/directory/Security/libgcrypt.html) >= 1.5
+or
+- [Mbed TLS](https://www.trustedfirmware.org/projects/mbed-tls/)
optional:
- [cmocka](https://cmocka.org/) >= 1.1.0
@@ -19,6 +21,7 @@ optional:
- [nss_wrapper](https://cwrap.org/) >= 1.1.2
- [uid_wrapper](https://cwrap.org/) >= 1.2.0
- [pam_wrapper](https://cwrap.org/) >= 1.0.1
+- [priv_wrapper](https://cwrap.org/) >= 1.0.0
Note that these version numbers are version we know works correctly. If you
build and run libssh successfully with an older version, please let us know.
@@ -39,7 +42,7 @@ GNU/Linux, MacOS X, MSYS/MinGW:
cmake -DUNIT_TESTING=ON -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
make
-On Windows you should choose a makefile gernerator with -G or use
+On Windows you should choose a makefile generator with -G or use
cmake-gui.exe ..
diff --git a/cmake/Modules/AddCMockaTest.cmake b/cmake/Modules/AddCMockaTest.cmake
index 4b0c2dad..79178183 100644
--- a/cmake/Modules/AddCMockaTest.cmake
+++ b/cmake/Modules/AddCMockaTest.cmake
@@ -116,5 +116,9 @@ function(ADD_CMOCKA_TEST _TARGET_NAME)
add_test(${_TARGET_NAME}
${TARGET_SYSTEM_EMULATOR} ${_TARGET_NAME}
)
+ if (WITH_COVERAGE)
+ include(CodeCoverage)
+ append_coverage_compiler_flags_to_target(${_TARGET_NAME})
+ endif (WITH_COVERAGE)
endfunction (ADD_CMOCKA_TEST)
diff --git a/cmake/Modules/CodeCoverage.cmake b/cmake/Modules/CodeCoverage.cmake
new file mode 100644
index 00000000..0fd70ae2
--- /dev/null
+++ b/cmake/Modules/CodeCoverage.cmake
@@ -0,0 +1,750 @@
+# Copyright (c) 2012 - 2017, Lars Bilke
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. 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.
+#
+# 3. Neither the name of the copyright holder nor the names of its 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 HOLDER 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.
+#
+# CHANGES:
+#
+# 2012-01-31, Lars Bilke
+# - Enable Code Coverage
+#
+# 2013-09-17, Joakim Söderberg
+# - Added support for Clang.
+# - Some additional usage instructions.
+#
+# 2016-02-03, Lars Bilke
+# - Refactored functions to use named parameters
+#
+# 2017-06-02, Lars Bilke
+# - Merged with modified version from github.com/ufz/ogs
+#
+# 2019-05-06, Anatolii Kurotych
+# - Remove unnecessary --coverage flag
+#
+# 2019-12-13, FeRD (Frank Dana)
+# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
+# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
+# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
+# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
+# - Set lcov basedir with -b argument
+# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
+# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
+# - Delete output dir, .info file on 'make clean'
+# - Remove Python detection, since version mismatches will break gcovr
+# - Minor cleanup (lowercase function names, update examples...)
+#
+# 2019-12-19, FeRD (Frank Dana)
+# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
+#
+# 2020-01-19, Bob Apthorpe
+# - Added gfortran support
+#
+# 2020-02-17, FeRD (Frank Dana)
+# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
+# in EXCLUDEs, and remove manual escaping from gcovr targets
+#
+# 2021-01-19, Robin Mueller
+# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
+# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
+# flags to the gcovr command
+#
+# 2020-05-04, Mihchael Davis
+# - Add -fprofile-abs-path to make gcno files contain absolute paths
+# - Fix BASE_DIRECTORY not working when defined
+# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
+#
+# 2021-05-10, Martin Stump
+# - Check if the generator is multi-config before warning about non-Debug builds
+#
+# 2022-02-22, Marko Wehle
+# - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively.
+# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
+#
+# 2022-09-28, Sebastian Mueller
+# - fix append_coverage_compiler_flags_to_target to correctly add flags
+# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent)
+#
+# USAGE:
+#
+# 1. Copy this file into your cmake modules path.
+#
+# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
+# using a CMake option() to enable it just optionally):
+# include(CodeCoverage)
+#
+# 3. Append necessary compiler flags for all supported source files:
+# append_coverage_compiler_flags()
+# Or for specific target:
+# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
+#
+# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
+#
+# 4. If you need to exclude additional directories from the report, specify them
+# using full paths in the COVERAGE_EXCLUDES variable before calling
+# setup_target_for_coverage_*().
+# Example:
+# set(COVERAGE_EXCLUDES
+# '${PROJECT_SOURCE_DIR}/src/dir1/*'
+# '/path/to/my/src/dir2/*')
+# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
+# Example:
+# setup_target_for_coverage_lcov(
+# NAME coverage
+# EXECUTABLE testrunner
+# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
+#
+# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
+# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
+# Example:
+# set(COVERAGE_EXCLUDES "dir1/*")
+# setup_target_for_coverage_gcovr_html(
+# NAME coverage
+# EXECUTABLE testrunner
+# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
+# EXCLUDE "dir2/*")
+#
+# 5. Use the functions described below to create a custom make target which
+# runs your test executable and produces a code coverage report.
+#
+# 6. Build a Debug build:
+# cmake -DCMAKE_BUILD_TYPE=Debug ..
+# make
+# make my_coverage_target
+#
+
+include(CMakeParseArguments)
+
+option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
+
+# Check prereqs
+find_program( GCOV_PATH gcov )
+find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
+find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
+find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
+find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
+find_program( CPPFILT_PATH NAMES c++filt )
+
+if(NOT GCOV_PATH)
+ message(FATAL_ERROR "gcov not found! Aborting...")
+endif() # NOT GCOV_PATH
+
+# Check supported compiler (Clang, GNU and Flang)
+get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+foreach(LANG ${LANGUAGES})
+ if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
+ if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
+ message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
+ endif()
+ elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU"
+ AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang")
+ message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...")
+ endif()
+endforeach()
+
+set(COVERAGE_COMPILER_FLAGS "-g --coverage -fprofile-update=atomic"
+ CACHE INTERNAL "")
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
+ include(CheckCXXCompilerFlag)
+ check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path)
+ if(HAVE_cxx_fprofile_abs_path)
+ set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
+ endif()
+endif()
+if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
+ include(CheckCCompilerFlag)
+ check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path)
+ if(HAVE_c_fprofile_abs_path)
+ set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
+ endif()
+endif()
+
+set(CMAKE_Fortran_FLAGS_COVERAGE
+ ${COVERAGE_COMPILER_FLAGS}
+ CACHE STRING "Flags used by the Fortran compiler during coverage builds."
+ FORCE )
+set(CMAKE_CXX_FLAGS_COVERAGE
+ ${COVERAGE_COMPILER_FLAGS}
+ CACHE STRING "Flags used by the C++ compiler during coverage builds."
+ FORCE )
+set(CMAKE_C_FLAGS_COVERAGE
+ ${COVERAGE_COMPILER_FLAGS}
+ CACHE STRING "Flags used by the C compiler during coverage builds."
+ FORCE )
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
+ ""
+ CACHE STRING "Flags used for linking binaries during coverage builds."
+ FORCE )
+set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
+ ""
+ CACHE STRING "Flags used by the shared libraries linker during coverage builds."
+ FORCE )
+mark_as_advanced(
+ CMAKE_Fortran_FLAGS_COVERAGE
+ CMAKE_CXX_FLAGS_COVERAGE
+ CMAKE_C_FLAGS_COVERAGE
+ CMAKE_EXE_LINKER_FLAGS_COVERAGE
+ CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
+
+get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
+ message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
+endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+ link_libraries(gcov)
+endif()
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_lcov(
+# NAME testrunner_coverage # New target name
+# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES testrunner # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
+# # to BASE_DIRECTORY, with CMake 3.4+)
+# NO_DEMANGLE # Don't demangle C++ symbols
+# # even if c++filt is found
+# )
+function(setup_target_for_coverage_lcov)
+
+ set(options NO_DEMANGLE SONARQUBE)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT LCOV_PATH)
+ message(FATAL_ERROR "lcov not found! Aborting...")
+ endif() # NOT LCOV_PATH
+
+ if(NOT GENHTML_PATH)
+ message(FATAL_ERROR "genhtml not found! Aborting...")
+ endif() # NOT GENHTML_PATH
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(DEFINED Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (CMake 3.4+: Also compute absolute paths)
+ set(LCOV_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
+ if(CMAKE_VERSION VERSION_GREATER 3.4)
+ get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
+ endif()
+ list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES LCOV_EXCLUDES)
+
+ # Conditional arguments
+ if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
+ set(GENHTML_EXTRA_ARGS "--demangle-cpp")
+ endif()
+
+ # Setting up commands which will be run to generate coverage data.
+ # Cleanup lcov
+ set(LCOV_CLEAN_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
+ -b ${BASEDIR} --zerocounters
+ )
+ # Create baseline to make sure untouched files show up in the report
+ set(LCOV_BASELINE_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
+ ${BASEDIR} -o ${Coverage_NAME}.base
+ )
+ # Run tests
+ set(LCOV_EXEC_TESTS_CMD
+ ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+ )
+ # Capturing lcov counters and generating report
+ set(LCOV_CAPTURE_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
+ ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
+ )
+ # add baseline counters
+ set(LCOV_BASELINE_COUNT_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
+ -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
+ )
+ # filter collected data to final coverage report
+ set(LCOV_FILTER_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
+ ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
+ )
+ # Generate HTML output
+ set(LCOV_GEN_HTML_CMD
+ ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
+ ${Coverage_NAME} ${Coverage_NAME}.info
+ )
+ if(${Coverage_SONARQUBE})
+ # Generate SonarQube output
+ set(GCOVR_XML_CMD
+ ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
+ ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
+ )
+ set(GCOVR_XML_CMD_COMMAND
+ COMMAND ${GCOVR_XML_CMD}
+ )
+ set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml)
+ set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.")
+ endif()
+
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Executed command report")
+ message(STATUS "Command to clean up lcov: ")
+ string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
+ message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
+
+ message(STATUS "Command to create baseline: ")
+ string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
+ message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
+
+ message(STATUS "Command to run the tests: ")
+ string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
+ message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
+
+ message(STATUS "Command to capture counters and generate report: ")
+ string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
+ message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
+
+ message(STATUS "Command to add baseline counters: ")
+ string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
+ message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
+
+ message(STATUS "Command to filter collected data: ")
+ string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
+ message(STATUS "${LCOV_FILTER_CMD_SPACED}")
+
+ message(STATUS "Command to generate lcov HTML output: ")
+ string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
+ message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
+
+ if(${Coverage_SONARQUBE})
+ message(STATUS "Command to generate SonarQube XML output: ")
+ string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
+ message(STATUS "${GCOVR_XML_CMD_SPACED}")
+ endif()
+ endif()
+
+ # Setup target
+ add_custom_target(${Coverage_NAME}
+ COMMAND ${LCOV_CLEAN_CMD}
+ COMMAND ${LCOV_BASELINE_CMD}
+ COMMAND ${LCOV_EXEC_TESTS_CMD}
+ COMMAND ${LCOV_CAPTURE_CMD}
+ COMMAND ${LCOV_BASELINE_COUNT_CMD}
+ COMMAND ${LCOV_FILTER_CMD}
+ COMMAND ${LCOV_GEN_HTML_CMD}
+ ${GCOVR_XML_CMD_COMMAND}
+
+ # Set output files as GENERATED (will be removed on 'make clean')
+ BYPRODUCTS
+ ${Coverage_NAME}.base
+ ${Coverage_NAME}.capture
+ ${Coverage_NAME}.total
+ ${Coverage_NAME}.info
+ ${GCOVR_XML_CMD_BYPRODUCTS}
+ ${Coverage_NAME}/index.html
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
+ )
+
+ # Show where to find the lcov info report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
+ ${GCOVR_XML_CMD_COMMENT}
+ )
+
+ # Show info where to find the report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
+ )
+
+endfunction() # setup_target_for_coverage_lcov
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_gcovr_xml(
+# NAME ctest_coverage # New target name
+# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES executable_target # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
+# # to BASE_DIRECTORY, with CMake 3.4+)
+# )
+# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
+# GCVOR command.
+function(setup_target_for_coverage_gcovr_xml)
+
+ set(options NONE)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT GCOVR_PATH)
+ message(FATAL_ERROR "gcovr not found! Aborting...")
+ endif() # NOT GCOVR_PATH
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(DEFINED Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (CMake 3.4+: Also compute absolute paths)
+ set(GCOVR_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
+ if(CMAKE_VERSION VERSION_GREATER 3.4)
+ get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
+ endif()
+ list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
+
+ # Combine excludes to several -e arguments
+ set(GCOVR_EXCLUDE_ARGS "")
+ foreach(EXCLUDE ${GCOVR_EXCLUDES})
+ list(APPEND GCOVR_EXCLUDE_ARGS "-e")
+ list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
+ endforeach()
+
+ # Set up commands which will be run to generate coverage data
+ # Run tests
+ set(GCOVR_XML_EXEC_TESTS_CMD
+ ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+ )
+ # Running gcovr
+ set(GCOVR_XML_CMD
+ ${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
+ ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
+ )
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Executed command report")
+
+ message(STATUS "Command to run tests: ")
+ string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
+ message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
+
+ message(STATUS "Command to generate gcovr XML coverage data: ")
+ string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
+ message(STATUS "${GCOVR_XML_CMD_SPACED}")
+ endif()
+
+ add_custom_target(${Coverage_NAME}
+ COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
+ COMMAND ${GCOVR_XML_CMD}
+
+ BYPRODUCTS ${Coverage_NAME}.xml
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Running gcovr to produce Cobertura code coverage report."
+ )
+
+ # Show info where to find the report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
+ )
+endfunction() # setup_target_for_coverage_gcovr_xml
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_gcovr_html(
+# NAME ctest_coverage # New target name
+# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES executable_target # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
+# # to BASE_DIRECTORY, with CMake 3.4+)
+# )
+# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
+# GCVOR command.
+function(setup_target_for_coverage_gcovr_html)
+
+ set(options NONE)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT GCOVR_PATH)
+ message(FATAL_ERROR "gcovr not found! Aborting...")
+ endif() # NOT GCOVR_PATH
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(DEFINED Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (CMake 3.4+: Also compute absolute paths)
+ set(GCOVR_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
+ if(CMAKE_VERSION VERSION_GREATER 3.4)
+ get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
+ endif()
+ list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
+
+ # Combine excludes to several -e arguments
+ set(GCOVR_EXCLUDE_ARGS "")
+ foreach(EXCLUDE ${GCOVR_EXCLUDES})
+ list(APPEND GCOVR_EXCLUDE_ARGS "-e")
+ list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
+ endforeach()
+
+ # Set up commands which will be run to generate coverage data
+ # Run tests
+ set(GCOVR_HTML_EXEC_TESTS_CMD
+ ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+ )
+ # Create folder
+ set(GCOVR_HTML_FOLDER_CMD
+ ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
+ )
+ # Running gcovr
+ set(GCOVR_HTML_CMD
+ ${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
+ ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
+ )
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Executed command report")
+
+ message(STATUS "Command to run tests: ")
+ string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
+ message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
+
+ message(STATUS "Command to create a folder: ")
+ string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
+ message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
+
+ message(STATUS "Command to generate gcovr HTML coverage data: ")
+ string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
+ message(STATUS "${GCOVR_HTML_CMD_SPACED}")
+ endif()
+
+ add_custom_target(${Coverage_NAME}
+ COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
+ COMMAND ${GCOVR_HTML_FOLDER_CMD}
+ COMMAND ${GCOVR_HTML_CMD}
+
+ BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Running gcovr to produce HTML code coverage report."
+ )
+
+ # Show info where to find the report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
+ )
+
+endfunction() # setup_target_for_coverage_gcovr_html
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_fastcov(
+# NAME testrunner_coverage # New target name
+# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES testrunner # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
+# NO_DEMANGLE # Don't demangle C++ symbols
+# # even if c++filt is found
+# SKIP_HTML # Don't create html report
+# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
+# )
+function(setup_target_for_coverage_fastcov)
+
+ set(options NO_DEMANGLE SKIP_HTML)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT FASTCOV_PATH)
+ message(FATAL_ERROR "fastcov not found! Aborting...")
+ endif()
+
+ if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
+ message(FATAL_ERROR "genhtml not found! Aborting...")
+ endif()
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (Patterns, not paths, for fastcov)
+ set(FASTCOV_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
+ list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
+
+ # Conditional arguments
+ if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
+ set(GENHTML_EXTRA_ARGS "--demangle-cpp")
+ endif()
+
+ # Set up commands which will be run to generate coverage data
+ set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
+
+ set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
+ --search-directory ${BASEDIR}
+ --process-gcno
+ --output ${Coverage_NAME}.json
+ --exclude ${FASTCOV_EXCLUDES}
+ )
+
+ set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
+ -C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info
+ )
+
+ if(Coverage_SKIP_HTML)
+ set(FASTCOV_HTML_CMD ";")
+ else()
+ set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
+ -o ${Coverage_NAME} ${Coverage_NAME}.info
+ )
+ endif()
+
+ set(FASTCOV_POST_CMD ";")
+ if(Coverage_POST_CMD)
+ set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
+ endif()
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
+
+ message(" Running tests:")
+ string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
+ message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
+
+ message(" Capturing fastcov counters and generating report:")
+ string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
+ message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
+
+ message(" Converting fastcov .json to lcov .info:")
+ string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
+ message(" ${FASTCOV_CONVERT_CMD_SPACED}")
+
+ if(NOT Coverage_SKIP_HTML)
+ message(" Generating HTML report: ")
+ string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
+ message(" ${FASTCOV_HTML_CMD_SPACED}")
+ endif()
+ if(Coverage_POST_CMD)
+ message(" Running post command: ")
+ string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
+ message(" ${FASTCOV_POST_CMD_SPACED}")
+ endif()
+ endif()
+
+ # Setup target
+ add_custom_target(${Coverage_NAME}
+
+ # Cleanup fastcov
+ COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
+ --search-directory ${BASEDIR}
+ --zerocounters
+
+ COMMAND ${FASTCOV_EXEC_TESTS_CMD}
+ COMMAND ${FASTCOV_CAPTURE_CMD}
+ COMMAND ${FASTCOV_CONVERT_CMD}
+ COMMAND ${FASTCOV_HTML_CMD}
+ COMMAND ${FASTCOV_POST_CMD}
+
+ # Set output files as GENERATED (will be removed on 'make clean')
+ BYPRODUCTS
+ ${Coverage_NAME}.info
+ ${Coverage_NAME}.json
+ ${Coverage_NAME}/index.html # report directory
+
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
+ )
+
+ set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.")
+ if(NOT Coverage_SKIP_HTML)
+ string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
+ endif()
+ # Show where to find the fastcov info report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
+ )
+
+endfunction() # setup_target_for_coverage_fastcov
+
+function(append_coverage_compiler_flags)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+ set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+ message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
+endfunction() # append_coverage_compiler_flags
+
+# Setup coverage for specific library
+function(append_coverage_compiler_flags_to_target name)
+ separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
+ target_compile_options(${name} PRIVATE ${_flag_list})
+ if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+ target_link_libraries(${name} PRIVATE gcov)
+ endif()
+endfunction()
diff --git a/cmake/Modules/DefineCMakeDefaults.cmake b/cmake/Modules/DefineCMakeDefaults.cmake
index ef4fb337..6f369faa 100644
--- a/cmake/Modules/DefineCMakeDefaults.cmake
+++ b/cmake/Modules/DefineCMakeDefaults.cmake
@@ -6,7 +6,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Put the include dirs which are in the source or build tree
# before all other include dirs, so the headers in the sources
-# are prefered over the already installed ones
+# are preferred over the already installed ones
# since cmake 2.4.1
set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
diff --git a/cmake/Modules/ExtractSymbols.cmake b/cmake/Modules/ExtractSymbols.cmake
index f7829334..95c850b6 100644
--- a/cmake/Modules/ExtractSymbols.cmake
+++ b/cmake/Modules/ExtractSymbols.cmake
@@ -50,15 +50,28 @@ file(READ ${HEADERS_LIST_FILE} HEADERS_LIST)
set(symbols)
foreach(header ${HEADERS_LIST})
+ file(READ ${header} header_content)
# Filter only lines containing the FILTER_PATTERN
- file(STRINGS ${header} contain_filter
- REGEX "^.*${FILTER_PATTERN}.*[(]"
+ # separated from the function name with one optional newline
+ string(REGEX MATCHALL
+ "${FILTER_PATTERN}[^(\n]*\n?[^(\n]*[(]"
+ contain_filter
+ "${header_content}"
+ )
+
+ # Remove the optional newline now
+ string(REGEX REPLACE
+ "(.+)\n?(.*)"
+ "\\1\\2"
+ oneline
+ "${contain_filter}"
)
# Remove function-like macros
- foreach(line ${contain_filter})
- if (NOT ${line} MATCHES ".*#[ ]*define")
+ # and anything with two underscores that sounds suspicious
+ foreach(line ${oneline})
+ if (NOT ${line} MATCHES ".*(#[ ]*define|__)")
list(APPEND not_macro ${line})
endif()
endforeach()
diff --git a/cmake/Modules/FindABIMap.cmake b/cmake/Modules/FindABIMap.cmake
index 5117b498..e7f725d2 100644
--- a/cmake/Modules/FindABIMap.cmake
+++ b/cmake/Modules/FindABIMap.cmake
@@ -220,13 +220,12 @@
# Search for python which is required
if (ABIMap_FIND_REQURIED)
- find_package(PythonInterp REQUIRED)
+ find_package(Python REQUIRED)
else()
- find_package(PythonInterp)
+ find_package(Python)
endif()
-
-if (PYTHONINTERP_FOUND)
+if (TARGET Python::Interpreter)
# Search for abimap tool used to generate the map files
find_program(ABIMAP_EXECUTABLE NAMES abimap DOC "path to the abimap executable")
mark_as_advanced(ABIMAP_EXECUTABLE)
diff --git a/cmake/Modules/FindArgp.cmake b/cmake/Modules/FindArgp.cmake
index 454965ac..13d74637 100644
--- a/cmake/Modules/FindArgp.cmake
+++ b/cmake/Modules/FindArgp.cmake
@@ -1,4 +1,8 @@
# - Try to find ARGP
+#
+# The argp can be either shipped as part of libc (ex. glibc) or as a separate
+# library that requires additional linking (ex. Windows, Mac, musl libc, ...)
+#
# Once done this will define
#
# ARGP_ROOT_DIR - Set this variable to the root installation of ARGP
@@ -60,7 +64,7 @@ if (ARGP_LIBRARY)
endif (ARGP_LIBRARY)
include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(ARGP DEFAULT_MSG ARGP_LIBRARIES ARGP_INCLUDE_DIR)
+find_package_handle_standard_args(Argp DEFAULT_MSG ARGP_LIBRARIES ARGP_INCLUDE_DIR)
# show the ARGP_INCLUDE_DIR and ARGP_LIBRARIES variables only in the advanced view
mark_as_advanced(ARGP_INCLUDE_DIR ARGP_LIBRARIES)
diff --git a/cmake/Modules/FindGSSAPI.cmake b/cmake/Modules/FindGSSAPI.cmake
index 4c3f44b1..d227d8dd 100644
--- a/cmake/Modules/FindGSSAPI.cmake
+++ b/cmake/Modules/FindGSSAPI.cmake
@@ -5,7 +5,7 @@
# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI
#
# Read-Only variables:
-# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found
+# GSSAPI_FLAVOR_MIT - set to TRUE if MIT Kerberos has been found
# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found
# GSSAPI_FOUND - system has GSSAPI
# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
diff --git a/config.h.cmake b/config.h.cmake
index 34690304..391ee162 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -82,30 +82,18 @@
/* Define to 1 if you have the <pthread.h> header file. */
#cmakedefine HAVE_PTHREAD_H 1
-/* Define to 1 if you have eliptic curve cryptography in openssl */
+/* Define to 1 if you have elliptic curve cryptography in openssl */
#cmakedefine HAVE_OPENSSL_ECC 1
-/* Define to 1 if you have eliptic curve cryptography in gcrypt */
+/* Define to 1 if you have elliptic curve cryptography in gcrypt */
#cmakedefine HAVE_GCRYPT_ECC 1
-/* Define to 1 if you have eliptic curve cryptography */
+/* Define to 1 if you have elliptic curve cryptography */
#cmakedefine HAVE_ECC 1
-/* Define to 1 if you have DSA */
-#cmakedefine HAVE_DSA 1
-
-/* Define to 1 if you have gl_flags as a glob_t sturct member */
+/* Define to 1 if you have gl_flags as a glob_t struct 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
-
-/* Define to 1 if you have OpenSSL with Poly1305 support */
-#cmakedefine HAVE_OPENSSL_EVP_POLY1305 1
-
/* Define to 1 if you have gcrypt with ChaCha20/Poly1305 support */
#cmakedefine HAVE_GCRYPT_CHACHA_POLY 1
@@ -114,21 +102,12 @@
/* Define to 1 if you have the `EVP_chacha20' function. */
#cmakedefine HAVE_OPENSSL_EVP_CHACHA20 1
-/* 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 `EVP_KDF_CTX_new_id' or `EVP_KDF_CTX_new` function. */
+#cmakedefine HAVE_OPENSSL_EVP_KDF_CTX 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
-
/* Define to 1 if you have the `snprintf' function. */
#cmakedefine HAVE_SNPRINTF 1
@@ -225,6 +204,7 @@
#cmakedefine HAVE_FALLTHROUGH_ATTRIBUTE 1
#cmakedefine HAVE_UNUSED_ATTRIBUTE 1
+#cmakedefine HAVE_WEAK_ATTRIBUTE 1
#cmakedefine HAVE_CONSTRUCTOR_ATTRIBUTE 1
#cmakedefine HAVE_DESTRUCTOR_ATTRIBUTE 1
@@ -275,6 +255,9 @@
/* Define to 1 if you want to enable PKCS #11 URI support */
#cmakedefine WITH_PKCS11_URI 1
+/* Define to 1 if we want to build a support for PKCS #11 provider. */
+#cmakedefine WITH_PKCS11_PROVIDER 1
+
/*************************** ENDIAN *****************************/
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 259424b4..c8b5a7e5 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -14,11 +14,13 @@ if (DOXYGEN_FOUND)
set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES)
set(DOXYGEN_MARKDOWN_SUPPORT YES)
set(DOXYGEN_FULL_PATH_NAMES NO)
+ set(DOXYGEN_GENERATE_TAGFILE "tags.xml")
set(DOXYGEN_PREDEFINED DOXYGEN
WITH_SERVER
WITH_SFTP
- PRINTF_ATTRIBUTE(x,y))
+ PRINTF_ATTRIBUTE\(x,y\))
+ set(DOXYGEN_DOT_GRAPH_MAX_NODES 100)
set(DOXYGEN_EXCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/that_style)
set(DOXYGEN_HTML_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/that_style/header.html)
@@ -34,6 +36,44 @@ if (DOXYGEN_FOUND)
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/folderclosed.svg
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/folderopen.svg
${CMAKE_CURRENT_SOURCE_DIR}/that_style/js/striped_bg.js)
+ set(DOXYGEN_EXCLUDE_PATTERNS */src/external/* fe25519.h ge25519.h sc25519.h
+ blf.h)
+ set(DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS chacha20_poly1305_keysched,dh_ctx,dh_ctx,dh_keypair,error_struct,
+ packet_struct,pem_get_password_struct,ssh_tokens_st,
+ sftp_attributes_struct,sftp_client_message_struct,
+ sftp_dir_struct,sftp_ext_struct,sftp_file_struct,sftp_message_struct,
+ sftp_packet_struct,sftp_request_queue_struct,sftp_session_struct,
+ sftp_status_message_struct,ssh_agent_state_struct,
+ ssh_agent_struct,ssh_auth_auto_state_struct,ssh_auth_request,
+ ssh_bind_config_keyword_table_s,ssh_bind_config_match_keyword_table_s,
+ ssh_bind_struct,ssh_buffer_struct,ssh_channel_callbacks_struct,
+ ssh_channel_read_termination_struct,ssh_channel_request,
+ ssh_channel_request_open,ssh_channel_struct,ssh_cipher_struct,
+ ssh_common_struct,ssh_config_keyword_table_s,
+ ssh_config_match_keyword_table_s,ssh_connector_struct,
+ ssh_counter_struct,ssh_crypto_struct,ssh_event_fd_wrapper,
+ ssh_event_struct,ssh_global_request,ssh_gssapi_struct,ssh_hmac_struct,
+ ssh_iterator,ssh_kbdint_struct,ssh_kex_struct,ssh_key_struct,
+ ssh_knownhosts_entry,ssh_list,ssh_mac_ctx_struct,ssh_message_struct,
+ ssh_packet_callbacks_struct,ssh_packet_header,ssh_poll_ctx_struct,
+ ssh_poll_handle_struct,ssh_pollfd_struct,ssh_private_key_struct,
+ ssh_public_key_struct,ssh_scp_struct,ssh_service_request,
+ ssh_session_struct,ssh_signature_struct,ssh_socket_struct,
+ ssh_string_struct,ssh_threads_callbacks_struct,ssh_timestamp,)
+ set(DOXYGEN_EXCLUDE_SYMBOLS_MACRO SSH_FXP*,SSH_SOCKET*,SERVERBANNER,SOCKOPT_TYPE_ARG4,SSH_FILEXFER*,
+ SSH_FXF*,SSH_S_*,SFTP_*,NSS_BUFLEN_PASSWD,CLOCK,MAX_LINE_SIZE,
+ PKCS11_URI,KNOWNHOSTS_MAXTYPES,)
+ set(DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS sftp_attributes,sftp_client_message,sftp_dir,sftp_ext,sftp_file,
+ sftp_message,sftp_packet,sftp_request_queue,sftp_session,
+ sftp_status_message,sftp_statvfs_t,poll_fn,ssh_callback_int,
+ ssh_callback_data,ssh_callback_int_int,ssh_message_callback,
+ ssh_channel_callback_int,ssh_channel_callback_data,ssh_callbacks,
+ ssh_gssapi_select_oid_callback,ssh_gssapi_accept_sec_ctx_callback,
+ ssh_gssapi_verify_mic_callback,ssh_server_callbacks,ssh_socket_callbacks,
+ ssh_packet_callbacks,ssh_channel_callbacks,ssh_bind,ssh_bind_callbacks,)
+ set(DOXYGEN_EXCLUDE_SYMBOLS ${DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS}
+ ${DOXYGEN_EXCLUDE_SYMBOLS_MACRO}
+ ${DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS})
# This updates the Doxyfile if we do changes here
set(_doxyfile_template "${CMAKE_BINARY_DIR}/CMakeDoxyfile.in")
@@ -44,6 +84,8 @@ if (DOXYGEN_FOUND)
${CMAKE_SOURCE_DIR}/include/libssh
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR})
+
+ add_custom_target(docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh ${CMAKE_BINARY_DIR})
endif() # DOXYGEN_FOUND
endif() # CMAKE_VERSION
diff --git a/doc/authentication.dox b/doc/authentication.dox
index 3196f645..7d0ab81d 100644
--- a/doc/authentication.dox
+++ b/doc/authentication.dox
@@ -33,6 +33,9 @@ The process of authenticating by public key to a server is the following:
used to authenticate the user).
- then, you retrieve the private key for this key and send a message
proving that you know that private key.
+ - when several identity files are specified, then the order of processing of
+ these files is from the last-mentioned to the first one
+ (if specified in the ~/.ssh/config, then starting from the bottom to the top).
The function ssh_userauth_autopubkey() does this using the available keys in
"~/.ssh/". The return values are the following:
diff --git a/doc/curve25519-sha256@libssh.org.txt b/doc/curve25519-sha256@libssh.org.txt
index 75541902..04d88575 100644
--- a/doc/curve25519-sha256@libssh.org.txt
+++ b/doc/curve25519-sha256@libssh.org.txt
@@ -3,13 +3,13 @@ curve25519-sha256@libssh.org.txt Aris Adamantiadis <aris@badcode.be>
1. Introduction
-This document describes the key exchange methode curve25519-sha256@libssh.org
+This document describes the key exchange method curve25519-sha256@libssh.org
for SSH version 2 protocol. It is provided as an alternative to the existing
key exchange mechanisms based on either Diffie-Hellman or Elliptic Curve Diffie-
Hellman [RFC5656].
The reason is the following : During summer of 2013, revelations from ex-
consultant at NSA Edward Snowden gave proof that NSA willingly inserts backdoors
-into softwares, hardware components and published standards. While it is still
+into software, hardware components and published standards. While it is still
believed that the mathematics behind ECC cryptography are still sound and solid,
some people (including Bruce Schneier [SCHNEIER]), showed their lack of confidence
in NIST-published curves such as nistp256, nistp384, nistp521, for which constant
@@ -42,8 +42,8 @@ The following is an overview of the key exchange process:
Client Server
------ ------
Generate ephemeral key pair.
-SSH_MSG_KEX_ECDH_INIT -------->
- Verify that client public key
+SSH_MSG_KEX_ECDH_INIT -------->
+ Verify that client public key
length is 32 bytes.
Generate ephemeral key pair.
Compute shared secret.
@@ -55,7 +55,7 @@ Compute shared secret.
Generate exchange hash.
Verify server's signature.
-* Optional but strongly recommanded as this protects against MITM attacks.
+* Optional but strongly recommended as this protects against MITM attacks.
This is implemented using the same messages as described in RFC5656 chapter 4
@@ -109,7 +109,7 @@ This number is calculated using the following procedure:
side's public key and the local private key scalar.
The whole 32 bytes of the number X are then converted into a big integer k.
- This conversion follows the network byte order. This step differs from
+ This conversion follows the network byte order. This step differs from
RFC5656.
[RFC5656] https://tools.ietf.org/html/rfc5656
diff --git a/doc/doc_coverage.sh b/doc/doc_coverage.sh
new file mode 100755
index 00000000..2f653275
--- /dev/null
+++ b/doc/doc_coverage.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+################################################################################
+# .doc_coverage.sh #
+# Script to detect overall documentation coverage of libssh. The script uses #
+# doxygen to generate the documentation then parses it's output. #
+# #
+# maintainer: Norbert Pocs <npocs@redhat.com> #
+################################################################################
+BUILD_DIR="$1"
+DOXYFILE_PATH="$BUILD_DIR/doc/Doxyfile.docs"
+INDEX_XML_PATH="$BUILD_DIR/doc/xml/index.xml"
+# filters
+F_EXCLUDE_FILES=' wrapper.h legacy.h crypto.h priv.h chacha.h curve25519.h '
+F_UNDOC_FUNC='(function).*is not documented'
+F_FUNC='kind="function"'
+F_HEADERS='libssh_8h_|group__libssh__'
+F_CUT_BEFORE='.*<name>'
+F_CUT_AFTER='<\/name><\/member>'
+# Doxygen options
+O_QUIET='QUIET=YES'
+O_GEN_XML='GENERATE_XML=YES'
+
+# check if build dir given
+if [ $# -eq 0 ]; then
+ echo "Please provide the build directory e.g.: ./build"
+ exit 255
+fi
+
+# modify doxyfile to our needs:
+# QUIET - less output
+# GENERATE_XML - xml needed to inspect all the functions
+# (note: the options are needed to be on separate lines)
+# We want to exclude irrelevant files
+MOD_DOXYFILE=$(cat "$DOXYFILE_PATH"; echo "$O_QUIET"; echo "$O_GEN_XML")
+MOD_DOXYFILE=${MOD_DOXYFILE//EXCLUDE_PATTERNS.*=/EXCLUDE_PATTERNS=$F_EXCLUDE_FILES/g}
+
+# call doxygen to get the warning messages
+# and also generate the xml for inspection
+DOXY_WARNINGS=$(echo "$MOD_DOXYFILE" | doxygen - 2>&1)
+
+# get the number of undocumented functions
+UNDOC_FUNC=$(echo "$DOXY_WARNINGS" | grep -cE "$F_UNDOC_FUNC")
+
+# filter out the lines consisting of functions of our interest
+FUNC_LINES=$(grep "$F_FUNC" "$INDEX_XML_PATH" | grep -E "$F_HEADERS")
+# cut the irrelevant information and leave just the function names
+ALL_FUNC=$(echo "$FUNC_LINES" | sed -e "s/$F_CUT_BEFORE//g" -e "s/$F_CUT_AFTER//")
+# remove duplicates and get the number of functions
+ALL_FUNC=$(echo "$ALL_FUNC" | sort - | uniq | wc -l)
+
+# percentage of the documented functions
+awk "BEGIN {printf \"Documentation coverage is %.2f%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"
diff --git a/doc/forwarding.dox b/doc/forwarding.dox
index ca3b94f8..2b202b4d 100644
--- a/doc/forwarding.dox
+++ b/doc/forwarding.dox
@@ -165,6 +165,8 @@ int web_server(ssh_session session)
char buffer[256];
int nbytes, nwritten;
int port = 0;
+ char *peer_address = NULL;
+ int peer_port = 0;
char *helloworld = ""
"HTTP/1.1 200 OK\n"
"Content-Type: text/html\n"
@@ -187,7 +189,8 @@ int web_server(ssh_session session)
return rc;
}
- channel = ssh_channel_accept_forward(session, 60000, &port);
+ channel = ssh_channel_open_forward_port(session, 60000, &port,
+ &peer_address, &peer_port);
if (channel == NULL)
{
fprintf(stderr, "Error waiting for incoming connection: %s\n",
@@ -204,6 +207,7 @@ int web_server(ssh_session session)
ssh_get_error(session));
ssh_channel_send_eof(channel);
ssh_channel_free(channel);
+ ssh_string_free_char(peer_address);
return SSH_ERROR;
}
if (strncmp(buffer, "GET /", 5)) continue;
@@ -216,13 +220,15 @@ int web_server(ssh_session session)
ssh_get_error(session));
ssh_channel_send_eof(channel);
ssh_channel_free(channel);
+ ssh_string_free_char(peer_address);
return SSH_ERROR;
}
- printf("Sent answer\n");
+ printf("Sent answer to %s:%d\n", peer_address, peer_port);
}
ssh_channel_send_eof(channel);
ssh_channel_free(channel);
+ ssh_string_free_char(peer_address);
return SSH_OK;
}
@endcode
diff --git a/doc/guided_tour.dox b/doc/guided_tour.dox
index 69576f18..904a739e 100644
--- a/doc/guided_tour.dox
+++ b/doc/guided_tour.dox
@@ -5,7 +5,7 @@
A SSH session goes through the following steps:
- Before connecting to the server, you can set up if you wish one or other
- server public key authentication, i.e. DSA or RSA. You can choose
+ server public key authentication, i.e. RSA, ED25519 or ECDSA. You can choose
cryptographic algorithms you trust and compression algorithms if any. You
must of course set up the hostname.
@@ -15,7 +15,7 @@ A SSH session goes through the following steps:
file.
- The client must authenticate: the classical ways are password, or
- public keys (from dsa and rsa key-pairs generated by openssh).
+ public keys (from ecdsa, ed25519 and rsa key-pairs generated by openssh).
If a SSH agent is running, it is possible to use it.
- Now that the user has been authenticated, you must open one or several
diff --git a/doc/introduction.dox b/doc/introduction.dox
index f2f3d3dd..8d2aa1d5 100644
--- a/doc/introduction.dox
+++ b/doc/introduction.dox
@@ -44,6 +44,10 @@ Table of contents:
@subpage libssh_tutor_threads
+@subpage libssh_tutor_pkcs11
+
+@subpage libssh_tutor_sftp_aio
+
@subpage libssh_tutor_todo
*/
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
index e40d8a15..04197603 100644
--- a/doc/mainpage.dox
+++ b/doc/mainpage.dox
@@ -20,7 +20,7 @@ the interesting functions as you go.
The libssh library provides:
- <strong>Key Exchange Methods</strong>: <i>curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521</i>, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1
- - <strong>Public Key Algorithms</strong>: ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa, rsa-sha2-512, rsa-sha2-256,ssh-dss
+ - <strong>Public Key Algorithms</strong>: ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa, rsa-sha2-512, rsa-sha2-256
- <strong>Ciphers</strong>: <i>aes256-ctr, aes192-ctr, aes128-ctr</i>, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, blowfish-cbc
- <strong>Compression Schemes</strong>: zlib, <i>zlib@openssh.com</i>, none
- <strong>MAC hashes</strong>: hmac-sha1, hmac-sha2-256, hmac-sha2-512, hmac-md5
@@ -38,7 +38,7 @@ The libssh library provides:
@section main-additional-features Additional Features
- Client <b>and</b> server support
- - SSHv2 and SSHv1 protocol support
+ - SSHv2 protocol support
- Supports <a href="https://test.libssh.org/" target="_blank">Linux, UNIX, BSD, Solaris, OS/2 and Windows</a>
- Automated test cases with nightly <a href="https://test.libssh.org/" target="_blank">tests</a>
- Event model based on poll(2), or a poll(2)-emulation.
@@ -149,7 +149,7 @@ The libssh Team
@subsection main-rfc-secsh Secure Shell (SSH)
-The following RFC documents described SSH-2 protcol as an Internet standard.
+The following RFC documents described SSH-2 protocol as an Internet standard.
- <a href="https://tools.ietf.org/html/rfc4250" target="_blank">RFC 4250</a>,
The Secure Shell (SSH) Protocol Assigned Numbers
@@ -213,15 +213,15 @@ It was later modified and expanded by the following RFCs.
Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol
- <a href="https://tools.ietf.org/html/rfc8709" target="_blank">RFC 8709</a>,
Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol
+ - <a href="https://tools.ietf.org/html/rfc8709" target="_blank">RFC 8731</a>,
+ Secure Shell (SSH) Key Exchange Method Using Curve25519 and Curve448
+ - <a href="https://tools.ietf.org/html/rfc9142" target="_blank">RFC 9142</a>,
+ Key Exchange (KEX) Method Updates and Recommendations for Secure Shell (SSH)
There are also drafts that are being currently developed and followed.
- - <a href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-kex-sha2-10" target="_blank">draft-ietf-curdle-ssh-kex-sha2-10</a>
- Key Exchange (KEX) Method Updates and Recommendations for Secure Shell (SSH)
- - <a href="https://tools.ietf.org/html/draft-miller-ssh-agent-03" target="_blank">draft-miller-ssh-agent-03</a>
+ - <a href="https://tools.ietf.org/html/draft-miller-ssh-agent-03" target="_blank">draft-miller-ssh-agent-08</a>
SSH Agent Protocol
- - <a href="https://tools.ietf.org/html/draft-ietf-curdle-ssh-curves-12" target="_blank">draft-ietf-curdle-ssh-curves-12</a>
- Secure Shell (SSH) Key Exchange Method using Curve25519 and Curve448
Interesting cryptography documents:
diff --git a/doc/pkcs11.dox b/doc/pkcs11.dox
index 0bdfc6dc..c2732a81 100644
--- a/doc/pkcs11.dox
+++ b/doc/pkcs11.dox
@@ -9,11 +9,11 @@ objects stored on the tokens can be uniquely identified is called PKCS #11 URI
(Uniform Resource Identifier) and is defined in RFC 7512
(https://tools.ietf.org/html/rfc7512).
-Pre-requisites:
+# Pre-requisites (OpenSSL < 3.0):
-OpenSSL defines an abstract layer called the "engine" to achieve cryptographic
-acceleration. The engine_pkcs11 module acts like an interface between the PKCS #11
-modules and the OpenSSL engine.
+OpenSSL 1.x defines an abstract layer called the "engine" to achieve
+cryptographic acceleration. The engine_pkcs11 module acts like an interface
+between the PKCS #11 modules and the OpenSSL application.
To build and use libssh with PKCS #11 support:
1. Enable the cmake option: $ cmake -DWITH_PKCS11_URI=ON
@@ -21,6 +21,20 @@ To build and use libssh with PKCS #11 support:
3. Install and configure engine_pkcs11 (https://github.com/OpenSC/libp11).
4. Plug in a working smart card or configure softhsm (https://www.opendnssec.org/softhsm).
+# Pre-requisites (OpenSSL 3.0.8+)
+
+The OpenSSL 3.0 is deprecating usage of low-level engines in favor of high-level
+"providers" to provide alternative implementation of cryptographic operations
+or acceleration.
+
+To build and use libssh with PKCS #11 support using OpenSSL providers:
+1. Install and configure pkcs11 provider (https://github.com/latchset/pkcs11-provider).
+2. Enable the cmake options: $ cmake -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
+3. Build with OpenSSL.
+4. Plug in a working smart card or configure softhsm (https://www.opendnssec.org/softhsm).
+
+# New API functions
+
The functions ssh_pki_import_pubkey_file() and ssh_pki_import_privkey_file() that
import the public and private keys from files respectively are now modified to support
PKCS #11 URIs. These functions automatically detect if the provided filename is a file path
@@ -31,7 +45,7 @@ corresponding to the PKCS #11 URI are loaded from the PKCS #11 device.
If you wish to authenticate using public keys on your own, follow the steps mentioned under
"Authentication with public keys" in Chapter 2 - A deeper insight into authentication.
-The function pki_uri_import() is used to populate the public/private ssh_key from the
+The function pki_uri_import() is used to populate the public/private ssh_key from the
engine with PKCS #11 URIs as the look up.
Here is a minimalistic example of public key authentication using PKCS #11 URIs:
@@ -64,4 +78,10 @@ We recommend the users to provide a specific PKCS #11 URI so that it matches onl
If the engine discovers multiple slots that could potentially contain the private keys referenced
by the provided PKCS #11 URI, the engine will not try to authenticate.
+For testing, the SoftHSM PKCS#11 library is used. But it has some issues with
+OpenSSL initialization/cleanup when used with OpenSSL 3.0 so we are using it
+indirectly through a p11-kit remoting as described in the following article:
+
+https://p11-glue.github.io/p11-glue/p11-kit/manual/remoting.html
+
*/
diff --git a/doc/sftp.dox b/doc/sftp.dox
index 1f99cfdf..4c176a4b 100644
--- a/doc/sftp.dox
+++ b/doc/sftp.dox
@@ -139,7 +139,7 @@ Unlike its equivalent in the SCP subsystem, this function does NOT change the
current directory to the newly created subdirectory.
-@subsection sftp_write Copying a file to the remote computer
+@subsection sftp_write Writing to a file on the remote computer
You handle the contents of a remote file just like you would do with a
local file: you open the file in a given mode, move the file pointer in it,
@@ -203,16 +203,14 @@ int sftp_helloworld(ssh_session session, sftp_session sftp)
@subsection sftp_read Reading a file from the remote computer
-The nice thing with reading a file over the network through SFTP is that it
-can be done both in a synchronous way or an asynchronous way. If you read the file
-asynchronously, your program can do something else while it waits for the
-results to come.
-
-Synchronous read is done with sftp_read().
+A synchronous read from a remote file is done using sftp_read(). This
+section describes how to download a remote file using sftp_read(). The
+next section will discuss more about synchronous/asynchronous read/write
+operations using libssh sftp API.
Files are normally transferred in chunks. A good chunk size is 16 KB. The following
example transfers the remote file "/etc/profile" in 16 KB chunks. For each chunk we
-request, sftp_read blocks till the data has been received:
+request, sftp_read() blocks till the data has been received:
@code
// Good chunk size
@@ -273,87 +271,39 @@ int sftp_read_sync(ssh_session session, sftp_session sftp)
}
@endcode
-Asynchronous read is done in two steps, first sftp_async_read_begin(), which
-returns a "request handle", and then sftp_async_read(), which uses that request handle.
-If the file has been opened in nonblocking mode, then sftp_async_read()
-might return SSH_AGAIN, which means that the request hasn't completed yet
-and that the function should be called again later on. Otherwise,
-sftp_async_read() waits for the data to come. To open a file in nonblocking mode,
-call sftp_file_set_nonblocking() right after you opened it. Default is blocking mode.
+@subsection sftp_aio Performing an asynchronous read/write on a file on the remote computer
-The example below reads a very big file in asynchronous, nonblocking, mode. Each
-time the data is not ready yet, a counter is incremented.
+sftp_read() performs a "synchronous" read operation on a remote file.
+This means that sftp_read() will first request the server to read some
+data from the remote file and then would wait until the server response
+containing data to read (or an error) arrives at the client side.
-@code
-// Good chunk size
-#define MAX_XFER_BUF_SIZE 16384
+sftp_write() performs a "synchronous" write operation on a remote file.
+This means that sftp_write() will first request the server to write some
+data to the remote file and then would wait until the server response
+containing information about the status of the write operation arrives at the
+client side.
-int sftp_read_async(ssh_session session, sftp_session sftp)
-{
- int access_type;
- sftp_file file;
- char buffer[MAX_XFER_BUF_SIZE];
- int async_request;
- int nbytes;
- long counter;
- int rc;
+If your client program wants to do something other than waiting for the
+response after requesting a read/write, the synchronous sftp_read() and
+sftp_write() can't be used. In such a case the "asynchronous" sftp aio API
+should be used.
- access_type = O_RDONLY;
- file = sftp_open(sftp, "some_very_big_file",
- access_type, 0);
- if (file == NULL) {
- fprintf(stderr, "Can't open file for reading: %s\n",
- ssh_get_error(session));
- return SSH_ERROR;
- }
- sftp_file_set_nonblocking(file);
-
- async_request = sftp_async_read_begin(file, sizeof(buffer));
- counter = 0L;
- usleep(10000);
- if (async_request >= 0) {
- nbytes = sftp_async_read(file, buffer, sizeof(buffer),
- async_request);
- } else {
- nbytes = -1;
- }
+Please go through @ref libssh_tutor_sftp_aio for a detailed description
+of the sftp aio API.
- while (nbytes > 0 || nbytes == SSH_AGAIN) {
- if (nbytes > 0) {
- write(1, buffer, nbytes);
- async_request = sftp_async_read_begin(file, sizeof(buffer));
- } else {
- counter++;
- }
- usleep(10000);
+The sftp aio API provides two categories of functions :
+ - sftp_aio_begin_*() : For requesting a read/write from the server.
+ - sftp_aio_wait_*() : For waiting for the response of a previously
+ issued read/write request from the server.
- if (async_request >= 0) {
- nbytes = sftp_async_read(file, buffer, sizeof(buffer),
- async_request);
- } else {
- nbytes = -1;
- }
- }
-
- if (nbytes < 0) {
- fprintf(stderr, "Error while reading file: %s\n",
- ssh_get_error(session));
- sftp_close(file);
- return SSH_ERROR;
- }
-
- printf("The counter has reached value: %ld\n", counter);
-
- rc = sftp_close(file);
- if (rc != SSH_OK) {
- fprintf(stderr, "Can't close the read file: %s\n",
- ssh_get_error(session));
- return rc;
- }
+Hence, the client program can call sftp_aio_begin_*() to request a read/write
+and then can perform any number of operations (other than waiting) before
+calling sftp_aio_wait_*() for waiting for the response of the previously
+issued request.
- return SSH_OK;
-}
-@endcode
+We call read/write operations performed in the manner described above as
+"asynchronous" read/write operations on a remote file.
@subsection sftp_ls Listing the contents of a directory
diff --git a/doc/sftp_aio.dox b/doc/sftp_aio.dox
new file mode 100644
index 00000000..9c26f5e1
--- /dev/null
+++ b/doc/sftp_aio.dox
@@ -0,0 +1,705 @@
+/**
+
+@page libssh_tutor_sftp_aio Chapter 10: The SFTP asynchronous I/O
+
+@section sftp_aio_api The SFTP asynchronous I/O
+
+NOTE : Please read @ref libssh_tutor_sftp before reading this page. The
+synchronous sftp_read() and sftp_write() have been described there.
+
+SFTP AIO stands for "SFTP Asynchronous Input/Output". This API contains
+functions which perform async read/write operations on remote files.
+
+File transfers performed using the asynchronous sftp aio API can be
+significantly faster than the file transfers performed using the synchronous
+sftp read/write API (see sftp_read() and sftp_write()).
+
+The sftp aio API functions are divided into two categories :
+ - sftp_aio_begin_*() [see sftp_aio_begin_read(), sftp_aio_begin_write()]:
+ These functions send a request for an i/o operation to the server and
+ provide the caller an sftp aio handle corresponding to the sent request.
+
+ - sftp_aio_wait_*() [see sftp_aio_wait_read(), sftp_aio_wait_write()]:
+ These functions wait for the server response corresponding to a previously
+ issued request. Which request ? the request corresponding to the sftp aio
+ handle supplied by the caller to these functions.
+
+Conceptually, you can think of the sftp aio handle as a request identifier.
+
+Technically, the sftp_aio_begin_*() functions dynamically allocate memory to
+store information about the i/o request they send and provide the caller a
+handle to this memory, we call this handle an sftp aio handle.
+
+sftp_aio_wait_*() functions use the information stored in that memory (handled
+by the caller supplied sftp aio handle) to identify a request, and then they
+wait for that request's response. These functions also release the memory
+handled by the caller supplied sftp aio handle (except when they return
+SSH_AGAIN).
+
+sftp_aio_free() can also be used to release the memory handled by an sftp aio
+handle but unlike the sftp_aio_wait_*() functions, it doesn't wait for a
+response. This should be used to release the memory corresponding to an sftp
+aio handle when some failure occurs. An example has been provided at the
+end of this page to show the usage of sftp_aio_free().
+
+To begin with, this tutorial will provide basic examples that describe the
+usage of sftp aio API to perform a single read/write operation.
+
+The later sections describe the usage of the sftp aio API to obtain faster file
+transfers as compared to the transfers performed using the synchronous sftp
+read/write API.
+
+On encountering an error, the sftp aio API functions set the sftp and ssh
+errors just like any other libssh sftp API function. These errors can be
+obtained using sftp_get_error(), ssh_get_error() and ssh_get_error_code().
+The code examples provided on this page ignore error handling for the sake of
+brevity.
+
+@subsection sftp_aio_read Using the sftp aio API for reading (a basic example)
+
+For performing an async read operation on a sftp file (see sftp_open()),
+the first step is to call sftp_aio_begin_read() to send a read request to the
+server. The caller is provided an sftp aio handle corresponding to the sent
+read request.
+
+The second step is to pass a pointer to this aio handle to
+sftp_aio_wait_read(), this function waits for the server response which
+indicates the success/failure of the read request. On success, the response
+indicates EOF or contains the data read from the sftp file.
+
+The following code example shows how a read operation can be performed
+on an sftp file using the sftp aio API.
+
+@code
+ssize_t read_chunk(sftp_file file, void *buf, size_t to_read)
+{
+ ssize_t bytes_requested, bytes_read;
+
+ // Variable to store an sftp aio handle
+ sftp_aio aio = NULL;
+
+ // Send a read request to the sftp server
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ // Here its possible that (bytes_requested < to_read) as specified in
+ // the function documentation of sftp_aio_begin_read()
+
+ // Wait for the response of the read request corresponding to the
+ // sftp aio handle stored in the aio variable.
+ bytes_read = sftp_aio_wait_read(&aio, buf, to_read);
+ if (bytes_read == SSH_ERROR) {
+ // handle error
+ }
+
+ return bytes_read;
+}
+@endcode
+
+@subsection sftp_aio_write Using the sftp aio API for writing (a basic example)
+
+For performing an async write operation on a sftp file (see sftp_open()),
+the first step is to call sftp_aio_begin_write() to send a write request to
+the server. The caller is provided an sftp aio handle corresponding to the
+sent write request.
+
+The second step is to pass a pointer to this aio handle to
+sftp_aio_wait_write(), this function waits for the server response which
+indicates the success/failure of the write request.
+
+The following code example shows how a write operation can be performed on an
+sftp file using the sftp aio API.
+
+@code
+ssize_t write_chunk(sftp_file file, void *buf, size_t to_write)
+{
+ ssize_t bytes_requested, bytes_written;
+
+ // Variable to store an sftp aio handle
+ sftp_aio aio = NULL;
+
+ // Send a write request to the sftp server
+ bytes_requested = sftp_aio_begin_write(file, buf, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ // Here its possible that (bytes_requested < to_write) as specified in
+ // the function documentation of sftp_aio_begin_write()
+
+ // Wait for the response of the write request corresponding to
+ // the sftp aio handle stored in the aio variable.
+ bytes_written = sftp_aio_wait_write(&aio);
+ if (bytes_written == SSH_ERROR) {
+ // handle error
+ }
+
+ return bytes_written;
+}
+@endcode
+
+@subsection sftp_aio_actual_use Using the sftp aio API to speed up a transfer
+
+The above examples were provided to introduce the sftp aio API.
+This is not how the sftp aio API is intended to be used, because the
+above usage offers no advantage over the synchronous sftp read/write API
+which does the same thing i.e issue a request and then immediately wait for
+its response.
+
+The facility that the sftp aio API provides is that the user can do
+anything between issuing a request and getting the corresponding response.
+Any number of operations can be performed after calling sftp_aio_begin_*()
+[which issues a request] and before calling sftp_aio_wait_*() [which waits
+for a response]
+
+The code can leverage this feature by calling sftp_aio_begin_*() multiple times
+to issue multiple requests before calling sftp_aio_wait_*() to wait for the
+response of an earlier issued request. This approach will keep a certain number
+of requests outstanding at the client side.
+
+After issuing those requests, while the client code does something else (for
+example waiting for an outstanding request's response, processing an obtained
+response, issuing another request or any other operation the client wants
+to perform), at the same time :
+
+ - Some of those outstanding requests may be travelling over the
+ network towards the server.
+
+ - Some of the outstanding requests may have reached the server and may
+ be queued for processing at the server side.
+
+ - Some of the outstanding requests may have been processed and the
+ corresponding responses may be travelling over the network towards the
+ client.
+
+ - Some of the responses corresponding to the outstanding requests may
+ have already reached the client side.
+
+Clearly in this case, operations that the client performs and operations
+involved in transfer/processing of a outstanding request can occur in
+parallel. Also, operations involved in transfer/processing of two or more
+outstanding requests may also occur in parallel (for example when one request
+travels to the server, another request's response may be incoming towards the
+client). Such kind of parallelism makes the overall transfer faster as compared
+to a transfer performed using the synchronous sftp read/write API.
+
+When the synchronous sftp read/write API is used to perform a transfer,
+a strict sequence is followed:
+
+ - The client issues a single read/write request.
+ - Then waits for its response.
+ - On obtaining the response, the client processes it.
+ - After the processing ends, the client issues the next read/write request.
+
+A file transfer performed in this manner would be slower than the case where
+multiple read/write requests are kept outstanding at the client side. Because
+here at any given time, operations related to transfer/processing of only one
+request/response pair occurs. This is in contrast to the multiple outstanding
+requests scenario where operations related to transfer/processing of multiple
+request/response pairs may occur at the same time.
+
+Although it's true that keeping multiple requests outstanding can speed up a
+transfer, those outstanding requests come at a cost of increased memory
+consumption both at the client side and the server side. Hence care must be
+taken to use a reasonable limit for the number of requests kept outstanding.
+
+The further sections provide code examples to show how uploads/downloads
+can be performed using the sftp aio API and the concept of outstanding requests
+discussed in this section. In those code examples, error handling has been
+ignored and at some places pseudo code has been used for the sake of brevity.
+
+The complete code for performing uploads/downloads using the sftp aio API,
+can be found at https://gitlab.com/libssh/libssh-mirror/-/tree/master.
+
+ - libssh benchmarks for uploads performed using the sftp aio API [See
+ tests/benchmarks/bench_sftp.c]
+ - libssh benchmarks for downloads performed using the sftp aio API. [See
+ tests/benchmarks/bench_sftp.c]
+ - libssh sftp ft API code for performing a local to remote transfer (upload).
+ [See src/sftp_ft.c]
+ - libssh sftp ft API code for performing a remote to local transfer
+ (download). [See src/sftp_ft.c]
+
+@subsection sftp_aio_cap Capping applied by the sftp aio API
+
+Before the code examples for uploads and downloads, its important
+to know about the capping applied by the sftp aio API.
+
+sftp_aio_begin_read() caps the number of bytes the caller can request
+to read from the remote file. That cap is the value of the max_read_length
+field of the sftp_limits_t returned by sftp_limits(). Say that cap is LIM
+and the caller passes x as the number of bytes to read to
+sftp_aio_begin_read(), then (assuming no error occurs) :
+
+ - if x <= LIM, then sftp_aio_begin_read() will request the server
+ to read x bytes from the remote file, and will return x.
+
+ - if x > LIM, then sftp_aio_begin_read() will request the server
+ to read LIM bytes from the remote file and will return LIM.
+
+Hence to request server to read x bytes (> LIM), the caller would have
+to call sftp_aio_begin_read() multiple times, typically in a loop and
+break out of the loop when the summation of return values of the multiple
+sftp_aio_begin_read() calls becomes equal to x.
+
+For the sake of simplicity, the code example for download in the upcoming
+section would always ask sftp_aio_begin_read() to read x <= LIM bytes,
+so that its return value is guaranteed to be x, unless an error occurs.
+
+Similarly, sftp_aio_begin_write() caps the number of bytes the caller
+can request to write to the remote file. That cap is the value of
+max_write_length field of the sftp_limits_t returned by sftp_limits().
+Say that cap is LIM and the caller passes x as the number of bytes to
+write to sftp_aio_begin_write(), then (assuming no error occurs) :
+
+ - if x <= LIM, then sftp_aio_begin_write() will request the server
+ to write x bytes to the remote file, and will return x.
+
+ - if x > LIM, then sftp_aio_begin_write() will request the server
+ to write LIM bytes to the remote file and will return LIM.
+
+Hence to request server to write x bytes (> LIM), the caller would have
+to call sftp_aio_begin_write() multiple times, typically in a loop and
+break out of the loop when the summation of return values of the multiple
+sftp_aio_begin_write() calls becomes equal to x.
+
+For the sake of simplicity, the code example for upload in the upcoming
+section would always ask sftp_aio_begin_write() to write x <= LIM bytes,
+so that its return value is guaranteed to be x, unless an error occurs.
+
+@subsection sftp_aio_download_example Performing a download using the sftp aio API
+
+Terminologies used in the following code snippets :
+
+ - sftp : The sftp_session opened using sftp_new() and initialised using
+ sftp_init()
+
+ - file : The sftp file handle of the remote file to download data
+ from. (See sftp_open())
+
+ - file_size : the size of the sftp file to download. This size can be obtained
+ by statting the remote file to download (e.g by using sftp_stat())
+
+ - We will need to maintain a queue which will be used to store the sftp aio
+ handles corresponding to the outstanding requests.
+
+First, we issue the read requests while ensuring that their count
+doesn't exceed a particular limit decided by us, and the number of bytes
+requested don't exceed the size of the file to download.
+
+@code
+sftp_aio aio = NULL;
+
+// Chunk size to use for the transfer
+size_t chunk_size;
+
+// For the limits structure that would be used
+// by the code to set the chunk size
+sftp_limits_t lim = NULL;
+
+// Max number of requests to keep outstanding at a time
+size_t in_flight_requests = 5;
+
+// Number of bytes for which requests have been sent
+size_t total_bytes_requested = 0;
+
+// Number of bytes which have been downloaded
+size_t bytes_downloaded = 0;
+
+// Buffer to use for the download
+char *buffer = NULL;
+
+// Helper variables
+size_t to_read;
+ssize_t bytes_requested;
+
+// Get the sftp limits
+lim = sftp_limits(sftp);
+if (lim == NULL) {
+ // handle error
+}
+
+// Set the chunk size for download = the max limit for reading
+// The reason for this has been given in the "Capping applied by
+// the sftp aio API" section (Its to make the code simpler)
+//
+// Assigning a size_t type variable a uint64_t type value here,
+// theoretically could cause an overflow, but practically
+// max_read_length would never exceed SIZE_MAX so its okay.
+chunk_size = lim->max_read_length;
+
+buffer = malloc(chunk_size);
+if (buffer == NULL) {
+ // handle error
+}
+
+... // Code to open the remote file (to download) using sftp_open().
+... // Code to stat the remote file's file size.
+... // Code to open the local file in which downloaded data is to be stored.
+... // Code to initialize the queue which will be used to store sftp aio
+ // handles.
+
+for (i = 0;
+ i < in_flight_requests && total_bytes_requested < file_size;
+ ++i) {
+ to_read = file_size - total_bytes_requested;
+ if (to_read > chunk_size) {
+ to_read = chunk_size;
+ }
+
+ // Issue a read request
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_read) {
+ // Should not happen for this code, as the to_read is <=
+ // max limit for reading (chunk size), so there is no reason
+ // for sftp_aio_begin_read() to return a lesser value.
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+@endcode
+
+At this point, at max in_flight_requests number of requests may be
+outstanding. Now we wait for the response corresponding to the earliest
+issued outstanding request.
+
+On getting that response, we issue another read request if there are
+still some bytes in the sftp file (to download) for which we haven't sent the
+read request. (This happens when total_bytes_requested < file_size)
+
+This issuing of another read request (under a condition) is done to
+keep the number of outstanding requests equal to the value of the
+in_flight_requests variable.
+
+This process has to be repeated for every remaining outstanding request.
+
+@code
+while (the queue is not empty) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ // Wait for the response of the request corresponding to the aio
+ bytes_read = sftp_aio_wait_read(&aio, buffer, chunk_size);
+ if (bytes_read == SSH_ERROR) {
+ //handle error
+ }
+
+ bytes_downloaded += bytes_read;
+ if (bytes_read != chunk_size && bytes_downloaded != file_size) {
+ // A short read encountered on the remote file before reaching EOF,
+ // short read before reaching EOF should never happen for the sftp aio
+ // API which respects the max limit for reading. This probably
+ // indicates a bad server.
+ }
+
+ // Pseudo code
+ WRITE bytes_read bytes from the buffer into the local file
+ in which downloaded data is to be stored ;
+
+ if (total_bytes_requested == file_size) {
+ // no need to issue more read requests
+ continue;
+ }
+
+ // else issue a read request
+ to_read = file_size - total_bytes_requested;
+ if (to_read > chunk_size) {
+ to_read = chunk_size;
+ }
+
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_read) {
+ // Should not happen for this code, as the to_read is <=
+ // max limit for reading (chunk size), so there is no reason
+ // for sftp_aio_begin_read() to return a lesser value.
+ }
+
+ total_bytes_requested += bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+free(buffer);
+sftp_limits_free(lim);
+
+... // Code to destroy the queue which was used to store the sftp aio
+ // handles.
+@endcode
+
+After exiting the while (the queue is not empty) loop, the download
+would've been complete (assuming no error occurs).
+
+@subsection sftp_aio_upload_example Performing an upload using the sftp aio API
+
+Terminologies used in the following code snippets :
+
+ - sftp : The sftp_session opened using sftp_new() and initialised using
+ sftp_init()
+
+ - file : The sftp file handle of the remote file in which uploaded data
+ is to be stored. (See sftp_open())
+
+ - file_size : The size of the local file to upload. This size can be
+ obtained by statting the local file to upload (e.g by using stat())
+
+ - We will need maintain a queue which will be used to store the sftp aio
+ handles corresponding to the outstanding requests.
+
+First, we issue the write requests while ensuring that their count
+doesn't exceed a particular limit decided by us, and the number of bytes
+requested to write don't exceed the size of the file to upload.
+
+@code
+sftp_aio aio = NULL;
+
+// The chunk size to use for the transfer
+size_t chunk_size;
+
+// For the limits structure that would be used by
+// the code to set the chunk size
+sftp_limits_t lim = NULL;
+
+// Max number of requests to keep outstanding at a time
+size_t in_flight_requests = 5;
+
+// Total number of bytes for which write requests have been sent
+size_t total_bytes_requested = 0;
+
+// Buffer to use for the upload
+char *buffer = NULL;
+
+// Helper variables
+size_t to_write;
+ssize_t bytes_requested;
+
+// Get the sftp limits
+lim = sftp_limits(sftp);
+if (lim == NULL) {
+ // handle error
+}
+
+// Set the chunk size for upload = the max limit for writing.
+// The reason for this has been given in the "Capping applied by
+// the sftp aio API" section (Its to make the code simpler)
+//
+// Assigning a size_t type variable a uint64_t type value here,
+// theoretically could cause an overflow, but practically
+// max_write_length would never exceed SIZE_MAX so its okay.
+chunk_size = lim->max_write_length;
+
+buffer = malloc(chunk_size);
+if (buffer == NULL) {
+ // handle error
+}
+
+... // Code to open the local file (to upload) [e.g using open(), fopen()].
+... // Code to stat the local file's file size [e.g using stat()].
+... // Code to open the remote file in which uploaded data will be stored [see
+ // sftp_open()].
+... // Code to initialize the queue which will be used to store sftp aio
+ // handles.
+
+for (i = 0;
+ i < in_flight_requests && total_bytes_requested < file_size;
+ ++i) {
+ to_write = file_size - total_bytes_requested;
+ if (to_write > chunk_size) {
+ to_write = chunk_size;
+ }
+
+ // Pseudo code
+ READ to_write bytes from the local file (to upload) into the buffer;
+
+ bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_write) {
+ // Should not happen for this code, as the to_write is <=
+ // max limit for writing (chunk size), so there is no reason
+ // for sftp_aio_begin_write() to return a lesser value.
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+@endcode
+
+At this point, at max in_flight_requests number of requests may be
+outstanding. Now we wait for the response corresponding to the earliest
+issued outstanding request.
+
+On getting that response, we issue another write request if there are
+still some bytes in the local file (to upload) for which we haven't sent
+the write request. (This happens when total_bytes_requested < file_size)
+
+This issuing of another write request (under a condition) is done to
+keep the number of outstanding requests equal to the value of the
+in_flight_requests variable.
+
+This process has to be repeated for every remaining outstanding request.
+
+@code
+while (the queue is not empty) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ // Wait for the response of the request corresponding to the aio
+ bytes_written = sftp_aio_wait_write(&aio);
+ if (bytes_written == SSH_ERROR) {
+ // handle error
+ }
+
+ // sftp_aio_wait_write() won't report a short write, so no need
+ // to check for a short write here.
+
+ if (total_bytes_requested == file_size) {
+ // no need to issue more write requests
+ continue;
+ }
+
+ // else issue a write request
+ to_write = file_size - total_bytes_requested;
+ if (to_write > chunk_size) {
+ to_write = chunk_size;
+ }
+
+ // Pseudo code
+ READ to_write bytes from the local file (to upload) into a buffer;
+
+ bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ // handle error
+ }
+
+ if ((size_t)bytes_requested < to_write) {
+ // Should not happen for this code, as the to_write is <=
+ // max limit for writing (chunk size), so there is no reason
+ // for sftp_aio_begin_write() to return a lesser value.
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ // Pseudo code
+ ENQUEUE aio in the queue;
+}
+
+free(buffer);
+
+... // Code to destroy the queue which was used to store the sftp aio
+ // handles.
+@endcode
+
+After exiting the while (the queue is not empty) loop, the upload
+would've been complete (assuming no error occurs).
+
+@subsection sftp_aio_free Example showing the usage of sftp_aio_free()
+
+The purpose of sftp_aio_free() was discussed at the beginning of this page,
+the following code example shows how it can be used during cleanup.
+
+@code
+void print_sftp_error(sftp_session sftp)
+{
+ if (sftp == NULL) {
+ return;
+ }
+
+ fprintf(stderr, "sftp error : %d\n", sftp_get_error(sftp));
+ fprintf(stderr, "ssh error : %s\n", ssh_get_error(sftp->session));
+}
+
+// Returns 0 on success, -1 on error
+int write_strings(sftp_file file)
+{
+ const char * strings[] = {
+ "This is the first string",
+ "This is the second string",
+ "This is the third string",
+ "This is the fourth string"
+ };
+
+ size_t string_count = sizeof(strings) / sizeof(strings[0]);
+ size_t i;
+
+ sftp_session sftp = NULL;
+ sftp_aio aio = NULL;
+
+ int rc;
+
+ if (file == NULL) {
+ return -1;
+ }
+
+ ... // Code to initialize the queue which will be used to store sftp aio
+ // handles
+
+ sftp = file->sftp;
+ for (i = 0; i < string_count; ++i) {
+ rc = sftp_aio_begin_write(file,
+ strings[i],
+ strlen(strings[i]),
+ &aio);
+ if (rc == SSH_ERROR) {
+ print_sftp_error(sftp);
+ goto err;
+ }
+
+ // Pseudo code
+ ENQUEUE aio in the queue of sftp aio handles
+ }
+
+ for (i = 0; i < string_count; ++i) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ rc = sftp_aio_wait_write(&aio);
+ if (rc == SSH_ERROR) {
+ print_sftp_error(sftp);
+ goto err;
+ }
+ }
+
+
+ ... // Code to destroy the queue in which sftp aio handles were
+ // stored
+
+ return 0;
+
+err:
+
+ while (queue is not empty) {
+ // Pseudo code
+ aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
+
+ sftp_aio_free(aio);
+ }
+
+ ... // Code to destroy the queue in which sftp aio handles were
+ // stored.
+
+ return -1;
+}
+
+@endcode
+
+*/
diff --git a/doc/shell.dox b/doc/shell.dox
index 0693bbcc..f51c489c 100644
--- a/doc/shell.dox
+++ b/doc/shell.dox
@@ -65,8 +65,17 @@ to as a "pty", for "pseudo-teletype". The remote processes won't see the
difference with a real text-oriented terminal.
If needed, you request the pty with the function ssh_channel_request_pty().
-Then you define its dimensions (number of rows and columns)
-with ssh_channel_change_pty_size().
+If you want define its dimensions (number of rows and columns),
+call ssh_channel_request_pty_size() instead. It's also possible to change
+the dimensions after creating the pty with ssh_channel_change_pty_size().
+
+These two functions configure the pty using the same terminal modes that
+stdin has. If stdin isn't a TTY, they use default modes that configure
+the pty with in canonical mode and e.g. preserving CR and LF characters.
+If you want to change the terminal modes used by the pty (e.g. to change
+CRLF handling), use ssh_channel_request_pty_size_modes(). This function
+accepts an additional "modes" buffer that is expected to contain encoded
+terminal modes according to RFC 4254 section 8.
Be your session interactive or not, the next step is to request a
shell with ssh_channel_request_shell().
@@ -320,18 +329,36 @@ int interactive_shell_session(ssh_session session, ssh_channel channel)
If your remote application is graphical, you can forward the X11 protocol to
your local computer.
-To do that, you first declare that you accept X11 connections with
-ssh_channel_accept_x11(). Then you create the forwarding tunnel for
-the X11 protocol with ssh_channel_request_x11().
+To do that, you first declare a callback to manage channel_open_request_x11_function.
+Then you create the forwarding tunnel for the X11 protocol with ssh_channel_request_x11().
The following code performs channel initialization and shell session
opening, and handles a parallel X11 connection:
@code
+#include <libssh/callbacks.h>
+
+ssh_channel x11channel = NULL;
+
+ssh_channel x11_open_request_callback(ssh_session session, const char *shost, int sport, void *userdata)
+{
+ x11channel = ssh_channel_new(session);
+ return x11channel;
+}
+
int interactive_shell_session(ssh_channel channel)
{
int rc;
- ssh_channel x11channel;
+
+ struct ssh_callbacks_struct cb =
+ {
+ .channel_open_request_x11_function = x11_open_request_callback,
+ .userdata = NULL
+ };
+
+ ssh_callbacks_init(&cb);
+ rc = ssh_set_callbacks(session, &cb);
+ if (rc != SSH_OK) return rc;
rc = ssh_channel_request_pty(channel);
if (rc != SSH_OK) return rc;
@@ -350,12 +377,15 @@ int interactive_shell_session(ssh_channel channel)
}
@endcode
-Don't forget to set the $DISPLAY environment variable on the remote
+Don't forget to check the $DISPLAY environment variable on the remote
side, or the remote applications won't try using the X11 tunnel:
@code
-$ export DISPLAY=:0
+$ echo $DISPLAY
+localhost:10.0
$ xclock &
@endcode
+See an implementation example at https://gitlab.com/libssh/libssh-mirror/-/tree/master/examples/ssh_X11_client.c for details.
+
*/
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 1cfefd8b..64458cfc 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -29,40 +29,48 @@ if (UNIX AND NOT WIN32)
add_executable(samplesftp samplesftp.c ${examples_SRCS})
target_compile_options(samplesftp PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
target_link_libraries(samplesftp ssh::ssh)
+
+ add_executable(sample_sftpserver sample_sftpserver.c ${examples_SRCS})
+ target_compile_options(sample_sftpserver PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
+ target_link_libraries(sample_sftpserver ssh::ssh ${ARGP_LIBRARIES})
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 ssh::ssh)
- if (WITH_SERVER AND (ARGP_LIBRARY OR HAVE_ARGP_H))
+ add_executable(ssh-X11-client ssh_X11_client.c ${examples_SRCS})
+ target_compile_options(ssh-X11-client PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
+ target_link_libraries(ssh-X11-client ssh::ssh)
+
+ if (WITH_SERVER AND (ARGP_LIBRARIES OR HAVE_ARGP_H))
if (HAVE_LIBUTIL)
add_executable(ssh_server_fork ssh_server.c)
target_compile_options(ssh_server_fork PRIVATE ${DEFAULT_C_COMPILE_FLAGS} -DWITH_FORK)
- target_link_libraries(ssh_server_fork ssh::ssh ${ARGP_LIBRARY} util)
+ target_link_libraries(ssh_server_fork ssh::ssh ${ARGP_LIBRARIES} util)
add_executable(ssh_server_pthread ssh_server.c)
target_compile_options(ssh_server_pthread PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
- target_link_libraries(ssh_server_pthread ssh::ssh ${ARGP_LIBRARY} pthread util)
+ target_link_libraries(ssh_server_pthread ssh::ssh ${ARGP_LIBRARIES} pthread util)
endif (HAVE_LIBUTIL)
if (WITH_GSSAPI AND GSSAPI_FOUND)
add_executable(proxy proxy.c)
target_compile_options(proxy PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
- target_link_libraries(proxy ssh::ssh ${ARGP_LIBRARY})
+ target_link_libraries(proxy ssh::ssh ${ARGP_LIBRARIES})
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 ssh::ssh ${ARGP_LIBRARY})
+ target_link_libraries(sshd_direct-tcpip ssh::ssh ${ARGP_LIBRARIES})
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 ssh::ssh ${ARGP_LIBRARY})
+ target_link_libraries(samplesshd-kbdint ssh::ssh ${ARGP_LIBRARIES})
add_executable(keygen2 keygen2.c ${examples_SRCS})
target_compile_options(keygen2 PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
- target_link_libraries(keygen2 ssh::ssh)
+ target_link_libraries(keygen2 ssh::ssh ${ARGP_LIBRARIES})
endif()
endif (UNIX AND NOT WIN32)
@@ -71,9 +79,9 @@ if (WITH_SERVER)
add_executable(samplesshd-cb samplesshd-cb.c)
target_compile_options(samplesshd-cb PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
target_link_libraries(samplesshd-cb ssh::ssh)
- if (ARGP_LIBRARY OR HAVE_ARGP_H)
- target_link_libraries(samplesshd-cb ${ARGP_LIBRARY})
- endif(ARGP_LIBRARY OR HAVE_ARGP_H)
+ if (ARGP_LIBRARIES OR HAVE_ARGP_H)
+ target_link_libraries(samplesshd-cb ${ARGP_LIBRARIES})
+ endif(ARGP_LIBRARIES OR HAVE_ARGP_H)
endif()
add_executable(exec exec.c ${examples_SRCS})
diff --git a/examples/exec.c b/examples/exec.c
index 7200ddef..77d3be47 100644
--- a/examples/exec.c
+++ b/examples/exec.c
@@ -17,7 +17,7 @@ int main(void) {
return 1;
}
- channel = ssh_channel_new(session);;
+ channel = ssh_channel_new(session);
if (channel == NULL) {
ssh_disconnect(session);
ssh_free(session);
diff --git a/examples/keygen.c b/examples/keygen.c
index 2ab00113..99f8c98c 100644
--- a/examples/keygen.c
+++ b/examples/keygen.c
@@ -27,14 +27,14 @@ int main(void)
rv = ssh_pki_generate(SSH_KEYTYPE_ED25519, 0, &key);
if (rv != SSH_OK) {
fprintf(stderr, "Failed to generate private key");
- return -1;
+ return -1;
}
- /* Write it to a file testkey in the current dirrectory */
+ /* Write it to a file testkey in the current directory */
rv = ssh_pki_export_privkey_file(key, NULL, NULL, NULL, "testkey");
if (rv != SSH_OK) {
fprintf(stderr, "Failed to write private key file");
- return -1;
+ return -1;
}
return 0;
diff --git a/examples/keygen2.c b/examples/keygen2.c
index 8cab1077..466e075b 100644
--- a/examples/keygen2.c
+++ b/examples/keygen2.c
@@ -38,6 +38,8 @@ struct arguments_st {
unsigned long bits;
char *file;
char *passphrase;
+ char *format;
+ int action_list;
};
static struct argp_option options[] = {
@@ -51,7 +53,6 @@ static struct argp_option options[] = {
"Accepted values are: "
"1024, 2048, 3072 (default), 4096, and 8192 for TYPE=\"rsa\"; "
"256 (default), 384, and 521 for TYPE=\"ecdsa\"; "
- "1024 (default) and 2048 for TYPE=\"dsa\"; "
"can be omitted for TYPE=\"ed25519\" "
"(it will be ignored if provided).\n",
.group = 0
@@ -85,7 +86,25 @@ static struct argp_option options[] = {
.flags = 0,
.doc = "The type of the key to be generated. "
"Accepted values are: "
- "\"rsa\", \"ecdsa\", \"ed25519\", and \"dsa\".\n",
+ "\"rsa\", \"ecdsa\", and \"ed25519\".\n",
+ .group = 0
+ },
+ {
+ .name = "list",
+ .key = 'l',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "List the Fingerprint of the given key\n",
+ .group = 0
+ },
+ {
+ .name = "format",
+ .key = 'm',
+ .arg = "FORMAT",
+ .flags = 0,
+ .doc = "Write the file in specific format. The supported values are "
+ "'PEM'and 'OpenSSH' file format. By default Ed25519 "
+ "keys are exported in OpenSSH format and others in PEM.\n",
.group = 0
},
{
@@ -144,9 +163,6 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
if (!strcmp(arg, "rsa")) {
arguments->type = SSH_KEYTYPE_RSA;
}
- else if (!strcmp(arg, "dsa")) {
- arguments->type = SSH_KEYTYPE_DSS;
- }
else if (!strcmp(arg, "ecdsa")) {
arguments->type = SSH_KEYTYPE_ECDSA;
}
@@ -160,6 +176,12 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
goto end;
}
break;
+ case 'l':
+ arguments->action_list = 1;
+ break;
+ case 'm':
+ arguments->format = strdup(arg);
+ break;
case ARGP_KEY_ARG:
if (state->arg_num > 0) {
/* Too many arguments. */
@@ -185,98 +207,80 @@ static int validate_args(struct arguments_st *args)
return EINVAL;
}
- switch(args->type) {
- case SSH_KEYTYPE_RSA:
- switch(args->bits) {
- case 0:
- /* If not provided, use default value */
- args->bits = 3072;
- break;
- case 1024:
- case 2048:
- case 3072:
- case 4096:
- case 8192:
- break;
- default:
- fprintf(stderr, "Error: Invalid bits parameter provided\n");
- rc = EINVAL;
- break;
- }
-
- if (args->file == NULL) {
- args->file = strdup("id_rsa");
- if (args->file == NULL) {
- rc = ENOMEM;
- break;
- }
- }
+ /* no other arguments needed for listing key fingerprints */
+ if (args->action_list) {
+ return 0;
+ }
+ switch (args->type) {
+ case SSH_KEYTYPE_RSA:
+ switch (args->bits) {
+ case 0:
+ /* If not provided, use default value */
+ args->bits = 3072;
break;
- case SSH_KEYTYPE_ECDSA:
- switch(args->bits) {
- case 0:
- /* If not provided, use default value */
- args->bits = 256;
- break;
- case 256:
- case 384:
- case 521:
- break;
- default:
- fprintf(stderr, "Error: Invalid bits parameter provided\n");
- rc = EINVAL;
- break;
- }
+ case 1024:
+ case 2048:
+ case 3072:
+ case 4096:
+ case 8192:
+ break;
+ default:
+ fprintf(stderr, "Error: Invalid bits parameter provided\n");
+ rc = EINVAL;
+ break;
+ }
+
+ if (args->file == NULL) {
+ args->file = strdup("id_rsa");
if (args->file == NULL) {
- args->file = strdup("id_ecdsa");
- if (args->file == NULL) {
- rc = ENOMEM;
- break;
- }
+ rc = ENOMEM;
+ break;
}
+ }
+ break;
+ case SSH_KEYTYPE_ECDSA:
+ switch (args->bits) {
+ case 0:
+ /* If not provided, use default value */
+ args->bits = 256;
break;
- case SSH_KEYTYPE_DSS:
- switch(args->bits) {
- case 0:
- /* If not provided, use default value */
- args->bits = 1024;
- break;
- case 1024:
- case 2048:
- break;
- default:
- fprintf(stderr, "Error: Invalid bits parameter provided\n");
- rc = EINVAL;
- break;
- }
+ case 256:
+ case 384:
+ case 521:
+ break;
+ default:
+ fprintf(stderr, "Error: Invalid bits parameter provided\n");
+ rc = EINVAL;
+ break;
+ }
+ if (args->file == NULL) {
+ args->file = strdup("id_ecdsa");
if (args->file == NULL) {
- args->file = strdup("id_dsa");
- if (args->file == NULL) {
- rc = ENOMEM;
- break;
- }
+ rc = ENOMEM;
+ break;
}
+ }
- break;
- case SSH_KEYTYPE_ED25519:
- /* Ignore value and overwrite with a zero */
- args->bits = 0;
+ break;
+ case SSH_KEYTYPE_ED25519:
+ /* Ignore value and overwrite with a zero */
+ args->bits = 0;
+ if (args->file == NULL) {
+ args->file = strdup("id_ed25519");
if (args->file == NULL) {
- args->file = strdup("id_ed25519");
- if (args->file == NULL) {
- rc = ENOMEM;
- break;
- }
+ rc = ENOMEM;
+ break;
}
+ }
- break;
- default:
- fprintf(stderr, "Error: unknown key type\n");
- rc = EINVAL;
- break;
+ break;
+ default:
+ fprintf(stderr, "Error: unknown key type\n");
+ rc = EINVAL;
+ break;
}
return rc;
@@ -289,6 +293,31 @@ static char doc[] = "Generate an SSH key pair. "
/* Our argp parser */
static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL};
+static void
+list_fingerprint(char *file)
+{
+ ssh_key key = NULL;
+ unsigned char *hash = NULL;
+ size_t hlen = 0;
+ int rc;
+
+ rc = ssh_pki_import_privkey_file(file, NULL, NULL, NULL, &key);
+ if (rc != SSH_OK) {
+ fprintf(stderr, "Failed to import private key %s\n", file);
+ return;
+ }
+
+ rc = ssh_get_publickey_hash(key, SSH_PUBLICKEY_HASH_SHA256, &hash, &hlen);
+ if (rc != SSH_OK) {
+ fprintf(stderr, "Failed to get key fingerprint\n");
+ return;
+ }
+ ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
+
+ ssh_clean_pubkey_hash(&hash);
+ ssh_key_free(key);
+}
+
int main(int argc, char *argv[])
{
ssh_key key = NULL;
@@ -302,6 +331,7 @@ int main(int argc, char *argv[])
.bits = 0,
.file = NULL,
.passphrase = NULL,
+ .action_list = 0,
};
if (argc < 2) {
@@ -319,6 +349,11 @@ int main(int argc, char *argv[])
goto end;
}
+ if (arguments.action_list && arguments.file) {
+ list_fingerprint(arguments.file);
+ goto end;
+ }
+
errno = 0;
rc = open(arguments.file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
if (rc < 0) {
@@ -361,8 +396,36 @@ int main(int argc, char *argv[])
}
/* Write the private key */
- rc = ssh_pki_export_privkey_file(key, arguments.passphrase, NULL, NULL,
- arguments.file);
+ if (arguments.format != NULL) {
+ if (strcasecmp(arguments.format, "PEM") == 0) {
+ rc = ssh_pki_export_privkey_file_format(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file,
+ SSH_FILE_FORMAT_PEM);
+ } else if (strcasecmp(arguments.format, "OpenSSH") == 0) {
+ rc = ssh_pki_export_privkey_file_format(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file,
+ SSH_FILE_FORMAT_OPENSSH);
+ } else {
+ rc = ssh_pki_export_privkey_file_format(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file,
+ SSH_FILE_FORMAT_DEFAULT);
+ }
+ } else {
+ rc = ssh_pki_export_privkey_file(key,
+ arguments.passphrase,
+ NULL,
+ NULL,
+ arguments.file);
+ }
if (rc != SSH_OK) {
fprintf(stderr, "Error: Failed to write private key file");
goto end;
diff --git a/examples/libssh_scp.c b/examples/libssh_scp.c
index b81059b4..adb82ef0 100644
--- a/examples/libssh_scp.c
+++ b/examples/libssh_scp.c
@@ -229,7 +229,7 @@ static int open_location(struct location *loc, int flag) {
return -1;
}
return 0;
- } else {
+ } else if (loc->path != NULL) {
loc->file = fopen(loc->path, flag == READ ? "r":"w");
if (!loc->file) {
if (errno == EISDIR) {
diff --git a/examples/proxy.c b/examples/proxy.c
index 159a37e5..49c39e4c 100644
--- a/examples/proxy.c
+++ b/examples/proxy.c
@@ -143,19 +143,11 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "dsakey",
- .key = 'd',
- .arg = "FILE",
- .flags = 0,
- .doc = "Set the dsa key.",
- .group = 0
- },
- {
.name = "rsakey",
.key = 'r',
.arg = "FILE",
.flags = 0,
- .doc = "Set the rsa key.",
+ .doc = "Set the rsa host key (deprecated alias to 'k').",
.group = 0
},
{
@@ -180,15 +172,11 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
case 'p':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
break;
- case 'd':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
- break;
+ case 'r':
+ /* deprecated */
case 'k':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
break;
- case 'r':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
- break;
case 'v':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3");
break;
@@ -237,7 +225,7 @@ int main(int argc, char **argv){
sshbind=ssh_bind_new();
session=ssh_new();
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, "sshd_rsa");
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, "sshd_rsa");
#ifdef HAVE_ARGP_H
/*
diff --git a/examples/sample_sftpserver.c b/examples/sample_sftpserver.c
new file mode 100644
index 00000000..b96f4ef7
--- /dev/null
+++ b/examples/sample_sftpserver.c
@@ -0,0 +1,515 @@
+/* This is a sample implementation of a libssh based SSH server */
+/*
+Copyright 2014 Audrius Butkevicius
+
+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.
+*/
+
+#include "config.h"
+
+#include <libssh/callbacks.h>
+#include <libssh/server.h>
+#include <libssh/sftp.h>
+#include <libssh/sftpserver.h>
+
+#include <poll.h>
+#ifdef HAVE_ARGP_H
+#include <argp.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#include <signal.h>
+#include <stdlib.h>
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+/* below are for sftp */
+#include <sys/statvfs.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <inttypes.h>
+
+#ifndef KEYS_FOLDER
+#ifdef _WIN32
+#define KEYS_FOLDER
+#else
+#define KEYS_FOLDER "/etc/ssh/"
+#endif
+#endif
+
+#define USER "myuser"
+#define PASS "mypassword"
+#define BUF_SIZE 1048576
+#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
+
+static void set_default_keys(ssh_bind sshbind,
+ int rsa_already_set,
+ int ecdsa_already_set)
+{
+ if (!rsa_already_set)
+ {
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
+ KEYS_FOLDER "ssh_host_rsa_key");
+ }
+ if (!ecdsa_already_set)
+ {
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
+ 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};
+#ifdef HAVE_ARGP_H
+const char *argp_program_version = "libssh sftp server example " SSH_STRINGIFY(LIBSSH_VERSION);
+const char *argp_program_bug_address = "<libssh@libssh.org>";
+
+/* Program documentation. */
+static char doc[] = "Sftp server implemented with libssh -- a Secure Shell protocol implementation";
+
+/* A description of the arguments we accept. */
+static char args_doc[] = "BINDADDR";
+
+/* The options we understand. */
+static struct argp_option options[] = {
+ {.name = "port",
+ .key = 'p',
+ .arg = "PORT",
+ .flags = 0,
+ .doc = "Set the port to bind.",
+ .group = 0},
+ {.name = "hostkey",
+ .key = 'k',
+ .arg = "FILE",
+ .flags = 0,
+ .doc = "Set a host key. Can be used multiple times. "
+ "Implies no default keys.",
+ .group = 0},
+ {.name = "rsakey",
+ .key = 'r',
+ .arg = "FILE",
+ .flags = 0,
+ .doc = "Set the rsa key.",
+ .group = 0},
+ {.name = "ecdsakey",
+ .key = 'e',
+ .arg = "FILE",
+ .flags = 0,
+ .doc = "Set the ecdsa key.",
+ .group = 0},
+ {.name = "authorizedkeys",
+ .key = 'a',
+ .arg = "FILE",
+ .flags = 0,
+ .doc = "Set the authorized keys file.",
+ .group = 0},
+ {.name = "no-default-keys",
+ .key = 'n',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Do not set default key locations.",
+ .group = 0},
+ {.name = "verbose",
+ .key = 'v',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Get verbose output.",
+ .group = 0},
+ {NULL, 0, NULL, 0, NULL, 0}};
+
+/* Parse a single option. */
+static error_t parse_opt(int key, char *arg, struct argp_state *state)
+{
+ /* Get the input argument from argp_parse, which we
+ * know is a pointer to our arguments structure. */
+ ssh_bind sshbind = state->input;
+ static int no_default_keys = 0;
+ static int rsa_already_set = 0, ecdsa_already_set = 0;
+
+ switch (key)
+ {
+ case 'n':
+ no_default_keys = 1;
+ break;
+ case 'p':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
+ break;
+ case 'k':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
+ /* We can't track the types of keys being added with this
+ option, so let's ensure we keep the keys we're adding
+ by just not setting the default keys */
+ no_default_keys = 1;
+ break;
+ case 'r':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
+ rsa_already_set = 1;
+ break;
+ case 'e':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
+ ecdsa_already_set = 1;
+ break;
+ case 'a':
+ strncpy(authorizedkeys, arg, DEF_STR_SIZE - 1);
+ break;
+ case 'v':
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
+ "3");
+ break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num >= 1)
+ {
+ /* Too many arguments. */
+ argp_usage(state);
+ }
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg);
+ break;
+ case ARGP_KEY_END:
+ if (state->arg_num < 1)
+ {
+ /* Not enough arguments. */
+ argp_usage(state);
+ }
+
+ if (!no_default_keys)
+ {
+ set_default_keys(sshbind,
+ rsa_already_set,
+ ecdsa_already_set);
+ }
+
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Our argp parser. */
+static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
+#endif /* HAVE_ARGP_H */
+
+/* A userdata struct for channel. */
+struct channel_data_struct
+{
+ /* Event which is used to poll the above descriptors. */
+ ssh_event event;
+ sftp_session sftp;
+};
+
+/* A userdata struct for session. */
+struct session_data_struct
+{
+ /* Pointer to the channel the session will allocate. */
+ ssh_channel channel;
+ int auth_attempts;
+ int authenticated;
+};
+
+static int auth_password(ssh_session session, const char *user,
+ const char *pass, void *userdata)
+{
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
+
+ (void)session;
+
+ if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0)
+ {
+ sdata->authenticated = 1;
+ return SSH_AUTH_SUCCESS;
+ }
+
+ sdata->auth_attempts++;
+ return SSH_AUTH_DENIED;
+}
+
+static int auth_publickey(ssh_session session,
+ const char *user,
+ struct ssh_key_struct *pubkey,
+ char signature_state,
+ void *userdata)
+{
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
+
+ (void)session;
+ (void)user;
+
+ if (signature_state == SSH_PUBLICKEY_STATE_NONE)
+ {
+ return SSH_AUTH_SUCCESS;
+ }
+
+ if (signature_state != SSH_PUBLICKEY_STATE_VALID)
+ {
+ return SSH_AUTH_DENIED;
+ }
+
+ // valid so far. Now look through authorized keys for a match
+ if (authorizedkeys[0])
+ {
+ ssh_key key = NULL;
+ int result;
+ struct stat buf;
+
+ if (stat(authorizedkeys, &buf) == 0)
+ {
+ result = ssh_pki_import_pubkey_file(authorizedkeys, &key);
+ if ((result != SSH_OK) || (key == NULL))
+ {
+ fprintf(stderr,
+ "Unable to import public key file %s\n",
+ authorizedkeys);
+ }
+ else
+ {
+ result = ssh_key_cmp(key, pubkey, SSH_KEY_CMP_PUBLIC);
+ ssh_key_free(key);
+ if (result == 0)
+ {
+ sdata->authenticated = 1;
+ return SSH_AUTH_SUCCESS;
+ }
+ }
+ }
+ }
+
+ // no matches
+ sdata->authenticated = 0;
+ return SSH_AUTH_DENIED;
+}
+
+static ssh_channel channel_open(ssh_session session, void *userdata)
+{
+ struct session_data_struct *sdata = (struct session_data_struct *)userdata;
+
+ sdata->channel = ssh_channel_new(session);
+ return sdata->channel;
+}
+
+static void handle_session(ssh_event event, ssh_session session)
+{
+ int n;
+
+ /* Our struct holding information about the channel. */
+ struct channel_data_struct cdata = {
+ .sftp = NULL,
+ };
+
+ /* Our struct holding information about the session. */
+ struct session_data_struct sdata = {
+ .channel = NULL,
+ .auth_attempts = 0,
+ .authenticated = 0,
+ };
+
+ struct ssh_channel_callbacks_struct channel_cb = {
+ .userdata = &(cdata.sftp),
+ .channel_data_function = sftp_channel_default_data_callback,
+ .channel_subsystem_request_function = sftp_channel_default_subsystem_request,
+ };
+
+ struct ssh_server_callbacks_struct server_cb = {
+ .userdata = &sdata,
+ .auth_password_function = auth_password,
+ .channel_open_request_session_function = channel_open,
+ };
+
+ if (authorizedkeys[0])
+ {
+ server_cb.auth_pubkey_function = auth_publickey;
+ ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY);
+ }
+ else
+ ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
+
+ ssh_callbacks_init(&server_cb);
+ ssh_callbacks_init(&channel_cb);
+
+ ssh_set_server_callbacks(session, &server_cb);
+
+ if (ssh_handle_key_exchange(session) != SSH_OK)
+ {
+ fprintf(stderr, "%s\n", ssh_get_error(session));
+ return;
+ }
+
+ ssh_event_add_session(event, session);
+
+ n = 0;
+ while (sdata.authenticated == 0 || sdata.channel == NULL) {
+ /* If the user has used up all attempts, or if he hasn't been able to
+ * authenticate in 10 seconds (n * 100ms), disconnect. */
+ if (sdata.auth_attempts >= 3 || n >= 100) {
+ return;
+ }
+
+ if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
+ fprintf(stderr, "%s\n", ssh_get_error(session));
+ return;
+ }
+ n++;
+ }
+
+ ssh_set_channel_callbacks(sdata.channel, &channel_cb);
+
+ do {
+ /* Poll the main event which takes care of the session, the channel and
+ * even our child process's stdout/stderr (once it's started). */
+ if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
+ ssh_channel_close(sdata.channel);
+ }
+
+ /* If child process's stdout/stderr has been registered with the event,
+ * or the child process hasn't started yet, continue. */
+ if (cdata.event != NULL) {
+ continue;
+ }
+ /* FIXME The server keeps hanging in the poll above when the client
+ * closes the channel */
+ } while (ssh_channel_is_open(sdata.channel));
+
+ ssh_channel_send_eof(sdata.channel);
+ ssh_channel_close(sdata.channel);
+
+ /* Wait up to 5 seconds for the client to terminate the session. */
+ for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
+ ssh_event_dopoll(event, 100);
+ }
+}
+
+/* SIGCHLD handler for cleaning up dead children. */
+static void sigchld_handler(int signo)
+{
+ (void)signo;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+}
+
+int main(int argc, char **argv)
+{
+ ssh_bind sshbind = NULL;
+ ssh_session session = NULL;
+ ssh_event event = NULL;
+ struct sigaction sa;
+ int rc;
+
+ /* Set up SIGCHLD handler. */
+ sa.sa_handler = sigchld_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+ if (sigaction(SIGCHLD, &sa, NULL) != 0)
+ {
+ fprintf(stderr, "Failed to register SIGCHLD handler\n");
+ return 1;
+ }
+
+ rc = ssh_init();
+ if (rc < 0)
+ {
+ fprintf(stderr, "ssh_init failed\n");
+ goto exit;
+ }
+
+ sshbind = ssh_bind_new();
+ if (sshbind == NULL)
+ {
+ fprintf(stderr, "ssh_bind_new failed\n");
+ goto exit;
+ }
+
+#ifdef HAVE_ARGP_H
+ argp_parse(&argp, argc, argv, 0, 0, sshbind);
+#else
+ (void)argc;
+ (void)argv;
+
+ set_default_keys(sshbind, 0, 0);
+#endif /* HAVE_ARGP_H */
+
+ if (ssh_bind_listen(sshbind) < 0)
+ {
+ fprintf(stderr, "%s\n", ssh_get_error(sshbind));
+ goto exit;
+ }
+
+ while (1)
+ {
+ session = ssh_new();
+ if (session == NULL)
+ {
+ fprintf(stderr, "Failed to allocate session\n");
+ continue;
+ }
+
+ /* Blocks until there is a new incoming connection. */
+ if (ssh_bind_accept(sshbind, session) != SSH_ERROR)
+ {
+ switch (fork())
+ {
+ case 0:
+ /* Remove the SIGCHLD handler inherited from parent. */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &sa, NULL);
+ /* Remove socket binding, which allows us to restart the
+ * parent process, without terminating existing sessions. */
+ ssh_bind_free(sshbind);
+
+ event = ssh_event_new();
+ if (event != NULL)
+ {
+ /* Blocks until the SSH session ends by either
+ * child process exiting, or client disconnecting. */
+ handle_session(event, session);
+ ssh_event_free(event);
+ }
+ else
+ {
+ fprintf(stderr, "Could not create polling context\n");
+ }
+ ssh_disconnect(session);
+ ssh_free(session);
+
+ exit(0);
+ case -1:
+ fprintf(stderr, "Failed to fork\n");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "%s\n", ssh_get_error(sshbind));
+ }
+ /* Since the session has been passed to a child fork, do some cleaning
+ * up at the parent process. */
+ ssh_disconnect(session);
+ ssh_free(session);
+ }
+
+exit:
+ ssh_bind_free(sshbind);
+ ssh_finalize();
+ return 0;
+}
diff --git a/examples/samplesftp.c b/examples/samplesftp.c
index 9c8cd34a..d82a556a 100644
--- a/examples/samplesftp.c
+++ b/examples/samplesftp.c
@@ -33,21 +33,18 @@ clients must be made or how a client should react.
#define BUF_SIZE 65536
#endif
-static int verbosity;
-static char *destination;
-
static void do_sftp(ssh_session session) {
sftp_session sftp = sftp_new(session);
sftp_dir dir;
sftp_attributes file;
sftp_statvfs_t sftpstatvfs;
struct statvfs sysstatvfs;
- sftp_file fichier;
+ sftp_file source;
sftp_file to;
int len = 1;
unsigned int i;
char data[BUF_SIZE] = {0};
- char *lnk;
+ char *lnk = NULL;
unsigned int count;
@@ -86,6 +83,7 @@ static void do_sftp(ssh_session session) {
goto end;
}
printf("readlink /tmp/sftp_symlink_test: %s\n", lnk);
+ ssh_string_free_char(lnk);
sftp_unlink(sftp, "/tmp/sftp_symlink_test");
@@ -173,7 +171,7 @@ static void do_sftp(ssh_session session) {
sftp_attributes_free(file);
}
- /* when file = NULL, an error has occured OR the directory listing is end of
+ /* when file = NULL, an error has occurred OR the directory listing is end of
* file */
if (!sftp_dir_eof(dir)) {
fprintf(stderr, "Error: %s\n", ssh_get_error(session));
@@ -188,8 +186,8 @@ static void do_sftp(ssh_session session) {
/* the small buffer size was intended to stress the library. of course, you
* can use a buffer till 20kbytes without problem */
- fichier = sftp_open(sftp, "/usr/bin/ssh", O_RDONLY, 0);
- if (!fichier) {
+ source = sftp_open(sftp, "/usr/bin/ssh", O_RDONLY, 0);
+ if (!source) {
fprintf(stderr, "Error opening /usr/bin/ssh: %s\n",
ssh_get_error(session));
goto end;
@@ -200,16 +198,16 @@ static void do_sftp(ssh_session session) {
if (!to) {
fprintf(stderr, "Error opening ssh-copy for writing: %s\n",
ssh_get_error(session));
- sftp_close(fichier);
+ sftp_close(source);
goto end;
}
- while ((len = sftp_read(fichier, data, 4096)) > 0) {
+ while ((len = sftp_read(source, data, 4096)) > 0) {
if (sftp_write(to, data, len) != len) {
fprintf(stderr, "Error writing %d bytes: %s\n",
len, ssh_get_error(session));
sftp_close(to);
- sftp_close(fichier);
+ sftp_close(source);
goto end;
}
}
@@ -219,10 +217,10 @@ static void do_sftp(ssh_session session) {
fprintf(stderr, "Error reading file: %s\n", ssh_get_error(session));
}
- sftp_close(fichier);
+ sftp_close(source);
sftp_close(to);
- printf("fichiers ferm\n");
- to = sftp_open(sftp, "/tmp/grosfichier", O_WRONLY|O_CREAT, 0644);
+ printf("file closed\n");
+ to = sftp_open(sftp, "/tmp/large_file", O_WRONLY|O_CREAT, 0644);
for (i = 0; i < 1000; ++i) {
len = sftp_write(to, data, sizeof(data));
@@ -243,50 +241,63 @@ static void usage(const char *argv0) {
fprintf(stderr, "Usage : %s [-v] remotehost\n"
"sample sftp test client - libssh-%s\n"
"Options :\n"
+ " -l user : log in as user\n"
+ " -p port : connect to port\n"
" -v : increase log verbosity\n",
argv0,
ssh_version(0));
exit(0);
}
-static int opts(int argc, char **argv) {
- int i;
-
- while ((i = getopt(argc, argv, "v")) != -1) {
- switch(i) {
- case 'v':
- verbosity++;
- break;
- default:
- fprintf(stderr, "unknown option %c\n", optopt);
- usage(argv[0]);
- return -1;
- }
- }
+int main(int argc, char **argv)
+{
+ ssh_session session = NULL;
+ char *destination = NULL;
+ int auth = 0;
+ int state;
- destination = argv[optind];
- if (destination == NULL) {
+ ssh_init();
+ session = ssh_new();
+
+ if (ssh_options_getopt(session, &argc, argv)) {
+ fprintf(stderr,
+ "Error parsing command line: %s\n",
+ ssh_get_error(session));
+ ssh_free(session);
+ ssh_finalize();
usage(argv[0]);
- return -1;
+ return EXIT_FAILURE;
}
- return 0;
-}
+ if (argc < 1) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ destination = argv[1];
-int main(int argc, char **argv) {
- ssh_session session;
+ if (ssh_options_set(session, SSH_OPTIONS_HOST, destination) < 0) {
+ return -1;
+ }
+ if (ssh_connect(session)) {
+ fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
+ return -1;
+ }
- if (opts(argc, argv) < 0) {
- return EXIT_FAILURE;
+ state = verify_knownhost(session);
+ if (state != 0) {
+ return -1;
}
- session = connect_ssh(destination, NULL, verbosity);
- if (session == NULL) {
- return EXIT_FAILURE;
+ auth = authenticate_console(session);
+ if (auth != SSH_AUTH_SUCCESS) {
+ return -1;
}
do_sftp(session);
ssh_disconnect(session);
ssh_free(session);
+
+ ssh_finalize();
+
return 0;
}
diff --git a/examples/samplesshd-cb.c b/examples/samplesshd-cb.c
index 628013aa..6b272102 100644
--- a/examples/samplesshd-cb.c
+++ b/examples/samplesshd-cb.c
@@ -49,6 +49,27 @@ static int tries = 0;
static int error = 0;
static ssh_channel chan=NULL;
+static int auth_none(ssh_session session,
+ const char *user,
+ void *userdata)
+{
+ ssh_string banner = NULL;
+
+ (void)user; /* unused */
+ (void)userdata; /* unused */
+
+ ssh_set_auth_methods(session,
+ SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_GSSAPI_MIC);
+
+ banner = ssh_string_from_char("Banner Example\n");
+ if (banner != NULL) {
+ ssh_send_issue_banner(session, banner);
+ }
+ ssh_string_free(banner);
+
+ return SSH_AUTH_DENIED;
+}
+
static int auth_password(ssh_session session, const char *user,
const char *password, void *userdata){
(void)userdata;
@@ -152,19 +173,11 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "dsakey",
- .key = 'd',
- .arg = "FILE",
- .flags = 0,
- .doc = "Set the dsa key.",
- .group = 0
- },
- {
.name = "rsakey",
.key = 'r',
.arg = "FILE",
.flags = 0,
- .doc = "Set the rsa key.",
+ .doc = "Set the rsa key (deprecated alias for 'k').",
.group = 0
},
{
@@ -197,15 +210,10 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
case 'p':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
break;
- case 'd':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
- break;
+ case 'r':
case 'k':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
break;
- case 'r':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
- break;
case 'v':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3");
break;
@@ -242,6 +250,7 @@ int main(int argc, char **argv){
ssh_event mainloop;
struct ssh_server_callbacks_struct cb = {
.userdata = NULL,
+ .auth_none_function = auth_none,
.auth_password_function = auth_password,
#ifdef WITH_GSSAPI
.auth_gssapi_mic_function = auth_gssapi_mic,
@@ -256,8 +265,7 @@ int main(int argc, char **argv){
sshbind=ssh_bind_new();
session=ssh_new();
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key");
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key");
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, KEYS_FOLDER "ssh_host_rsa_key");
#ifdef HAVE_ARGP_H
/*
diff --git a/examples/samplesshd-kbdint.c b/examples/samplesshd-kbdint.c
index 9b09cf27..d382abac 100644
--- a/examples/samplesshd-kbdint.c
+++ b/examples/samplesshd-kbdint.c
@@ -113,19 +113,11 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "dsakey",
- .key = 'd',
- .arg = "FILE",
- .flags = 0,
- .doc = "Set the dsa key.",
- .group = 0
- },
- {
.name = "rsakey",
.key = 'r',
.arg = "FILE",
.flags = 0,
- .doc = "Set the rsa key.",
+ .doc = "Set the rsa key (deprecated alias for 'k').",
.group = 0
},
{
@@ -151,15 +143,10 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
port = atoi(arg);
break;
- case 'd':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
- break;
+ case 'r':
case 'k':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
break;
- case 'r':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
- break;
case 'v':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3");
break;
@@ -306,10 +293,8 @@ int main(int argc, char **argv){
sshbind=ssh_bind_new();
session=ssh_new();
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY,
- KEYS_FOLDER "ssh_host_dsa_key");
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY,
- KEYS_FOLDER "ssh_host_rsa_key");
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY,
+ KEYS_FOLDER "ssh_host_rsa_key");
#ifdef HAVE_ARGP_H
/*
@@ -369,9 +354,9 @@ int main(int argc, char **argv){
}
} while(!chan);
- if(!chan) {
- printf("Error: cleint did not ask for a channel session (%s)\n",
- ssh_get_error(session));
+ if (!chan) {
+ printf("Error: client did not ask for a channel session (%s)\n",
+ ssh_get_error(session));
ssh_finalize();
return 1;
}
diff --git a/examples/senddata.c b/examples/senddata.c
index 042b462b..21181fb9 100644
--- a/examples/senddata.c
+++ b/examples/senddata.c
@@ -17,7 +17,7 @@ int main(void) {
return 1;
}
- channel = ssh_channel_new(session);;
+ channel = ssh_channel_new(session);
if (channel == NULL) {
ssh_disconnect(session);
return 1;
diff --git a/examples/ssh_X11_client.c b/examples/ssh_X11_client.c
new file mode 100644
index 00000000..e3386813
--- /dev/null
+++ b/examples/ssh_X11_client.c
@@ -0,0 +1,950 @@
+/*
+ * ssh.c - Simple example of SSH X11 client using libssh
+ *
+ * Copyright (C) 2022 Marco Fortina
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. * If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. * If you
+ * do not wish to do so, delete this exception statement from your
+ * version. * If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ *
+ *
+ *
+ * ssh_X11_client
+ * ==============
+ *
+ * AUTHOR URL
+ * https://gitlab.com/marco.fortina/libssh-x11-client/
+ *
+ * This is a simple example of SSH X11 client using libssh.
+ *
+ * Features:
+ *
+ * - support local display (e.g. :0)
+ * - support remote display (e.g. localhost:10.0)
+ * - using callbacks and event polling to significantly reduce CPU utilization
+ * - use X11 forwarding with authentication spoofing (like openssh)
+ *
+ * Note:
+ *
+ * - part of this code was inspired by openssh's one.
+ *
+ * Dependencies:
+ *
+ * - gcc >= 7.5.0
+ * - libssh >= 0.8.0
+ * - libssh-dev >= 0.8.0
+ *
+ * To Build:
+ * gcc -o ssh_X11_client ssh_X11_client.c -lssh -g
+ *
+ * Donations:
+ *
+ * If you liked this work and wish to support the developer please donate to:
+ * Bitcoin: 1N2rQimKbeUQA8N2LU5vGopYQJmZsBM2d6
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <time.h>
+
+#include <libssh/libssh.h>
+#include <libssh/callbacks.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/*
+ * Data Structures and Macros
+ */
+
+#define _PATH_UNIX_X "/tmp/.X11-unix/X%d"
+#define _XAUTH_CMD "/usr/bin/xauth list %s 2>/dev/null"
+
+typedef struct item {
+ ssh_channel channel;
+ int fd_in;
+ int fd_out;
+ int protected;
+ struct item *next;
+} node_t;
+
+node_t *node = NULL;
+
+
+/*
+ * Mutex
+ */
+
+pthread_mutex_t mutex;
+
+
+/*
+ * Function declarations
+ */
+
+/* Linked nodes to manage channel/fd tuples */
+static int insert_item(ssh_channel channel, int fd_in, int fd_out,
+ int protected);
+static void delete_item(ssh_channel channel);
+static node_t * search_item(ssh_channel channel);
+
+/* X11 Display */
+const char * ssh_gai_strerror(int gaierr);
+static int x11_get_proto(const char *display, char **_proto, char **_data);
+static void set_nodelay(int fd);
+static int connect_local_xsocket_path(const char *pathname);
+static int connect_local_xsocket(int display_number);
+static int x11_connect_display(void);
+
+/* Send data to channel */
+static int copy_fd_to_channel_callback(int fd, int revents, void *userdata);
+
+/* Read data from channel */
+static int copy_channel_to_fd_callback(ssh_session session, ssh_channel channel,
+ void *data, uint32_t len, int is_stderr,
+ void *userdata);
+
+/* EOF&Close channel */
+static void channel_close_callback(ssh_session session, ssh_channel channel,
+ void *userdata);
+
+/* X11 Request */
+static ssh_channel x11_open_request_callback(ssh_session session,
+ const char *shost, int sport,
+ void *userdata);
+
+/* Main loop */
+static int main_loop(ssh_channel channel);
+
+/* Internals */
+int64_t _current_timestamp(void);
+
+/* Global variables */
+const char *hostname = NULL;
+int enableX11 = 1;
+
+/*
+ * Callbacks Data Structures
+ */
+
+/* SSH Channel Callbacks */
+struct ssh_channel_callbacks_struct channel_cb =
+{
+ .channel_data_function = copy_channel_to_fd_callback,
+ .channel_eof_function = channel_close_callback,
+ .channel_close_function = channel_close_callback,
+ .userdata = NULL
+};
+
+/* SSH Callbacks */
+struct ssh_callbacks_struct cb =
+{
+ .channel_open_request_x11_function = x11_open_request_callback,
+ .userdata = NULL
+};
+
+
+/*
+ * SSH Event Context
+ */
+
+short events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
+ssh_event event;
+
+
+/*
+ * Internal data structures
+ */
+
+struct termios _saved_tio;
+
+
+/*
+ * Internal functions
+ */
+
+int64_t _current_timestamp(void)
+{
+ struct timeval tv;
+ int64_t milliseconds;
+
+ gettimeofday(&tv, NULL);
+ milliseconds = (int64_t)(tv.tv_sec) * 1000 + (tv.tv_usec / 1000);
+
+ return milliseconds;
+}
+
+static void _logging_callback(int priority, const char *function,
+ const char *buffer, void *userdata)
+{
+ FILE *fp = NULL;
+ char buf[100];
+ int64_t milliseconds;
+
+ time_t now = time(0);
+
+ (void)userdata;
+
+ strftime(buf, 100, "%Y-%m-%d %H:%M:%S", localtime(&now));
+
+ fp = fopen("debug.log","a");
+ if (fp == NULL) {
+ printf("Error!");
+ exit(-11);
+ }
+
+ milliseconds = _current_timestamp();
+
+ fprintf(fp, "[%s.%jd, %d] %s: %s\n", buf, milliseconds, priority,
+ function, buffer);
+ fclose(fp);
+}
+
+static int _enter_term_raw_mode(void)
+{
+ struct termios tio;
+ int ret = tcgetattr(fileno(stdin), &tio);
+ if (ret != -1) {
+ _saved_tio = tio;
+ tio.c_iflag |= IGNPAR;
+ tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
+#ifdef IUCLC
+ tio.c_iflag &= ~IUCLC;
+#endif
+ tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+#ifdef IEXTEN
+ tio.c_lflag &= ~IEXTEN;
+#endif
+ tio.c_oflag &= ~OPOST;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ ret = tcsetattr(fileno(stdin), TCSADRAIN, &tio);
+ }
+
+ return ret;
+}
+
+static int _leave_term_raw_mode(void)
+{
+ int ret = tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio);
+ return ret;
+}
+
+
+/*
+ * Functions
+ */
+
+static int insert_item(ssh_channel channel, int fd_in, int fd_out,
+ int protected)
+{
+ node_t *node_iterator = NULL, *new = NULL;
+
+ pthread_mutex_lock(&mutex);
+
+ if (node == NULL) {
+ /* Calloc ensure that node is full of 0 */
+ node = (node_t *) calloc(1, sizeof(node_t));
+ if (node == NULL) {
+ pthread_mutex_unlock(&mutex);
+ return -1;
+ }
+ node->channel = channel;
+ node->fd_in = fd_in;
+ node->fd_out = fd_out;
+ node->protected = protected;
+ node->next = NULL;
+ } else {
+ node_iterator = node;
+ while (node_iterator->next != NULL) {
+ node_iterator = node_iterator->next;
+ }
+ /* Create the new node */
+ new = (node_t *) malloc(sizeof(node_t));
+ if (new == NULL) {
+ pthread_mutex_unlock(&mutex);
+ return -1;
+ }
+ new->channel = channel;
+ new->fd_in = fd_in;
+ new->fd_out = fd_out;
+ new->protected = protected;
+ new->next = NULL;
+ node_iterator->next = new;
+
+ }
+
+ pthread_mutex_unlock(&mutex);
+ return 0;
+}
+
+
+static void delete_item(ssh_channel channel)
+{
+ node_t *current = NULL, *previous = NULL;
+
+ pthread_mutex_lock(&mutex);
+
+ for (current = node; current; previous = current, current = current->next) {
+ if (current->channel != channel) {
+ continue;
+ }
+
+ if (previous == NULL) {
+ node = current->next;
+ } else {
+ previous->next = current->next;
+ }
+
+ free(current);
+ pthread_mutex_unlock(&mutex);
+ return;
+ }
+
+ pthread_mutex_unlock(&mutex);
+}
+
+
+static node_t *search_item(ssh_channel channel)
+{
+ node_t *current = NULL;
+
+ pthread_mutex_lock(&mutex);
+
+ current = node;
+ while (current != NULL) {
+ if (current->channel == channel) {
+ pthread_mutex_unlock(&mutex);
+ return current;
+ } else {
+ current = current->next;
+ }
+ }
+
+ pthread_mutex_unlock(&mutex);
+
+ return NULL;
+}
+
+
+
+static void set_nodelay(int fd)
+{
+ int opt, rc;
+ socklen_t optlen;
+
+ optlen = sizeof(opt);
+
+ rc = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen);
+ if (rc == -1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "getsockopt TCP_NODELAY: %.100s",
+ strerror(errno));
+ return;
+ }
+ if (opt == 1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "fd %d is TCP_NODELAY", fd);
+ return;
+ }
+ opt = 1;
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "fd %d setting TCP_NODELAY", fd);
+
+ rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
+ if (rc == -1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "setsockopt TCP_NODELAY: %.100s",
+ strerror(errno));
+ }
+}
+
+
+const char *ssh_gai_strerror(int gaierr)
+{
+ if (gaierr == EAI_SYSTEM && errno != 0) {
+ return strerror(errno);
+ }
+ return gai_strerror(gaierr);
+}
+
+
+
+static int x11_get_proto(const char *display, char **_proto, char **_cookie)
+{
+ char cmd[1024], line[512], xdisplay[512];
+ static char proto[512], cookie[512];
+ FILE *f = NULL;
+ int ret = 0;
+
+ *_proto = proto;
+ *_cookie = cookie;
+
+ proto[0] = cookie[0] = '\0';
+
+ if (strncmp(display, "localhost:", 10) == 0) {
+ ret = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", display + 10);
+ if (ret < 0 || (size_t)ret >= sizeof(xdisplay)) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "display name too long. display: %s", display);
+ return -1;
+ }
+ display = xdisplay;
+ }
+
+ snprintf(cmd, sizeof(cmd), _XAUTH_CMD, display);
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "xauth cmd: %s", cmd);
+
+ f = popen(cmd, "r");
+ if (f && fgets(line, sizeof(line), f) &&
+ sscanf(line, "%*s %511s %511s", proto, cookie) == 2) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ if (f) {
+ pclose(f);
+ }
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "proto: %s - cookie: %s - ret: %d",
+ proto, cookie, ret);
+
+ return ret;
+}
+
+static int connect_local_xsocket_path(const char *pathname)
+{
+ int sock, rc;
+ struct sockaddr_un addr;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "socket: %.100s",
+ strerror(errno));
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ addr.sun_path[0] = '\0';
+ /* pathname is guaranteed to be initialized and larger than addr.sun_path[108] */
+ memcpy(addr.sun_path + 1, pathname, sizeof(addr.sun_path) - 1);
+ rc = connect(sock, (struct sockaddr *)&addr,
+ offsetof(struct sockaddr_un, sun_path) + 1 + strlen(pathname));
+ if (rc == 0) {
+ return sock;
+ }
+ close(sock);
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "connect %.100s: %.100s",
+ addr.sun_path, strerror(errno));
+
+ return -1;
+}
+
+
+static int connect_local_xsocket(int display_number)
+{
+ char buf[1024] = {0};
+ snprintf(buf, sizeof(buf), _PATH_UNIX_X, display_number);
+ return connect_local_xsocket_path(buf);
+}
+
+
+static int x11_connect_display(void)
+{
+ int display_number;
+ const char *display = NULL;
+ char buf[1024], *cp = NULL;
+ struct addrinfo hints, *ai = NULL, *aitop = NULL;
+ char strport[NI_MAXSERV];
+ int gaierr = 0, sock = 0;
+
+ /* Try to open a socket for the local X server. */
+ display = getenv("DISPLAY");
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "display: %s", display);
+
+ if (display == 0) {
+ return -1;
+ }
+
+ /* Check if it is a unix domain socket. */
+ if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') {
+ /* Connect to the unix domain socket. */
+ if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "Could not parse display number from DISPLAY: %.100s",
+ display);
+ return -1;
+ }
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "display_number: %d",
+ display_number);
+
+ /* Create a socket. */
+ sock = connect_local_xsocket(display_number);
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "socket: %d", sock);
+
+ if (sock < 0) {
+ return -1;
+ }
+
+ /* OK, we now have a connection to the display. */
+ return sock;
+ }
+
+ /* Connect to an inet socket. */
+ strncpy(buf, display, sizeof(buf) - 1);
+ cp = strchr(buf, ':');
+ if (cp == 0) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "Could not find ':' in DISPLAY: %.100s", display);
+ return -1;
+ }
+ *cp = 0;
+ if (sscanf(cp + 1, "%d", &display_number) != 1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "Could not parse display number from DISPLAY: %.100s",
+ display);
+ return -1;
+ }
+
+ /* Look up the host address */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf(strport, sizeof(strport), "%u", 6000 + display_number);
+ gaierr = getaddrinfo(buf, strport, &hints, &aitop);
+ if (gaierr != 0) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "%.100s: unknown host. (%s)",
+ buf, ssh_gai_strerror(gaierr));
+ return -1;
+ }
+ for (ai = aitop; ai; ai = ai->ai_next) {
+ /* Create a socket. */
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock == -1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "socket: %.100s",
+ strerror(errno));
+ continue;
+ }
+ /* Connect it to the display. */
+ if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "connect %.100s port %u: %.100s", buf,
+ 6000 + display_number, strerror(errno));
+ close(sock);
+ continue;
+ }
+ /* Success */
+ break;
+ }
+ freeaddrinfo(aitop);
+ if (ai == 0) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "connect %.100s port %u: %.100s",
+ buf, 6000 + display_number, strerror(errno));
+ return -1;
+ }
+ set_nodelay(sock);
+
+ return sock;
+}
+
+
+
+static int copy_fd_to_channel_callback(int fd, int revents, void *userdata)
+{
+ ssh_channel channel = (ssh_channel)userdata;
+ char buf[2097152];
+ int sz = 0, ret = 0;
+
+ node_t *temp_node = search_item(channel);
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "event: %d - fd: %d", revents, fd);
+
+ if (channel == NULL) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "channel does not exist.");
+ if (temp_node->protected == 0) {
+ close(fd);
+ }
+ return -1;
+ }
+
+ if (fcntl(fd, F_GETFD) == -1) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "fcntl error. fd: %d", fd);
+ ssh_channel_close(channel);
+ return -1;
+ }
+
+ if ((revents & POLLIN) || (revents & POLLPRI)) {
+ sz = read(fd, buf, sizeof(buf));
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "sz: %d", sz);
+ if (sz > 0) {
+ ret = ssh_channel_write(channel, buf, sz);
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "channel_write ret: %d", ret);
+ } else if (sz < 0) {
+ ssh_channel_close(channel);
+ return -1;
+ } else {
+ /* sz = 0. Why the hell I'm here? */
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "Why the hell am I here?: sz: %d", sz);
+ if (temp_node->protected == 0) {
+ close(fd);
+ }
+ return -1;
+ }
+ }
+
+ if ((revents & POLLHUP) || (revents & POLLNVAL) || (revents & POLLERR)) {
+ ssh_channel_close(channel);
+ return -1;
+ }
+
+ return sz;
+}
+
+
+static int copy_channel_to_fd_callback(ssh_session session, ssh_channel channel,
+ void *data, uint32_t len, int is_stderr,
+ void *userdata)
+{
+ node_t *temp_node = NULL;
+ int fd, sz;
+
+ (void)session;
+ (void)is_stderr;
+ (void)userdata;
+
+ temp_node = search_item(channel);
+
+ fd = temp_node->fd_out;
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "len: %d - fd: %d - is_stderr: %d",
+ len, fd, is_stderr);
+
+ sz = write(fd, data, len);
+
+ return sz;
+}
+
+
+static void channel_close_callback(ssh_session session, ssh_channel channel,
+ void *userdata)
+{
+ node_t *temp_node = NULL;
+
+ (void)session;
+ (void)userdata;
+
+ temp_node = search_item(channel);
+
+ if (temp_node != NULL) {
+ int fd = temp_node->fd_in;
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "fd: %d", fd);
+
+ delete_item(channel);
+ ssh_event_remove_fd(event, fd);
+
+ if (temp_node->protected == 0) {
+ close(fd);
+ }
+ }
+}
+
+
+static ssh_channel x11_open_request_callback(ssh_session session,
+ const char *shost, int sport,
+ void *userdata)
+{
+ ssh_channel channel = NULL;
+ int sock, rv;
+
+ (void)shost;
+ (void)sport;
+ (void)userdata;
+
+ channel = ssh_channel_new(session);
+
+ sock = x11_connect_display();
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "sock: %d", sock);
+
+ rv = insert_item(channel, sock, sock, 0);
+ if (rv != 0) {
+ ssh_channel_free(channel);
+ return NULL;
+ }
+
+ ssh_event_add_fd(event, sock, events, copy_fd_to_channel_callback, channel);
+ ssh_event_add_session(event, session);
+
+ ssh_add_channel_callbacks(channel, &channel_cb);
+
+ return channel;
+}
+
+
+
+/*
+ * MAIN LOOP
+ */
+
+static int main_loop(ssh_channel channel)
+{
+ ssh_session session = ssh_channel_get_session(channel);
+ int rv;
+
+ rv = insert_item(channel, fileno(stdin), fileno(stdout), 1);
+ if (rv != 0) {
+ return -1;
+ }
+
+ ssh_callbacks_init(&channel_cb);
+ ssh_set_channel_callbacks(channel, &channel_cb);
+
+ event = ssh_event_new();
+ if (event == NULL) {
+ printf("Couldn't get a event\n");
+ return -1;
+ }
+
+ rv = ssh_event_add_fd(event, fileno(stdin), events,
+ copy_fd_to_channel_callback, channel);
+ if (rv != SSH_OK) {
+ printf("Couldn't add an fd to the event\n");
+ return -1;
+ }
+
+ rv = ssh_event_add_session(event, session);
+ if (rv != SSH_OK) {
+ printf("Couldn't add the session to the event\n");
+ return -1;
+ }
+
+ do {
+ if (ssh_event_dopoll(event, 1000) == SSH_ERROR) {
+ printf("Error : %s\n", ssh_get_error(session));
+ /* fall through */
+ }
+ } while (!ssh_channel_is_closed(channel));
+
+ delete_item(channel);
+ ssh_event_remove_fd(event, fileno(stdin));
+ ssh_event_remove_session(event, session);
+ ssh_event_free(event);
+
+ return 0;
+}
+
+
+/*
+ * USAGE
+ */
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage : ssh-X11-client [options] [login@]hostname\n"
+ "sample X11 client - libssh-%s\n"
+ "Options :\n"
+ " -l user : Specifies the user to log in as on the remote "
+ "machine.\n"
+ " -p port : Port to connect to on the remote host.\n"
+ " -v : Verbose mode. Multiple -v options increase the "
+ "verbosity. The maximum is 5.\n"
+ " -C : Requests compression of all data.\n"
+ " -x : Disables X11 forwarding.\n"
+ "\n",
+ ssh_version(0));
+
+ exit(0);
+}
+
+static int opts(int argc, char **argv)
+{
+ int i;
+
+ while ((i = getopt(argc,argv,"x")) != -1) {
+ switch (i) {
+ case 'x':
+ enableX11 = 0;
+ break;
+ default:
+ fprintf(stderr, "Unknown option %c\n", optopt);
+ return -1;
+ }
+ }
+
+ if (optind < argc) {
+ hostname = argv[optind++];
+ }
+
+ if (hostname == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * MAIN
+ */
+
+int main(int argc, char **argv)
+{
+ char *password = NULL;
+
+ ssh_session session = NULL;
+ ssh_channel channel = NULL;
+
+ int ret;
+
+ const char *display = NULL;
+ char *proto = NULL, *cookie = NULL;
+
+ ssh_set_log_callback(_logging_callback);
+ ret = ssh_init();
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ session = ssh_new();
+ if (session == NULL) {
+ exit(-1);
+ }
+
+ if (ssh_options_getopt(session, &argc, argv) || opts(argc, argv)) {
+ fprintf(stderr, "Error parsing command line: %s\n",
+ ssh_get_error(session));
+ ssh_free(session);
+ ssh_finalize();
+ usage();
+ }
+
+ if (ssh_options_set(session, SSH_OPTIONS_HOST, hostname) < 0) {
+ return -1;
+ }
+
+ ret = ssh_connect(session);
+ if (ret != SSH_OK) {
+ fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
+ exit(-1);
+ }
+
+ password = getpass("Password: ");
+ ret = ssh_userauth_password(session, NULL, password);
+ if (ret != SSH_AUTH_SUCCESS) {
+ fprintf(stderr, "Error authenticating with password: %s\n",
+ ssh_get_error(session));
+ exit(-1);
+ }
+
+ channel = ssh_channel_new(session);
+ if (channel == NULL) {
+ return SSH_ERROR;
+ }
+
+ ret = ssh_channel_open_session(channel);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ ret = ssh_channel_request_pty(channel);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ ret = ssh_channel_change_pty_size(channel, 80, 24);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ if (enableX11 == 1) {
+ display = getenv("DISPLAY");
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "display: %s", display);
+
+ if (display) {
+ ssh_callbacks_init(&cb);
+ ret = ssh_set_callbacks(session, &cb);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ ret = x11_get_proto(display, &proto, &cookie);
+ if (ret != 0) {
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__,
+ "Using fake authentication data for X11 forwarding");
+ proto = NULL;
+ cookie = NULL;
+ }
+
+ _ssh_log(SSH_LOG_FUNCTIONS, __func__, "proto: %s - cookie: %s",
+ proto, cookie);
+ /* See https://gitlab.com/libssh/libssh-mirror/-/blob/master/src/channels.c#L2062 for details. */
+ ret = ssh_channel_request_x11(channel, 0, proto, cookie, 0);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+ }
+ }
+
+ ret = _enter_term_raw_mode();
+ if (ret != 0) {
+ exit(-1);
+ }
+
+ ret = ssh_channel_request_shell(channel);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ ret = main_loop(channel);
+ if (ret != SSH_OK) {
+ return ret;
+ }
+
+ _leave_term_raw_mode();
+
+ ssh_channel_close(channel);
+ ssh_channel_free(channel);
+ ssh_disconnect(session);
+ ssh_free(session);
+ ssh_finalize();
+}
diff --git a/examples/ssh_client.c b/examples/ssh_client.c
index aaf0cb5b..cc249183 100644
--- a/examples/ssh_client.c
+++ b/examples/ssh_client.c
@@ -94,7 +94,6 @@ static void usage(void)
"Options :\n"
" -l user : log in as user\n"
" -p port : connect to port\n"
- " -d : use DSS to verify host public key\n"
" -r : use RSA to verify host public key\n"
" -F file : parse configuration file instead of default one\n"
#ifdef WITH_PCAP
@@ -298,25 +297,41 @@ static void shell(ssh_session session)
static void batch_shell(ssh_session session)
{
ssh_channel channel;
- char buffer[PATH_MAX];
- size_t i;
- int s = 0;
+ char *buffer = NULL;
+ size_t i, s, n;
+ channel = ssh_channel_new(session);
+ if (channel == NULL) {
+ return;
+ }
+
+ n = 0;
for (i = 0; i < MAXCMD && cmds[i]; ++i) {
- s += snprintf(buffer + s, sizeof(buffer) - s, "%s ", cmds[i]);
+ /* Including space after cmds[i] */
+ n += strlen(cmds[i]) + 1;
}
+ /* Trailing \0 */
+ n += 1;
- channel = ssh_channel_new(session);
- if (channel == NULL) {
+ buffer = malloc(n);
+ if (buffer == NULL) {
+ ssh_channel_free(channel);
return;
}
+ s = 0;
+ for (i = 0; i < MAXCMD && cmds[i]; ++i) {
+ s += snprintf(buffer + s, n - s, "%s ", cmds[i]);
+ }
+
ssh_channel_open_session(channel);
if (ssh_channel_request_exec(channel, buffer)) {
printf("Error executing '%s' : %s\n", buffer, ssh_get_error(session));
+ free(buffer);
ssh_channel_free(channel);
return;
}
+ free(buffer);
select_loop(session, channel);
ssh_channel_free(channel);
}
diff --git a/examples/ssh_server.c b/examples/ssh_server.c
index 85eb1689..3e9f344b 100644
--- a/examples/ssh_server.c
+++ b/examples/ssh_server.c
@@ -45,36 +45,10 @@ The goal is to show the API in action.
#define BUF_SIZE 1048576
#endif
-#ifndef KEYS_FOLDER
-#ifdef _WIN32
-#define KEYS_FOLDER
-#else
-#define KEYS_FOLDER "/etc/ssh/"
-#endif
-#endif
-
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
+#define AUTH_KEYS_MAX_LINE_SIZE 2048
-static void set_default_keys(ssh_bind sshbind,
- int rsa_already_set,
- int dsa_already_set,
- int ecdsa_already_set) {
- if (!rsa_already_set) {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY,
- KEYS_FOLDER "ssh_host_rsa_key");
- }
- if (!dsa_already_set) {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY,
- KEYS_FOLDER "ssh_host_dsa_key");
- }
- if (!ecdsa_already_set) {
- 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};
char username[128] = "myuser";
@@ -110,19 +84,11 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "dsakey",
- .key = 'd',
- .arg = "FILE",
- .flags = 0,
- .doc = "Set the dsa key.",
- .group = 0
- },
- {
.name = "rsakey",
.key = 'r',
.arg = "FILE",
.flags = 0,
- .doc = "Set the rsa key.",
+ .doc = "Set the rsa key (deprecated alias for 'k').",
.group = 0
},
{
@@ -130,7 +96,7 @@ static struct argp_option options[] = {
.key = 'e',
.arg = "FILE",
.flags = 0,
- .doc = "Set the ecdsa key.",
+ .doc = "Set the ecdsa key (deprecated alias for 'k').",
.group = 0
},
{
@@ -158,14 +124,6 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "no-default-keys",
- .key = 'n',
- .arg = NULL,
- .flags = 0,
- .doc = "Do not set default key locations.",
- .group = 0
- },
- {
.name = "verbose",
.key = 'v',
.arg = NULL,
@@ -181,34 +139,19 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
/* Get the input argument from argp_parse, which we
* know is a pointer to our arguments structure. */
ssh_bind sshbind = state->input;
- static int no_default_keys = 0;
- static int rsa_already_set = 0, dsa_already_set = 0, ecdsa_already_set = 0;
switch (key) {
- case 'n':
- no_default_keys = 1;
- break;
case 'p':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
break;
- case 'd':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
- dsa_already_set = 1;
- break;
case 'k':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
- /* We can't track the types of keys being added with this
- option, so let's ensure we keep the keys we're adding
- by just not setting the default keys */
- no_default_keys = 1;
break;
case 'r':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
- rsa_already_set = 1;
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
break;
case 'e':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, arg);
- ecdsa_already_set = 1;
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
break;
case 'a':
strncpy(authorizedkeys, arg, DEF_STR_SIZE-1);
@@ -235,14 +178,6 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
/* Not enough arguments. */
argp_usage (state);
}
-
- if (!no_default_keys) {
- set_default_keys(sshbind,
- rsa_already_set,
- dsa_already_set,
- ecdsa_already_set);
- }
-
break;
default:
return ARGP_ERR_UNKNOWN;
@@ -256,18 +191,12 @@ static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
int no_default_keys = 0;
int rsa_already_set = 0;
- int dsa_already_set = 0;
int ecdsa_already_set = 0;
int key;
- while((key = getopt(argc, argv, "a:d:e:k:np:P:r:u:v")) != -1) {
- if (key == 'n') {
- no_default_keys = 1;
- } else if (key == 'p') {
+ while((key = getopt(argc, argv, "a:e:k:p:P:r:u:v")) != -1) {
+ if (key == 'p') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, optarg);
- } else if (key == 'd') {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, optarg);
- dsa_already_set = 1;
} else if (key == 'k') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
/* We can't track the types of keys being added with this
@@ -275,10 +204,10 @@ static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
by just not setting the default keys */
no_default_keys = 1;
} else if (key == 'r') {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, optarg);
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
rsa_already_set = 1;
} else if (key == 'e') {
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, optarg);
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
ecdsa_already_set = 1;
} else if (key == 'a') {
strncpy(authorizedkeys, optarg, DEF_STR_SIZE-1);
@@ -299,14 +228,12 @@ static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
"libssh %s -- a Secure Shell protocol implementation\n"
"\n"
" -a, --authorizedkeys=FILE Set the authorized keys file.\n"
- " -d, --dsakey=FILE Set the dsa key.\n"
- " -e, --ecdsakey=FILE Set the ecdsa key.\n"
+ " -e, --ecdsakey=FILE Set the ecdsa key (deprecated alias for 'k').\n"
" -k, --hostkey=FILE Set a host key. Can be used multiple times.\n"
" Implies no default keys.\n"
- " -n, --no-default-keys Do not set default key locations.\n"
" -p, --port=PORT Set the port to bind.\n"
" -P, --pass=PASSWORD Set expected password.\n"
- " -r, --rsakey=FILE Set the rsa key.\n"
+ " -r, --rsakey=FILE Set the rsa key (deprecated alias for 'k').\n"
" -u, --user=USERNAME Set expected username.\n"
" -v, --verbose Get verbose output.\n"
" -?, --help Give this help list\n"
@@ -329,7 +256,6 @@ static int parse_opt(int argc, char **argv, ssh_bind sshbind) {
if (!no_default_keys) {
set_default_keys(sshbind,
rsa_already_set,
- dsa_already_set,
ecdsa_already_set);
}
@@ -536,7 +462,7 @@ static int shell_request(ssh_session session, ssh_channel channel,
static int subsystem_request(ssh_session session, ssh_channel channel,
const char *subsystem, void *userdata) {
- /* subsystem requests behave simillarly to exec requests. */
+ /* subsystem requests behave similarly to exec requests. */
if (strcmp(subsystem, "sftp") == 0) {
return exec_request(session, channel, SFTP_SERVER_PATH, userdata);
}
@@ -565,6 +491,15 @@ static int auth_publickey(ssh_session session,
void *userdata)
{
struct session_data_struct *sdata = (struct session_data_struct *) userdata;
+ ssh_key key = NULL;
+ FILE *fp = NULL;
+ char line[AUTH_KEYS_MAX_LINE_SIZE] = {0};
+ char *p = NULL;
+ const char *q = NULL;
+ unsigned int lineno = 0;
+ int result;
+ int i;
+ enum ssh_keytypes_e type;
(void) user;
(void) session;
@@ -577,31 +512,72 @@ static int auth_publickey(ssh_session session,
return SSH_AUTH_DENIED;
}
- // valid so far. Now look through authorized keys for a match
- if (authorizedkeys[0]) {
- ssh_key key = NULL;
- int result;
- struct stat buf;
-
- if (stat(authorizedkeys, &buf) == 0) {
- result = ssh_pki_import_pubkey_file( authorizedkeys, &key );
- if ((result != SSH_OK) || (key==NULL)) {
- fprintf(stderr,
- "Unable to import public key file %s\n",
- authorizedkeys);
- } else {
- result = ssh_key_cmp( key, pubkey, SSH_KEY_CMP_PUBLIC );
- ssh_key_free(key);
- if (result == 0) {
- sdata->authenticated = 1;
- return SSH_AUTH_SUCCESS;
- }
+ fp = fopen(authorizedkeys, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Error: opening authorized keys file %s failed, reason: %s\n",
+ authorizedkeys, strerror(errno));
+ return SSH_AUTH_DENIED;
+ }
+
+ while (fgets(line, sizeof(line), fp)) {
+ lineno++;
+
+ /* Skip leading whitespace and ignore comments */
+ p = line;
+
+ for (i = 0; i < AUTH_KEYS_MAX_LINE_SIZE; i++) {
+ if (!isspace((int)p[i])) {
+ break;
}
}
+
+ if (p[i] == '#' || p[i] == '\0' || p[i] == '\n') {
+ continue;
+ }
+
+ q = &p[i];
+ for (; i < AUTH_KEYS_MAX_LINE_SIZE; i++) {
+ if (isspace((int)p[i])) {
+ p[i] = '\0';
+ break;
+ }
+ }
+
+ type = ssh_key_type_from_name(q);
+
+ q = &p[i + 1];
+ for (; i < AUTH_KEYS_MAX_LINE_SIZE; i++) {
+ if (isspace((int)p[i])) {
+ p[i] = '\0';
+ break;
+ }
+ }
+
+ result = ssh_pki_import_pubkey_base64(q, type, &key);
+ if (result != SSH_OK) {
+ fprintf(stderr,
+ "Warning: Cannot import key on line no. %d in authorized keys file: %s\n",
+ lineno,
+ authorizedkeys);
+ continue;
+ }
+
+ result = ssh_key_cmp(key, pubkey, SSH_KEY_CMP_PUBLIC);
+ ssh_key_free(key);
+ if (result == 0) {
+ sdata->authenticated = 1;
+ fclose(fp);
+ return SSH_AUTH_SUCCESS;
+ }
+ }
+ if (ferror(fp) != 0) {
+ fprintf(stderr,
+ "Error: Reading from authorized keys file %s failed, reason: %s\n",
+ authorizedkeys, strerror(errno));
}
+ fclose(fp);
- // no matches
- sdata->authenticated = 0;
+ /* no matches */
return SSH_AUTH_DENIED;
}
diff --git a/examples/sshd_direct-tcpip.c b/examples/sshd_direct-tcpip.c
index 589c0889..84389b28 100644
--- a/examples/sshd_direct-tcpip.c
+++ b/examples/sshd_direct-tcpip.c
@@ -15,7 +15,7 @@ clients must be made or how a client should react.
/*
Example:
- ./sshd_direct-tcpip -v -p 2022 -d serverkey.dsa -r serverkey.rsa 127.0.0.1
+ ./sshd_direct-tcpip -v -p 2022 -r serverkey.rsa 127.0.0.1
*/
#include "config.h"
@@ -27,6 +27,9 @@ clients must be made or how a client should react.
#ifdef HAVE_ARGP_H
#include <argp.h>
#endif
+#ifndef _WIN32
+#include <netinet/in.h>
+#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <stdbool.h>
@@ -91,8 +94,15 @@ cleanup_push(struct cleanup_node_struct** head_ref,
{
// Allocate memory for node
struct cleanup_node_struct *new_node = malloc(sizeof *new_node);
+ if (new_node == NULL) {
+ return;
+ }
- new_node->next = (*head_ref);
+ if (*head_ref != NULL) {
+ new_node->next = *head_ref;
+ } else {
+ new_node->next = NULL;
+ }
// Copy new_data
new_node->data = new_data;
@@ -193,7 +203,7 @@ subsystem_request(UNUSED_PARAM(ssh_session session),
UNUSED_PARAM(void *userdata))
{
_ssh_log(SSH_LOG_PROTOCOL,
- "=== subsystem_request", "Channel subsystem reqeuest: %s",
+ "=== subsystem_request", "Channel subsystem request: %s",
subsystem);
return 0;
}
@@ -289,7 +299,7 @@ my_channel_eof_function(ssh_session session,
_ssh_log(SSH_LOG_PROTOCOL,
"=== my_channel_eof_function",
- "Got EOF on channel. Shuting down write on socket (fd = %d).",
+ "Got EOF on channel. Shutting down write on socket (fd = %d).",
*event_fd_data->p_fd);
stack_socket_close(session, event_fd_data);
@@ -418,7 +428,7 @@ my_fd_data_function(UNUSED_PARAM(socket_t fd),
break;
}
wr += i;
- _ssh_log(SSH_LOG_FUNCTIONS, "=== my_fd_data_function", "channel_write (%d from %d)", wr, len);
+ _ssh_log(SSH_LOG_FUNCTIONS, "=== my_fd_data_function", "ssh_channel_write (%d from %d)", wr, len);
} while (i > 0 && wr < len);
}
else {
@@ -518,6 +528,13 @@ message_callback(UNUSED_PARAM(ssh_session session),
pFd = malloc(sizeof *pFd);
cb_chan = malloc(sizeof *cb_chan);
event_fd_data = malloc(sizeof *event_fd_data);
+ if (pFd == NULL || cb_chan == NULL || event_fd_data == NULL) {
+ SAFE_FREE(pFd);
+ SAFE_FREE(cb_chan);
+ SAFE_FREE(event_fd_data);
+ close(socket_fd);
+ return 1;
+ }
(*pFd) = socket_fd;
event_fd_data->channel = channel;
@@ -573,19 +590,11 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "dsakey",
- .key = 'd',
- .arg = "FILE",
- .flags = 0,
- .doc = "Set the dsa key.",
- .group = 0
- },
- {
.name = "rsakey",
.key = 'r',
.arg = "FILE",
.flags = 0,
- .doc = "Set the rsa key.",
+ .doc = "Set the rsa key (deprecated alias for 'k').",
.group = 0
},
{
@@ -612,15 +621,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
case 'p':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
break;
- case 'd':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
- break;
+ case 'r':
case 'k':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
break;
- case 'r':
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
- break;
case 'v':
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "1");
break;
@@ -671,8 +675,7 @@ main(int argc, char **argv)
session = ssh_new();
mainloop = ssh_event_new();
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key");
- ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key");
+ ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, KEYS_FOLDER "ssh_host_rsa_key");
#ifdef HAVE_ARGP_H
/*
diff --git a/examples/sshnetcat.c b/examples/sshnetcat.c
index 9bc5d52e..59b0a289 100644
--- a/examples/sshnetcat.c
+++ b/examples/sshnetcat.c
@@ -238,9 +238,10 @@ void set_pcap(ssh_session session){
}
void cleanup_pcap(void);
-void cleanup_pcap(){
+void cleanup_pcap(void)
+{
ssh_pcap_file_free(pcap);
- pcap=NULL;
+ pcap = NULL;
}
#endif
diff --git a/include/libssh/CMakeLists.txt b/include/libssh/CMakeLists.txt
index 83e7b9f8..93445680 100644
--- a/include/libssh/CMakeLists.txt
+++ b/include/libssh/CMakeLists.txt
@@ -20,6 +20,13 @@ if (WITH_SERVER)
${libssh_HDRS}
server.h
)
+
+ if (WITH_SFTP)
+ set(libssh_HDRS
+ ${libssh_HDRS}
+ sftpserver.h
+ )
+ endif (WITH_SFTP)
endif (WITH_SERVER)
install(
diff --git a/include/libssh/agent.h b/include/libssh/agent.h
index d4eefbbf..caf8d3e2 100644
--- a/include/libssh/agent.h
+++ b/include/libssh/agent.h
@@ -70,6 +70,10 @@
#define SSH_AGENT_RSA_SHA2_256 0x02
#define SSH_AGENT_RSA_SHA2_512 0x04
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct ssh_agent_struct {
struct ssh_socket_struct *sock;
ssh_buffer ident;
@@ -77,7 +81,6 @@ struct ssh_agent_struct {
ssh_channel channel;
};
-#ifndef _WIN32
/* agent.c */
/**
* @brief Create a new ssh agent structure.
@@ -115,6 +118,9 @@ ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
ssh_string ssh_agent_sign_data(ssh_session session,
const ssh_key pubkey,
struct ssh_buffer_struct *data);
+
+#ifdef __cplusplus
+}
#endif
#endif /* __AGENT_H */
diff --git a/include/libssh/auth.h b/include/libssh/auth.h
index 90b377d4..b358b7a2 100644
--- a/include/libssh/auth.h
+++ b/include/libssh/auth.h
@@ -23,6 +23,10 @@
#include "config.h"
#include "libssh/callbacks.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
SSH_PACKET_CALLBACK(ssh_packet_userauth_banner);
SSH_PACKET_CALLBACK(ssh_packet_userauth_failure);
SSH_PACKET_CALLBACK(ssh_packet_userauth_success);
@@ -100,4 +104,8 @@ enum ssh_auth_service_state_e {
SSH_AUTH_SERVICE_DENIED,
};
+#ifdef __cplusplus
+}
+#endif
+
#endif /* AUTH_H_ */
diff --git a/include/libssh/bignum.h b/include/libssh/bignum.h
index 726ed7b9..6b5dc1a2 100644
--- a/include/libssh/bignum.h
+++ b/include/libssh/bignum.h
@@ -25,9 +25,16 @@
#include "libssh/libgcrypt.h"
#include "libssh/libmbedcrypto.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
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);
+#ifdef __cplusplus
+}
+#endif
#endif /* BIGNUM_H_ */
diff --git a/include/libssh/bind.h b/include/libssh/bind.h
index 94256d4a..cd9199b6 100644
--- a/include/libssh/bind.h
+++ b/include/libssh/bind.h
@@ -25,6 +25,10 @@
#include "libssh/kex.h"
#include "libssh/session.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct ssh_bind_struct {
struct ssh_common_struct common; /* stuff common to ssh_bind and ssh_session */
struct ssh_bind_callbacks_struct *bind_callbacks;
@@ -35,11 +39,9 @@ struct ssh_bind_struct {
char *wanted_methods[SSH_KEX_METHODS];
char *banner;
char *ecdsakey;
- char *dsakey;
char *rsakey;
char *ed25519key;
ssh_key ecdsa;
- ssh_key dsa;
ssh_key rsa;
ssh_key ed25519;
char *bindaddr;
@@ -51,10 +53,14 @@ struct ssh_bind_struct {
char *config_dir;
char *pubkey_accepted_key_types;
char* moduli_file;
+ int rsa_min_size;
};
struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct
*sshbind);
+#ifdef __cplusplus
+}
+#endif
#endif /* BIND_H_ */
diff --git a/include/libssh/bind_config.h b/include/libssh/bind_config.h
index cb68da89..5f2dccce 100644
--- a/include/libssh/bind_config.h
+++ b/include/libssh/bind_config.h
@@ -28,6 +28,10 @@
#include "libssh/server.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
enum ssh_bind_config_opcode_e {
/* Known but not allowed in Match block */
BIND_CFG_NOT_ALLOWED_IN_MATCH = -4,
@@ -61,4 +65,18 @@ enum ssh_bind_config_opcode_e {
*/
int ssh_bind_config_parse_file(ssh_bind sshbind, const char *filename);
+/* @brief Parse configuration string and set the options to the given bind session
+ *
+ * @params[in] bind The ssh bind session
+ * @params[in] input Null terminated string containing the configuration
+ *
+ * @returns SSH_OK on successful parsing the configuration string,
+ * SSH_ERROR on error
+ */
+int ssh_bind_config_parse_string(ssh_bind bind, const char *input);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* BIND_CONFIG_H_ */
diff --git a/include/libssh/blf.h b/include/libssh/blf.h
index ce131e6b..201821a2 100644
--- a/include/libssh/blf.h
+++ b/include/libssh/blf.h
@@ -49,6 +49,10 @@
#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */
#define BLF_MAXUTILIZED ((BLF_N+2)*4) /* 576 bits */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Blowfish context */
typedef struct BlowfishContext {
uint32_t S[4][256]; /* S-Boxes */
@@ -84,4 +88,9 @@ void ssh_blf_cbc_decrypt(ssh_blf_ctx *, uint8_t *, uint8_t *, uint32_t);
uint32_t Blowfish_stream2word(const uint8_t *, uint16_t , uint16_t *);
#endif /* !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H) */
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _BLF_H */
diff --git a/include/libssh/buffer.h b/include/libssh/buffer.h
index cd2dea6a..1fce7b76 100644
--- a/include/libssh/buffer.h
+++ b/include/libssh/buffer.h
@@ -27,6 +27,10 @@
#define SSH_BUFFER_PACK_END ((uint32_t) 0x4f65feb3)
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void ssh_buffer_set_secure(ssh_buffer buffer);
int ssh_buffer_add_ssh_string(ssh_buffer buffer, ssh_string string);
int ssh_buffer_add_u8(ssh_buffer buffer, uint8_t data);
@@ -63,9 +67,9 @@ int ssh_buffer_prepend_data(ssh_buffer buffer, const void *data, uint32_t len);
int ssh_buffer_add_buffer(ssh_buffer buffer, ssh_buffer source);
/* buffer_read_*() returns the number of bytes read, except for ssh strings */
-int ssh_buffer_get_u8(ssh_buffer buffer, uint8_t *data);
-int ssh_buffer_get_u32(ssh_buffer buffer, uint32_t *data);
-int ssh_buffer_get_u64(ssh_buffer buffer, uint64_t *data);
+uint32_t ssh_buffer_get_u8(ssh_buffer buffer, uint8_t *data);
+uint32_t ssh_buffer_get_u32(ssh_buffer buffer, uint32_t *data);
+uint32_t ssh_buffer_get_u64(ssh_buffer buffer, uint64_t *data);
/* ssh_buffer_get_ssh_string() is an exception. if the String read is too large or invalid, it will answer NULL. */
ssh_string ssh_buffer_get_ssh_string(ssh_buffer buffer);
@@ -74,4 +78,8 @@ ssh_string ssh_buffer_get_ssh_string(ssh_buffer buffer);
uint32_t ssh_buffer_pass_bytes_end(ssh_buffer buffer, uint32_t len);
uint32_t ssh_buffer_pass_bytes(ssh_buffer buffer, uint32_t len);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* BUFFER_H_ */
diff --git a/include/libssh/callbacks.h b/include/libssh/callbacks.h
index 972314cb..29c1d391 100644
--- a/include/libssh/callbacks.h
+++ b/include/libssh/callbacks.h
@@ -27,6 +27,7 @@
#include <libssh/libssh.h>
#include <string.h>
+#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
@@ -56,7 +57,7 @@ typedef void (*ssh_callback_int) (int code, void *user);
* @returns number of bytes processed by the callee. The remaining bytes will
* be sent in the next callback message, when more data is available.
*/
-typedef int (*ssh_callback_data) (const void *data, size_t len, void *user);
+typedef size_t (*ssh_callback_data) (const void *data, size_t len, void *user);
typedef void (*ssh_callback_int_int) (int code, int errno_code, void *user);
@@ -81,9 +82,9 @@ typedef void (*ssh_log_callback) (ssh_session session, int priority,
*
* @param priority Priority of the log, the smaller being the more important.
*
- * @param function The function name calling the the logging fucntions.
+ * @param function The function name calling the logging functions.
*
- * @param message The actual message
+ * @param buffer The actual message
*
* @param userdata Userdata to be passed to the callback function.
*/
@@ -117,6 +118,8 @@ typedef void (*ssh_global_request_callback) (ssh_session session,
* sends back an X11 connection attempt. This is a client-side API
* @param session current session handler
* @param userdata Userdata to be passed to the callback function.
+ * @param originator_address IP address of the machine who sent the request
+ * @param originator_port port number of the machine who sent the request
* @returns a valid ssh_channel handle if the request is to be allowed
* @returns NULL if the request should not be allowed
* @warning The channel pointer returned by this callback must be closed by the application.
@@ -137,6 +140,26 @@ typedef ssh_channel (*ssh_channel_open_request_auth_agent_callback) (ssh_session
void *userdata);
/**
+ * @brief Handles an SSH new channel open "forwarded-tcpip" request. This
+ * happens when the server forwards an incoming TCP connection on a port it was
+ * previously requested to listen on. This is a client-side API
+ * @param session current session handler
+ * @param destination_address the address that the TCP connection connected to
+ * @param destination_port the port that the TCP connection connected to
+ * @param originator_address the originator IP address
+ * @param originator_port the originator port
+ * @param userdata Userdata to be passed to the callback function.
+ * @returns a valid ssh_channel handle if the request is to be allowed
+ * @returns NULL if the request should not be allowed
+ * @warning The channel pointer returned by this callback must be closed by the
+ * application.
+ */
+typedef ssh_channel (*ssh_channel_open_request_forwarded_tcpip_callback) (ssh_session session,
+ const char *destination_address, int destination_port,
+ const char *originator_address, int originator_port,
+ void *userdata);
+
+/**
* The structure to replace libssh functions with appropriate callbacks.
*/
struct ssh_callbacks_struct {
@@ -169,6 +192,11 @@ struct ssh_callbacks_struct {
/** This function will be called when an incoming "auth-agent" request is received.
*/
ssh_channel_open_request_auth_agent_callback channel_open_request_auth_agent_function;
+ /**
+ * This function will be called when an incoming "forwarded-tcpip"
+ * request is received.
+ */
+ ssh_channel_open_request_forwarded_tcpip_callback channel_open_request_forwarded_tcpip_function;
};
typedef struct ssh_callbacks_struct *ssh_callbacks;
@@ -221,8 +249,8 @@ typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *us
* @param user User that wants to authenticate
* @param pubkey public key used for authentication
* @param signature_state SSH_PUBLICKEY_STATE_NONE if the key is not signed (simple public key probe),
- * SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be
- * replied with a SSH_AUTH_DENIED.
+ * SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be
+ * replied with a SSH_AUTH_DENIED.
* @param userdata Userdata to be passed to the callback function.
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
@@ -268,11 +296,11 @@ typedef ssh_string (*ssh_gssapi_select_oid_callback) (ssh_session session, const
int n_oid, ssh_string *oids, void *userdata);
/*
- * @brief handle the negociation of a security context, server side.
+ * @brief handle the negotiation of a security context, server side.
* @param session current session handler
* @param[in] input_token input token provided by client
* @param[out] output_token output of the gssapi accept_sec_context method,
- * NULL after completion.
+ * NULL after completion.
* @returns SSH_OK if the token was generated correctly or accept_sec_context
* returned GSS_S_COMPLETE
* @returns SSH_ERROR in case of error
@@ -397,7 +425,7 @@ struct ssh_socket_callbacks_struct {
*/
ssh_callback_int_int exception;
/** This function is called when the ssh_socket_connect was used on the socket
- * on nonblocking state, and the connection successed.
+ * on nonblocking state, and the connection succeeded.
*/
ssh_callback_int_int connected;
};
@@ -625,6 +653,7 @@ typedef void (*ssh_channel_signal_callback) (ssh_session session,
* @brief SSH channel exit status callback. Called when a channel has received an exit status
* @param session Current session handler
* @param channel the actual channel
+ * @param exit_status Exit status of the ran command
* @param userdata Userdata to be passed to the callback function.
*/
typedef void (*ssh_channel_exit_status_callback) (ssh_session session,
@@ -637,7 +666,7 @@ typedef void (*ssh_channel_exit_status_callback) (ssh_session session,
* @param session Current session handler
* @param channel the actual channel
* @param signal the signal name (without the SIG prefix)
- * @param core a boolean telling wether a core has been dumped or not
+ * @param core a boolean telling whether a core has been dumped or not
* @param errmsg the description of the exception
* @param lang the language of the description (format: RFC 3066)
* @param userdata Userdata to be passed to the callback function.
@@ -652,12 +681,13 @@ typedef void (*ssh_channel_exit_signal_callback) (ssh_session session,
/**
* @brief SSH channel PTY request from a client.
+ * @param session the session
* @param channel the channel
* @param term The type of terminal emulation
* @param width width of the terminal, in characters
* @param height height of the terminal, in characters
* @param pxwidth width of the terminal, in pixels
- * @param pxheight height of the terminal, in pixels
+ * @param pwheight height of the terminal, in pixels
* @param userdata Userdata to be passed to the callback function.
* @returns 0 if the pty request is accepted
* @returns -1 if the request is denied
@@ -671,6 +701,7 @@ typedef int (*ssh_channel_pty_request_callback) (ssh_session session,
/**
* @brief SSH channel Shell request from a client.
+ * @param session the session
* @param channel the channel
* @param userdata Userdata to be passed to the callback function.
* @returns 0 if the shell request is accepted
@@ -683,6 +714,7 @@ typedef int (*ssh_channel_shell_request_callback) (ssh_session session,
* @brief SSH auth-agent-request from the client. This request is
* sent by a client when agent forwarding is available.
* Server is free to ignore this callback, no answer is expected.
+ * @param session the session
* @param channel the channel
* @param userdata Userdata to be passed to the callback function.
*/
@@ -694,7 +726,12 @@ typedef void (*ssh_channel_auth_agent_req_callback) (ssh_session session,
* @brief SSH X11 request from the client. This request is
* sent by a client when X11 forwarding is requested(and available).
* Server is free to ignore this callback, no answer is expected.
+ * @param session the session
* @param channel the channel
+ * @param single_connection If true, only one channel should be forwarded
+ * @param auth_protocol The X11 authentication method to be used
+ * @param auth_cookie Authentication cookie encoded hexadecimal
+ * @param screen_number Screen number
* @param userdata Userdata to be passed to the callback function.
*/
typedef void (*ssh_channel_x11_req_callback) (ssh_session session,
@@ -706,11 +743,12 @@ typedef void (*ssh_channel_x11_req_callback) (ssh_session session,
void *userdata);
/**
* @brief SSH channel PTY windows change (terminal size) from a client.
+ * @param session the session
* @param channel the channel
* @param width width of the terminal, in characters
* @param height height of the terminal, in characters
* @param pxwidth width of the terminal, in pixels
- * @param pxheight height of the terminal, in pixels
+ * @param pwheight height of the terminal, in pixels
* @param userdata Userdata to be passed to the callback function.
* @returns 0 if the pty request is accepted
* @returns -1 if the request is denied
@@ -723,6 +761,7 @@ typedef int (*ssh_channel_pty_window_change_callback) (ssh_session session,
/**
* @brief SSH channel Exec request from a client.
+ * @param session the session
* @param channel the channel
* @param command the shell command to be executed
* @param userdata Userdata to be passed to the callback function.
@@ -736,6 +775,7 @@ typedef int (*ssh_channel_exec_request_callback) (ssh_session session,
/**
* @brief SSH channel environment request from a client.
+ * @param session the session
* @param channel the channel
* @param env_name name of the environment value to be set
* @param env_value value of the environment value to be set
@@ -752,6 +792,7 @@ typedef int (*ssh_channel_env_request_callback) (ssh_session session,
void *userdata);
/**
* @brief SSH channel subsystem request from a client.
+ * @param session the session
* @param channel the channel
* @param subsystem the subsystem required
* @param userdata Userdata to be passed to the callback function.
@@ -766,6 +807,8 @@ typedef int (*ssh_channel_subsystem_request_callback) (ssh_session session,
/**
* @brief SSH channel write will not block (flow control).
*
+ * @param session the session
+ *
* @param channel the channel
*
* @param[in] bytes size of the remote window in bytes. Writing as much data
@@ -777,9 +820,31 @@ typedef int (*ssh_channel_subsystem_request_callback) (ssh_session session,
*/
typedef int (*ssh_channel_write_wontblock_callback) (ssh_session session,
ssh_channel channel,
- size_t bytes,
+ uint32_t bytes,
void *userdata);
+/**
+ * @brief SSH channel open callback. Called when a channel open succeeds or fails.
+ * @param session Current session handler
+ * @param channel the actual channel
+ * @param is_success is 1 when the open succeeds, and 0 otherwise.
+ * @param userdata Userdata to be passed to the callback function.
+ */
+typedef void (*ssh_channel_open_resp_callback) (ssh_session session,
+ ssh_channel channel,
+ bool is_success,
+ void *userdata);
+
+/**
+ * @brief SSH channel request response callback. Called when a response to the pending request is received.
+ * @param session Current session handler
+ * @param channel the actual channel
+ * @param userdata Userdata to be passed to the callback function.
+ */
+typedef void (*ssh_channel_request_resp_callback) (ssh_session session,
+ ssh_channel channel,
+ void *userdata);
+
struct ssh_channel_callbacks_struct {
/** DON'T SET THIS use ssh_callbacks_init() instead. */
size_t size;
@@ -847,6 +912,14 @@ struct ssh_channel_callbacks_struct {
* not to block.
*/
ssh_channel_write_wontblock_callback channel_write_wontblock_function;
+ /**
+ * This functions will be called when the channel has received a channel open confirmation or failure.
+ */
+ ssh_channel_open_resp_callback channel_open_response_function;
+ /**
+ * This functions will be called when the channel has received the response to the pending request.
+ */
+ ssh_channel_request_resp_callback channel_request_response_function;
};
typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks;
@@ -917,7 +990,7 @@ LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
/** @} */
-/** @group libssh_threads
+/** @addtogroup libssh_threads
* @{
*/
@@ -983,13 +1056,14 @@ LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void);
* @see ssh_threads_set_callbacks
*/
LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void);
+/** @} */
/**
* @brief Set the logging callback function.
*
* @param[in] cb The callback to set.
*
- * @return 0 on success, < 0 on errror.
+ * @return 0 on success, < 0 on error.
*/
LIBSSH_API int ssh_set_log_callback(ssh_logging_callback cb);
@@ -1000,7 +1074,6 @@ LIBSSH_API int ssh_set_log_callback(ssh_logging_callback cb);
*/
LIBSSH_API ssh_logging_callback ssh_get_log_callback(void);
-/** @} */
#ifdef __cplusplus
}
#endif
diff --git a/include/libssh/chacha.h b/include/libssh/chacha.h
index 867f532d..ab3fe492 100644
--- a/include/libssh/chacha.h
+++ b/include/libssh/chacha.h
@@ -18,6 +18,10 @@ struct chacha_ctx {
#define CHACHA_CTRLEN 8
#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN)
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void chacha_keysetup(struct chacha_ctx *x, const uint8_t *k, uint32_t kbits)
#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
__attribute__((__bounded__(__minbytes__, 2, CHACHA_MINKEYLEN)))
@@ -37,4 +41,8 @@ void chacha_encrypt_bytes(struct chacha_ctx *x, const uint8_t *m,
#endif
;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* CHACHA_H */
diff --git a/include/libssh/channels.h b/include/libssh/channels.h
index bbabcfd1..cb2bea43 100644
--- a/include/libssh/channels.h
+++ b/include/libssh/channels.h
@@ -22,6 +22,10 @@
#define CHANNELS_H_
#include "libssh/priv.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** @internal
* Describes the different possible states in a
* outgoing (client) channel request
@@ -35,7 +39,7 @@ enum ssh_channel_request_state_e {
SSH_CHANNEL_REQ_STATE_ACCEPTED,
/** A request has been replied and refused */
SSH_CHANNEL_REQ_STATE_DENIED,
- /** A request has been replied and an error happend */
+ /** A request has been replied and an error happened */
SSH_CHANNEL_REQ_STATE_ERROR
};
@@ -98,7 +102,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request);
SSH_PACKET_CALLBACK(channel_rcv_data);
int channel_default_bufferize(ssh_channel channel,
- void *data, size_t len,
+ void *data, uint32_t len,
bool is_stderr);
int ssh_channel_flush(ssh_channel channel);
uint32_t ssh_channel_new_id(ssh_session session);
@@ -109,4 +113,8 @@ int ssh_global_request(ssh_session session,
ssh_buffer buffer,
int reply);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* CHANNELS_H_ */
diff --git a/include/libssh/config.h b/include/libssh/config.h
index f2b9b57c..4f8a9b6f 100644
--- a/include/libssh/config.h
+++ b/include/libssh/config.h
@@ -42,7 +42,6 @@ enum ssh_config_opcode_e {
SOC_MACS,
SOC_COMPRESSION,
SOC_TIMEOUT,
- SOC_PROTOCOL,
SOC_STRICTHOSTKEYCHECK,
SOC_KNOWNHOSTS,
SOC_PROXYCOMMAND,
@@ -60,8 +59,13 @@ enum ssh_config_opcode_e {
SOC_KBDINTERACTIVEAUTHENTICATION,
SOC_PASSWORDAUTHENTICATION,
SOC_PUBKEYAUTHENTICATION,
- SOC_PUBKEYACCEPTEDTYPES,
+ SOC_PUBKEYACCEPTEDKEYTYPES,
SOC_REKEYLIMIT,
+ SOC_IDENTITYAGENT,
+ SOC_IDENTITIESONLY,
+ SOC_CONTROLMASTER,
+ SOC_CONTROLPATH,
+ SOC_CERTIFICATE,
SOC_MAX /* Keep this one last in the list */
};
diff --git a/include/libssh/config_parser.h b/include/libssh/config_parser.h
index e974917c..4648614c 100644
--- a/include/libssh/config_parser.h
+++ b/include/libssh/config_parser.h
@@ -26,6 +26,12 @@
#ifndef CONFIG_PARSER_H_
#define CONFIG_PARSER_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+
char *ssh_config_get_cmd(char **str);
char *ssh_config_get_token(char **str);
@@ -45,13 +51,20 @@ int ssh_config_get_yesno(char **str, int notfound);
* be stored or NULL if we do not care about the result.
* @param[out] port Pointer to the location, where the new port will
* be stored or NULL if we do not care about the result.
+ * @param[in] ignore_port Set to true if we should not attempt to parse
+ * port number.
*
* @returns SSH_OK if the provided string is in format of SSH URI,
* SSH_ERROR on failure
*/
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port);
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* LIBSSH_CONFIG_H_ */
diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h
index 67d98392..32016827 100644
--- a/include/libssh/crypto.h
+++ b/include/libssh/crypto.h
@@ -111,7 +111,11 @@ struct ssh_crypto_struct {
#endif /* WITH_GEX */
#ifdef HAVE_ECDH
#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_KEY *ecdh_privkey;
+#else
+ EVP_PKEY *ecdh_privkey;
+#endif /* OPENSSL_VERSION_NUMBER */
#elif defined HAVE_GCRYPT_ECC
gcry_sexp_t ecdh_privkey;
#elif defined HAVE_LIBMBEDCRYPTO
@@ -126,8 +130,9 @@ struct ssh_crypto_struct {
ssh_curve25519_pubkey curve25519_server_pubkey;
#endif
ssh_string dh_server_signature; /* information used by dh_handshake. */
- size_t digest_len; /* len of the two fields below */
+ size_t session_id_len;
unsigned char *session_id;
+ size_t digest_len; /* len of the secret hash */
unsigned char *secret_hash; /* Secret hash is same as session id until re-kex */
unsigned char *encryptIV;
unsigned char *decryptIV;
@@ -207,12 +212,23 @@ struct ssh_cipher_struct {
void (*cleanup)(struct ssh_cipher_struct *cipher);
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
const struct ssh_cipher_struct *ssh_get_chacha20poly1305_cipher(void);
int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len);
int secure_memcmp(const void *s1, const void *s2, size_t n);
+#if defined(HAVE_LIBCRYPTO) && !defined(WITH_PKCS11_PROVIDER)
+ENGINE *pki_get_engine(void);
+#endif /* HAVE_LIBCRYPTO */
+
+#ifdef __cplusplus
+}
+#endif
#endif /* _CRYPTO_H_ */
diff --git a/include/libssh/curve25519.h b/include/libssh/curve25519.h
index f0cc6348..a55f52c7 100644
--- a/include/libssh/curve25519.h
+++ b/include/libssh/curve25519.h
@@ -33,6 +33,10 @@
#define crypto_scalarmult crypto_scalarmult_curve25519
#else
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define CURVE25519_PUBKEY_SIZE 32
#define CURVE25519_PRIVKEY_SIZE 32
int crypto_scalarmult_base(unsigned char *q, const unsigned char *n);
@@ -48,9 +52,14 @@ typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE];
int ssh_client_curve25519_init(ssh_session session);
+void ssh_client_curve25519_remove_callbacks(ssh_session session);
#ifdef WITH_SERVER
void ssh_server_curve25519_init(ssh_session session);
#endif /* WITH_SERVER */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* CURVE25519_H_ */
diff --git a/include/libssh/dh-gex.h b/include/libssh/dh-gex.h
index 4fc23d82..0f547e37 100644
--- a/include/libssh/dh-gex.h
+++ b/include/libssh/dh-gex.h
@@ -23,10 +23,19 @@
#ifndef SRC_DH_GEX_H_
#define SRC_DH_GEX_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int ssh_client_dhgex_init(ssh_session session);
+void ssh_client_dhgex_remove_callbacks(ssh_session session);
#ifdef WITH_SERVER
void ssh_server_dhgex_init(ssh_session session);
#endif /* WITH_SERVER */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* SRC_DH_GEX_H_ */
diff --git a/include/libssh/dh.h b/include/libssh/dh.h
index 390b30da..34c4a7ed 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -30,20 +30,34 @@ struct dh_ctx;
#define DH_CLIENT_KEYPAIR 0
#define DH_SERVER_KEYPAIR 1
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* functions implemented by crypto backends */
int ssh_dh_init_common(struct ssh_crypto_struct *crypto);
void ssh_dh_cleanup(struct ssh_crypto_struct *crypto);
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
int ssh_dh_get_parameters(struct dh_ctx *ctx,
const_bignum *modulus, const_bignum *generator);
+#else
+int ssh_dh_get_parameters(struct dh_ctx *ctx,
+ bignum *modulus, bignum *generator);
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_set_parameters(struct dh_ctx *ctx,
const bignum modulus, const bignum generator);
int ssh_dh_keypair_gen_keys(struct dh_ctx *ctx, int peer);
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
const_bignum *priv, const_bignum *pub);
+#else
+int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
+ bignum *priv, bignum *pub);
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
- const bignum priv, const bignum pub);
+ bignum priv, bignum pub);
int ssh_dh_compute_shared_secret(struct dh_ctx *ctx, int local, int remote,
bignum *dest);
@@ -63,8 +77,10 @@ int ssh_dh_get_current_server_publickey_blob(ssh_session session,
ssh_key ssh_dh_get_next_server_publickey(ssh_session session);
int ssh_dh_get_next_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob);
+int dh_handshake(ssh_session session);
int ssh_client_dh_init(ssh_session session);
+void ssh_client_dh_remove_callbacks(ssh_session session);
#ifdef WITH_SERVER
void ssh_server_dh_init(ssh_session session);
#endif /* WITH_SERVER */
@@ -72,4 +88,8 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet);
int ssh_fallback_group(uint32_t pmax, bignum *p, bignum *g);
bool ssh_dh_is_known_group(bignum modulus, bignum generator);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* DH_H_ */
diff --git a/include/libssh/ecdh.h b/include/libssh/ecdh.h
index 17fe02e7..4c4c54eb 100644
--- a/include/libssh/ecdh.h
+++ b/include/libssh/ecdh.h
@@ -42,9 +42,14 @@
#define HAVE_ECDH 1
#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks;
/* Backend-specific functions. */
int ssh_client_ecdh_init(ssh_session session);
+void ssh_client_ecdh_remove_callbacks(ssh_session session);
int ecdh_build_k(ssh_session session);
#ifdef WITH_SERVER
@@ -53,4 +58,8 @@ void ssh_server_ecdh_init(ssh_session session);
SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init);
#endif /* WITH_SERVER */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* ECDH_H_ */
diff --git a/include/libssh/ed25519.h b/include/libssh/ed25519.h
index 8a3263c8..72a86c0b 100644
--- a/include/libssh/ed25519.h
+++ b/include/libssh/ed25519.h
@@ -24,10 +24,10 @@
/**
* @defgroup ed25519 ed25519 API
- * @internal
* @brief API for DJB's ed25519
*
- * @{ */
+ * @{
+ */
#define ED25519_PK_LEN 32
#define ED25519_SK_LEN 64
@@ -37,6 +37,10 @@ typedef uint8_t ed25519_pubkey[ED25519_PK_LEN];
typedef uint8_t ed25519_privkey[ED25519_SK_LEN];
typedef uint8_t ed25519_signature[ED25519_SIG_LEN];
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** @internal
* @brief generate an ed25519 key pair
* @param[out] pk generated public key
@@ -76,4 +80,8 @@ int crypto_sign_ed25519_open(
const ed25519_pubkey pk);
/** @} */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* ED25519_H_ */
diff --git a/include/libssh/fe25519.h b/include/libssh/fe25519.h
index 438d85db..0dfb0613 100644
--- a/include/libssh/fe25519.h
+++ b/include/libssh/fe25519.h
@@ -33,6 +33,10 @@ typedef struct {
uint32_t v[32];
} fe25519;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void fe25519_freeze(fe25519 *r);
void fe25519_unpack(fe25519 *r, const unsigned char x[32]);
@@ -65,4 +69,8 @@ void fe25519_invert(fe25519 *r, const fe25519 *x);
void fe25519_pow2523(fe25519 *r, const fe25519 *x);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/include/libssh/ge25519.h b/include/libssh/ge25519.h
index 329bd042..480f29dc 100644
--- a/include/libssh/ge25519.h
+++ b/include/libssh/ge25519.h
@@ -28,6 +28,10 @@ typedef struct
fe25519 t;
} ge25519;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern const ge25519 ge25519_base;
int ge25519_unpackneg_vartime(ge25519 *r, const unsigned char p[32]);
@@ -40,4 +44,8 @@ void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const sc25
void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/include/libssh/gssapi.h b/include/libssh/gssapi.h
index ccd83664..b0c74c73 100644
--- a/include/libssh/gssapi.h
+++ b/include/libssh/gssapi.h
@@ -29,6 +29,10 @@
typedef struct ssh_gssapi_struct *ssh_gssapi;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef WITH_SERVER
int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server);
@@ -42,4 +46,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response);
int ssh_gssapi_auth_mic(ssh_session session);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* GSSAPI_H */
diff --git a/include/libssh/kex.h b/include/libssh/kex.h
index 3a1f4a6f..4a2ecb99 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -31,15 +31,24 @@ struct ssh_kex_struct {
char *methods[SSH_KEX_METHODS];
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
SSH_PACKET_CALLBACK(ssh_packet_kexinit);
-int ssh_send_kex(ssh_session session, int server_kex);
+int ssh_send_kex(ssh_session session);
void ssh_list_kex(struct ssh_kex_struct *kex);
int ssh_set_client_kex(ssh_session session);
+int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex);
int ssh_kex_select_methods(ssh_session session);
int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name);
char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list);
char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list);
+char *ssh_add_to_default_algos(enum ssh_kex_types_e algo, const char *list);
+char *ssh_remove_from_default_algos(enum ssh_kex_types_e algo,
+ const char *list);
+char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list);
char **ssh_space_tokenize(const char *chain);
int ssh_get_kex1(ssh_session session);
char *ssh_find_matching(const char *in_d, const char *what_d);
@@ -56,4 +65,8 @@ int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie);
int ssh_hashbufout_add_cookie(ssh_session session);
int ssh_generate_session_keys(ssh_session session);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* KEX_H_ */
diff --git a/include/libssh/keys.h b/include/libssh/keys.h
index 7b138612..1379539e 100644
--- a/include/libssh/keys.h
+++ b/include/libssh/keys.h
@@ -29,34 +29,36 @@ struct ssh_public_key_struct {
int type;
const char *type_c; /* Don't free it ! it is static */
#if defined(HAVE_LIBGCRYPT)
- gcry_sexp_t dsa_pub;
gcry_sexp_t rsa_pub;
#elif defined(HAVE_LIBCRYPTO)
- DSA *dsa_pub;
- RSA *rsa_pub;
+ EVP_PKEY *key_pub;
#elif defined(HAVE_LIBMBEDCRYPTO)
mbedtls_pk_context *rsa_pub;
- void *dsa_pub;
#endif
};
struct ssh_private_key_struct {
int type;
#if defined(HAVE_LIBGCRYPT)
- gcry_sexp_t dsa_priv;
gcry_sexp_t rsa_priv;
#elif defined(HAVE_LIBCRYPTO)
- DSA *dsa_priv;
- RSA *rsa_priv;
+ EVP_PKEY *key_priv;
#elif defined(HAVE_LIBMBEDCRYPTO)
mbedtls_pk_context *rsa_priv;
- void *dsa_priv;
#endif
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
const char *ssh_type_to_char(int type);
int ssh_type_from_name(const char *name);
ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* KEYS_H_ */
diff --git a/include/libssh/knownhosts.h b/include/libssh/knownhosts.h
index 44e434c0..b50018cd 100644
--- a/include/libssh/knownhosts.h
+++ b/include/libssh/knownhosts.h
@@ -22,6 +22,10 @@
#ifndef SSH_KNOWNHOSTS_H_
#define SSH_KNOWNHOSTS_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
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
@@ -29,4 +33,8 @@ ssh_session_get_known_hosts_entry_file(ssh_session session,
const char *filename,
struct ssh_knownhosts_entry **pentry);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* SSH_KNOWNHOSTS_H_ */
diff --git a/include/libssh/legacy.h b/include/libssh/legacy.h
index 911173ee..38bef4da 100644
--- a/include/libssh/legacy.h
+++ b/include/libssh/legacy.h
@@ -31,6 +31,10 @@
typedef struct ssh_private_key_struct* ssh_private_key;
typedef struct ssh_public_key_struct* ssh_public_key;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
LIBSSH_API int ssh_auth_list(ssh_session session);
LIBSSH_API int ssh_userauth_offer_pubkey(ssh_session session, const char *username, int type, ssh_string publickey);
LIBSSH_API int ssh_userauth_pubkey(ssh_session session, const char *username, ssh_string publickey, ssh_private_key privatekey);
@@ -88,19 +92,19 @@ SSH_DEPRECATED LIBSSH_API int channel_select(ssh_channel *readchans, ssh_channel
SSH_DEPRECATED LIBSSH_API void channel_set_blocking(ssh_channel channel, int blocking);
SSH_DEPRECATED LIBSSH_API int channel_write(ssh_channel channel, const void *data, uint32_t len);
-LIBSSH_API void privatekey_free(ssh_private_key prv);
-LIBSSH_API ssh_private_key privatekey_from_file(ssh_session session, const char *filename,
+SSH_DEPRECATED LIBSSH_API void privatekey_free(ssh_private_key prv);
+SSH_DEPRECATED LIBSSH_API ssh_private_key privatekey_from_file(ssh_session session, const char *filename,
int type, const char *passphrase);
-LIBSSH_API void publickey_free(ssh_public_key key);
-LIBSSH_API int ssh_publickey_to_file(ssh_session session, const char *file,
+SSH_DEPRECATED LIBSSH_API void publickey_free(ssh_public_key key);
+SSH_DEPRECATED LIBSSH_API int ssh_publickey_to_file(ssh_session session, const char *file,
ssh_string pubkey, int type);
-LIBSSH_API ssh_string publickey_from_file(ssh_session session, const char *filename,
+SSH_DEPRECATED LIBSSH_API ssh_string publickey_from_file(ssh_session session, const char *filename,
int *type);
-LIBSSH_API ssh_public_key publickey_from_privatekey(ssh_private_key prv);
-LIBSSH_API ssh_string publickey_to_string(ssh_public_key key);
-LIBSSH_API int ssh_try_publickey_from_file(ssh_session session, const char *keyfile,
+SSH_DEPRECATED LIBSSH_API ssh_public_key publickey_from_privatekey(ssh_private_key prv);
+SSH_DEPRECATED LIBSSH_API ssh_string publickey_to_string(ssh_public_key key);
+SSH_DEPRECATED LIBSSH_API int ssh_try_publickey_from_file(ssh_session session, const char *keyfile,
ssh_string *publickey, int *type);
-LIBSSH_API enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey);
+SSH_DEPRECATED LIBSSH_API enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey);
LIBSSH_API ssh_string ssh_get_pubkey(ssh_session session);
@@ -117,4 +121,8 @@ SSH_DEPRECATED LIBSSH_API size_t string_len(ssh_string str);
SSH_DEPRECATED LIBSSH_API ssh_string string_new(size_t size);
SSH_DEPRECATED LIBSSH_API char *string_to_char(ssh_string str);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* LEGACY_H_ */
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index 403c2d22..2f6bdc0a 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -25,13 +25,14 @@
#ifdef HAVE_LIBCRYPTO
-#include <openssl/dsa.h>
+#include "libssh/libssh.h"
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>
+#include <openssl/ec.h>
typedef EVP_MD_CTX* SHACTX;
typedef EVP_MD_CTX* SHA256CTX;
@@ -39,11 +40,6 @@ typedef EVP_MD_CTX* SHA384CTX;
typedef EVP_MD_CTX* SHA512CTX;
typedef EVP_MD_CTX* MD5CTX;
typedef EVP_MD_CTX* HMACCTX;
-#ifdef HAVE_ECC
-typedef EVP_MD_CTX *EVPCTX;
-#else
-typedef void *EVPCTX;
-#endif
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH
@@ -58,8 +54,15 @@ typedef void *EVPCTX;
#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE
#endif
+/* Use ssh_crypto_free() to release memory allocated by bignum_bn2dec(),
+ bignum_bn2hex() and other functions that use crypto-library functions that
+ are documented to allocate memory that needs to be de-allocate with
+ OPENSSL_free. */
+#define ssh_crypto_free(x) OPENSSL_free(x)
+
#include <openssl/bn.h>
#include <openssl/opensslv.h>
+
typedef BIGNUM* bignum;
typedef const BIGNUM* const_bignum;
typedef BN_CTX* bignum_CTX;
@@ -110,10 +113,14 @@ 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)
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+#define ssh_fips_mode() EVP_default_properties_is_fips_enabled(NULL)
#else
#define ssh_fips_mode() false
#endif
+ssh_string pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p);
+int pki_key_ecgroup_name_to_nid(const char *group);
#endif /* HAVE_LIBCRYPTO */
#endif /* LIBCRYPTO_H_ */
diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h
index 347d851b..a8044545 100644
--- a/include/libssh/libgcrypt.h
+++ b/include/libssh/libgcrypt.h
@@ -32,7 +32,6 @@ typedef gcry_md_hd_t SHA384CTX;
typedef gcry_md_hd_t SHA512CTX;
typedef gcry_md_hd_t MD5CTX;
typedef gcry_md_hd_t HMACCTX;
-typedef gcry_md_hd_t EVPCTX;
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define MD5_DIGEST_LEN 16
@@ -49,6 +48,8 @@ typedef gcry_md_hd_t EVPCTX;
#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE
+#define ssh_crypto_free(x) gcry_free(x)
+
typedef gcry_mpi_t bignum;
typedef const struct gcry_mpi *const_bignum;
typedef void* bignum_CTX;
@@ -104,6 +105,10 @@ int ssh_gcry_rand_range(bignum rnd, bignum max);
} while(0)
/* Helper functions for data conversions. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Extract an MPI from the given s-expression SEXP named NAME which is
encoded using INFORMAT and store it in a newly allocated ssh_string
encoded using OUTFORMAT. */
@@ -114,6 +119,10 @@ ssh_string ssh_sexp_extract_mpi(const gcry_sexp_t sexp,
#define ssh_fips_mode() false
+#ifdef __cplusplus
+}
+#endif
+
#endif /* HAVE_LIBGCRYPT */
#endif /* LIBGCRYPT_H_ */
diff --git a/include/libssh/libmbedcrypto.h b/include/libssh/libmbedcrypto.h
index fe53019b..918fe293 100644
--- a/include/libssh/libmbedcrypto.h
+++ b/include/libssh/libmbedcrypto.h
@@ -34,6 +34,7 @@
#include <mbedtls/cipher.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/platform.h>
typedef mbedtls_md_context_t *SHACTX;
typedef mbedtls_md_context_t *SHA256CTX;
@@ -41,7 +42,6 @@ typedef mbedtls_md_context_t *SHA384CTX;
typedef mbedtls_md_context_t *SHA512CTX;
typedef mbedtls_md_context_t *MD5CTX;
typedef mbedtls_md_context_t *HMACCTX;
-typedef mbedtls_md_context_t *EVPCTX;
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
@@ -59,6 +59,8 @@ typedef mbedtls_md_context_t *EVPCTX;
#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE
+#define ssh_crypto_free(x) mbedtls_free(x)
+
typedef mbedtls_mpi *bignum;
typedef const mbedtls_mpi *const_bignum;
typedef void* bignum_CTX;
@@ -73,9 +75,13 @@ struct mbedtls_ecdsa_sig {
bignum s;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
bignum ssh_mbedcry_bn_new(void);
void ssh_mbedcry_bn_free(bignum num);
-unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix);
+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);
@@ -101,7 +107,7 @@ int ssh_mbedcry_hex2bn(bignum *dest, char *data);
} while(0)
#define bignum_bn2dec(num) ssh_mbedcry_bn2num(num, 10)
#define bignum_dec2bn(data, bn) mbedtls_mpi_read_string(bn, 10, data)
-#define bignum_bn2hex(num, dest) (*dest)=ssh_mbedcry_bn2num(num, 16)
+#define bignum_bn2hex(num, dest) (*dest)=(unsigned char *)ssh_mbedcry_bn2num(num, 16)
#define bignum_hex2bn(data, dest) ssh_mbedcry_hex2bn(dest, data)
#define bignum_rand(rnd, bits) ssh_mbedcry_rand((rnd), (bits), 0, 1)
#define bignum_rand_range(rnd, max) ssh_mbedcry_rand_range(rnd, max)
@@ -136,5 +142,9 @@ ssh_string make_ecpoint_string(const mbedtls_ecp_group *g, const
#define ssh_fips_mode() false
+#ifdef __cplusplus
+}
+#endif
+
#endif /* HAVE_LIBMBEDCRYPTO */
#endif /* LIBMBEDCRYPTO_H_ */
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 4bfd81ef..13f1b812 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -1,7 +1,7 @@
/*
* This file is part of the SSH Library
*
- * Copyright (c) 2003-2021 by Aris Adamantiadis and the libssh team
+ * Copyright (c) 2003-2024 by Aris Adamantiadis and the libssh team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -50,18 +50,13 @@
#endif
#include <stdarg.h>
+#include <stdint.h>
+#include <inttypes.h>
#ifdef _MSC_VER
- /* Visual Studio hasn't inttypes.h so it doesn't know uint32_t */
- typedef int int32_t;
- typedef unsigned int uint32_t;
- typedef unsigned short uint16_t;
- typedef unsigned char uint8_t;
- typedef unsigned long long uint64_t;
typedef int mode_t;
#else /* _MSC_VER */
#include <unistd.h>
- #include <inttypes.h>
#include <sys/types.h>
#endif /* _MSC_VER */
@@ -82,7 +77,7 @@
#define PRINTF_ATTRIBUTE(a,b)
#endif /* __GNUC__ */
-#ifdef __GNUC__
+#if !defined(SSH_SUPPRESS_DEPRECATED) && defined(__GNUC__)
#define SSH_DEPRECATED __attribute__ ((deprecated))
#else
#define SSH_DEPRECATED
@@ -196,7 +191,8 @@ enum ssh_global_requests_e {
SSH_GLOBAL_REQUEST_UNKNOWN=0,
SSH_GLOBAL_REQUEST_TCPIP_FORWARD,
SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD,
- SSH_GLOBAL_REQUEST_KEEPALIVE
+ SSH_GLOBAL_REQUEST_KEEPALIVE,
+ SSH_GLOBAL_REQUEST_NO_MORE_SESSIONS
};
enum ssh_publickey_state_e {
@@ -277,12 +273,12 @@ enum ssh_error_types_e {
/* some types for keys */
enum ssh_keytypes_e{
SSH_KEYTYPE_UNKNOWN=0,
- SSH_KEYTYPE_DSS=1,
+ SSH_KEYTYPE_DSS=1, /* deprecated */
SSH_KEYTYPE_RSA,
SSH_KEYTYPE_RSA1,
SSH_KEYTYPE_ECDSA, /* deprecated */
SSH_KEYTYPE_ED25519,
- SSH_KEYTYPE_DSS_CERT01,
+ SSH_KEYTYPE_DSS_CERT01, /* deprecated */
SSH_KEYTYPE_RSA_CERT01,
SSH_KEYTYPE_ECDSA_P256,
SSH_KEYTYPE_ECDSA_P384,
@@ -299,7 +295,8 @@ enum ssh_keytypes_e{
enum ssh_keycmp_e {
SSH_KEY_CMP_PUBLIC = 0,
- SSH_KEY_CMP_PRIVATE
+ SSH_KEY_CMP_PRIVATE = 1,
+ SSH_KEY_CMP_CERTIFICATE = 2,
};
#define SSH_ADDRSTRLEN 46
@@ -328,16 +325,16 @@ enum {
/** No logging at all
*/
SSH_LOG_NOLOG=0,
- /** Only warnings
+ /** Only unrecoverable errors
*/
SSH_LOG_WARNING,
- /** High level protocol information
+ /** Information for the users
*/
SSH_LOG_PROTOCOL,
- /** Lower level protocol infomations, packet level
+ /** Debug information, to see what is going on
*/
SSH_LOG_PACKET,
- /** Every function path
+ /** Trace information and recoverable error messages
*/
SSH_LOG_FUNCTIONS
};
@@ -353,17 +350,25 @@ enum {
/** No logging at all */
#define SSH_LOG_NONE 0
-/** Show only warnings */
+/** Show only fatal warnings */
#define SSH_LOG_WARN 1
/** Get some information what's going on */
#define SSH_LOG_INFO 2
-/** Get detailed debuging information **/
+/** Get detailed debugging information **/
#define SSH_LOG_DEBUG 3
/** Get trace output, packet information, ... */
#define SSH_LOG_TRACE 4
/** @} */
+enum ssh_control_master_options_e {
+ SSH_CONTROL_MASTER_NO,
+ SSH_CONTROL_MASTER_AUTO,
+ SSH_CONTROL_MASTER_YES,
+ SSH_CONTROL_MASTER_ASK,
+ SSH_CONTROL_MASTER_AUTOASK
+};
+
enum ssh_options_e {
SSH_OPTIONS_HOST,
SSH_OPTIONS_PORT,
@@ -406,6 +411,12 @@ enum ssh_options_e {
SSH_OPTIONS_PROCESS_CONFIG,
SSH_OPTIONS_REKEY_DATA,
SSH_OPTIONS_REKEY_TIME,
+ SSH_OPTIONS_RSA_MIN_SIZE,
+ SSH_OPTIONS_IDENTITY_AGENT,
+ SSH_OPTIONS_IDENTITIES_ONLY,
+ SSH_OPTIONS_CONTROL_MASTER,
+ SSH_OPTIONS_CONTROL_PATH,
+ SSH_OPTIONS_CERTIFICATE,
};
enum {
@@ -468,6 +479,8 @@ LIBSSH_API int ssh_channel_request_exec(ssh_channel channel, const char *cmd);
LIBSSH_API int ssh_channel_request_pty(ssh_channel channel);
LIBSSH_API int ssh_channel_request_pty_size(ssh_channel channel, const char *term,
int cols, int rows);
+LIBSSH_API int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *term,
+ int cols, int rows, const unsigned char* modes, size_t modes_len);
LIBSSH_API int ssh_channel_request_shell(ssh_channel channel);
LIBSSH_API int ssh_channel_request_send_signal(ssh_channel channel, const char *signum);
LIBSSH_API int ssh_channel_request_send_break(ssh_channel channel, uint32_t length);
@@ -477,8 +490,6 @@ LIBSSH_API int ssh_channel_request_x11(ssh_channel channel, int single_connectio
const char *cookie, int screen_number);
LIBSSH_API int ssh_channel_request_auth_agent(ssh_channel channel);
LIBSSH_API int ssh_channel_send_eof(ssh_channel channel);
-LIBSSH_API int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct
- timeval * timeout);
LIBSSH_API void ssh_channel_set_blocking(ssh_channel channel, int blocking);
LIBSSH_API void ssh_channel_set_counter(ssh_channel channel,
ssh_counter counter);
@@ -509,7 +520,12 @@ LIBSSH_API char *ssh_dirname (const char *path);
LIBSSH_API int ssh_finalize(void);
/* REVERSE PORT FORWARDING */
-LIBSSH_API ssh_channel ssh_channel_accept_forward(ssh_session session,
+LIBSSH_API ssh_channel ssh_channel_open_forward_port(ssh_session session,
+ int timeout_ms,
+ int *destination_port,
+ char **originator,
+ int *originator_port);
+SSH_DEPRECATED LIBSSH_API ssh_channel ssh_channel_accept_forward(ssh_session session,
int timeout_ms,
int *destination_port);
LIBSSH_API int ssh_channel_cancel_forward(ssh_session session,
@@ -528,6 +544,7 @@ LIBSSH_API socket_t ssh_get_fd(ssh_session session);
LIBSSH_API char *ssh_get_hexa(const unsigned char *what, size_t len);
LIBSSH_API char *ssh_get_issue_banner(ssh_session session);
LIBSSH_API int ssh_get_openssh_version(ssh_session session);
+LIBSSH_API int ssh_request_no_more_sessions(ssh_session session);
LIBSSH_API int ssh_get_server_publickey(ssh_session session, ssh_key *key);
@@ -551,7 +568,27 @@ SSH_DEPRECATED LIBSSH_API int ssh_write_knownhost(ssh_session session);
SSH_DEPRECATED LIBSSH_API char *ssh_dump_knownhost(ssh_session session);
SSH_DEPRECATED LIBSSH_API int ssh_is_server_known(ssh_session session);
SSH_DEPRECATED LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len);
+SSH_DEPRECATED LIBSSH_API int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct
+ timeval * timeout);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_accept_request(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_close(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_deny_request(ssh_scp scp, const char *reason);
+SSH_DEPRECATED LIBSSH_API void ssh_scp_free(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_init(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_leave_directory(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_pull_request(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int perms);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int perms);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_read(ssh_scp scp, void *buffer, size_t size);
+SSH_DEPRECATED LIBSSH_API const char *ssh_scp_request_get_filename(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_request_get_permissions(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API size_t ssh_scp_request_get_size(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API uint64_t ssh_scp_request_get_size64(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API const char *ssh_scp_request_get_warning(ssh_scp scp);
+SSH_DEPRECATED LIBSSH_API int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len);
LIBSSH_API int ssh_get_random(void *where,int len,int strong);
@@ -651,6 +688,12 @@ typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len,
/** @} */
+enum ssh_file_format_e {
+ SSH_FILE_FORMAT_DEFAULT = 0,
+ SSH_FILE_FORMAT_OPENSSH,
+ SSH_FILE_FORMAT_PEM,
+};
+
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)
@@ -663,6 +706,7 @@ LIBSSH_API int ssh_key_is_private(const ssh_key k);
LIBSSH_API int ssh_key_cmp(const ssh_key k1,
const ssh_key k2,
enum ssh_keycmp_e what);
+LIBSSH_API ssh_key ssh_key_dup(const ssh_key key);
LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
ssh_key *pkey);
@@ -676,6 +720,13 @@ LIBSSH_API int ssh_pki_export_privkey_base64(const ssh_key privkey,
ssh_auth_callback auth_fn,
void *auth_data,
char **b64_key);
+LIBSSH_API int
+ssh_pki_export_privkey_base64_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ char **b64_key,
+ enum ssh_file_format_e format);
LIBSSH_API int ssh_pki_import_privkey_file(const char *filename,
const char *passphrase,
ssh_auth_callback auth_fn,
@@ -686,6 +737,13 @@ LIBSSH_API int ssh_pki_export_privkey_file(const ssh_key privkey,
ssh_auth_callback auth_fn,
void *auth_data,
const char *filename);
+LIBSSH_API int
+ssh_pki_export_privkey_file_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename,
+ enum ssh_file_format_e format);
LIBSSH_API int ssh_pki_copy_cert_to_privkey(const ssh_key cert_key,
ssh_key privkey);
@@ -718,24 +776,6 @@ LIBSSH_API void ssh_print_hash(enum ssh_publickey_hash_type type, unsigned char
LIBSSH_API int ssh_send_ignore (ssh_session session, const char *data);
LIBSSH_API int ssh_send_debug (ssh_session session, const char *message, int always_display);
LIBSSH_API void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds);
-LIBSSH_API int ssh_scp_accept_request(ssh_scp scp);
-LIBSSH_API int ssh_scp_close(ssh_scp scp);
-LIBSSH_API int ssh_scp_deny_request(ssh_scp scp, const char *reason);
-LIBSSH_API void ssh_scp_free(ssh_scp scp);
-LIBSSH_API int ssh_scp_init(ssh_scp scp);
-LIBSSH_API int ssh_scp_leave_directory(ssh_scp scp);
-LIBSSH_API ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location);
-LIBSSH_API int ssh_scp_pull_request(ssh_scp scp);
-LIBSSH_API int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode);
-LIBSSH_API int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int perms);
-LIBSSH_API int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int perms);
-LIBSSH_API int ssh_scp_read(ssh_scp scp, void *buffer, size_t size);
-LIBSSH_API const char *ssh_scp_request_get_filename(ssh_scp scp);
-LIBSSH_API int ssh_scp_request_get_permissions(ssh_scp scp);
-LIBSSH_API size_t ssh_scp_request_get_size(ssh_scp scp);
-LIBSSH_API uint64_t ssh_scp_request_get_size64(ssh_scp scp);
-LIBSSH_API const char *ssh_scp_request_get_warning(ssh_scp scp);
-LIBSSH_API int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len);
LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd,
fd_set *readfds, struct timeval *timeout);
LIBSSH_API int ssh_service_request(ssh_session session, const char *service);
@@ -759,10 +799,8 @@ LIBSSH_API int ssh_userauth_try_publickey(ssh_session session,
LIBSSH_API int ssh_userauth_publickey(ssh_session session,
const char *username,
const ssh_key privkey);
-#ifndef _WIN32
LIBSSH_API int ssh_userauth_agent(ssh_session session,
const char *username);
-#endif
LIBSSH_API int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
char** value);
LIBSSH_API int ssh_userauth_publickey_auto(ssh_session session,
@@ -833,6 +871,7 @@ LIBSSH_API int ssh_buffer_add_data(ssh_buffer buffer, const void *data, uint32_t
LIBSSH_API uint32_t ssh_buffer_get_data(ssh_buffer buffer, void *data, uint32_t requestedlen);
LIBSSH_API void *ssh_buffer_get(ssh_buffer buffer);
LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer);
+LIBSSH_API int ssh_session_set_disconnect_message(ssh_session session, const char *message);
#ifndef LIBSSH_LEGACY_0_4
#include "libssh/legacy.h"
diff --git a/include/libssh/libsshpp.hpp b/include/libssh/libsshpp.hpp
index f792465c..e0f21e85 100644
--- a/include/libssh/libsshpp.hpp
+++ b/include/libssh/libsshpp.hpp
@@ -523,7 +523,7 @@ public:
return ssh_channel_is_open(channel) != 0;
}
int openForward(const char *remotehost, int remoteport,
- const char *sourcehost=NULL, int localport=0){
+ const char *sourcehost, int localport=0){
int err=ssh_channel_open_forward(channel,remotehost,remoteport,
sourcehost, localport);
ssh_throw(err);
@@ -587,9 +587,12 @@ public:
ssh_throw(err);
return_throwable;
}
- void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0){
+ void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0,
+ const unsigned char* modes=NULL, size_t modes_len=0){
int err;
- if(term != NULL && cols != 0 && rows != 0)
+ if(term != NULL && cols != 0 && rows != 0 && modes != NULL)
+ err=ssh_channel_request_pty_size_modes(channel,term,cols,rows,modes,modes_len);
+ else if(term != NULL && cols != 0 && rows != 0)
err=ssh_channel_request_pty_size(channel,term,cols,rows);
else
err=ssh_channel_request_pty(channel);
@@ -630,8 +633,8 @@ public:
* @param is_stderr write should be done on the stderr channel (server only)
* @returns number of bytes written
* @throws SshException in case of error
- * @see channel_write
- * @see channel_write_stderr
+ * @see ssh_channel_write
+ * @see ssh_channel_write_stderr
*/
int write(const void *data, size_t len, bool is_stderr=false){
int ret;
@@ -669,7 +672,7 @@ private:
inline Channel *Session::acceptForward(int timeout_ms){
ssh_channel forward =
- ssh_channel_accept_forward(c_session, timeout_ms, NULL);
+ ssh_channel_open_forward_port(c_session, timeout_ms, NULL, NULL, NULL);
ssh_throw_null(c_session,forward);
Channel *newchan = new Channel(*this,forward);
return newchan;
diff --git a/include/libssh/messages.h b/include/libssh/messages.h
index 04d041d4..160306cc 100644
--- a/include/libssh/messages.h
+++ b/include/libssh/messages.h
@@ -28,6 +28,7 @@ struct ssh_auth_request {
int method;
char *password;
struct ssh_key_struct *pubkey;
+ char *sigtype;
enum ssh_publickey_state_e signature_state;
char kbdint_response;
};
@@ -91,6 +92,10 @@ struct ssh_message_struct {
struct ssh_global_request global_request;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
SSH_PACKET_CALLBACK(ssh_packet_channel_open);
SSH_PACKET_CALLBACK(ssh_packet_global_request);
@@ -103,4 +108,8 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel,
const char *request, uint8_t want_reply);
ssh_message ssh_message_pop_head(ssh_session session);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* MESSAGES_H_ */
diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index 5044817a..fc8596f7 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -21,6 +21,25 @@
#ifndef MISC_H_
#define MISC_H_
+#ifdef _WIN32
+
+# ifdef _MSC_VER
+# ifndef _SSIZE_T_DEFINED
+# undef ssize_t
+# include <BaseTsd.h>
+ typedef _W64 SSIZE_T ssize_t;
+# define _SSIZE_T_DEFINED
+# endif /* _SSIZE_T_DEFINED */
+# endif /* _MSC_VER */
+
+#else
+# include <sys/types.h>
+#endif /* _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* in misc.c */
/* gets the user home dir. */
char *ssh_get_user_home_dir(void);
@@ -75,13 +94,13 @@ const void *_ssh_list_pop_head(struct ssh_list *list);
/** @brief fetch the head element of a list and remove it from list
* @param type type of the element to return
- * @param list the ssh_list to use
+ * @param ssh_list the ssh_list to use
* @return the first element of the list, or NULL if the list is empty
*/
#define ssh_list_pop_head(type, ssh_list)\
((type)_ssh_list_pop_head(ssh_list))
-int ssh_make_milliseconds(long sec, long usec);
+int ssh_make_milliseconds(unsigned long sec, unsigned long usec);
void ssh_timestamp_init(struct ssh_timestamp *ts);
int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout);
int ssh_timeout_update(struct ssh_timestamp *ts, int timeout);
@@ -96,7 +115,18 @@ 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);
-int ssh_tmpname(char *template);
+int ssh_tmpname(char *name);
char *ssh_strreplace(const char *src, const char *pattern, const char *repl);
+
+ssize_t ssh_readn(int fd, void *buf, size_t nbytes);
+ssize_t ssh_writen(int fd, const void *buf, size_t nbytes);
+
+int ssh_check_hostname_syntax(const char *hostname);
+int ssh_check_username_syntax(const char *username);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* MISC_H_ */
diff --git a/include/libssh/options.h b/include/libssh/options.h
index e8dc6c69..9050d3be 100644
--- a/include/libssh/options.h
+++ b/include/libssh/options.h
@@ -21,11 +21,20 @@
#ifndef _OPTIONS_H
#define _OPTIONS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int ssh_config_parse_file(ssh_session session, const char *filename);
int ssh_config_parse_string(ssh_session session, const char *input);
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
- const char *list);
+ const char *list,
+ char **place);
int ssh_options_apply(ssh_session session);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _OPTIONS_H */
diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index 561bba8e..f0c8cb20 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -51,6 +51,10 @@ enum ssh_packet_filter_result_e {
int ssh_packet_send(ssh_session session);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
SSH_PACKET_CALLBACK(ssh_packet_unimplemented);
SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback);
SSH_PACKET_CALLBACK(ssh_packet_ignore_callback);
@@ -63,11 +67,12 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info);
SSH_PACKET_CALLBACK(ssh_packet_kexdh_init);
#endif
+int ssh_packet_send_newkeys(ssh_session session);
int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum);
int ssh_packet_parse_type(ssh_session session);
//int packet_flush(ssh_session session, int enforce_blocking);
-int ssh_packet_socket_callback(const void *data, size_t len, void *user);
+size_t ssh_packet_socket_callback(const void *data, size_t len, void *user);
void ssh_packet_register_socket_callback(ssh_session session, struct ssh_socket_struct *s);
void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks);
void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks);
@@ -80,7 +85,7 @@ int ssh_packet_decrypt(ssh_session session, uint8_t *destination, uint8_t *sourc
size_t start, size_t encrypted_size);
unsigned char *ssh_packet_encrypt(ssh_session session,
void *packet,
- uint32_t len);
+ size_t len);
int ssh_packet_hmac_verify(ssh_session session, const void *data, size_t len,
unsigned char *mac, enum ssh_hmac_e type);
int ssh_packet_set_newkeys(ssh_session session,
@@ -88,4 +93,8 @@ int ssh_packet_set_newkeys(ssh_session session,
struct ssh_crypto_struct *ssh_packet_get_current_crypto(ssh_session session,
enum ssh_crypto_direction_e direction);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* PACKET_H_ */
diff --git a/include/libssh/pcap.h b/include/libssh/pcap.h
index 2e43ae87..2a6c3c27 100644
--- a/include/libssh/pcap.h
+++ b/include/libssh/pcap.h
@@ -27,6 +27,10 @@
#ifdef WITH_PCAP
typedef struct ssh_pcap_context_struct* ssh_pcap_context;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len);
ssh_pcap_context ssh_pcap_context_new(ssh_session session);
@@ -41,5 +45,9 @@ int ssh_pcap_context_write(ssh_pcap_context,enum ssh_pcap_direction direction, v
uint32_t len, uint32_t origlen);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* WITH_PCAP */
#endif /* PCAP_H_ */
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 6357b0b6..efb9bdbf 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -33,8 +33,8 @@
#include <openssl/evp.h>
#endif
#include "libssh/crypto.h"
-#ifdef HAVE_OPENSSL_ED25519
-/* If using OpenSSL implementation, define the signature lenght which would be
+#ifdef HAVE_LIBCRYPTO
+/* If using OpenSSL implementation, define the signature length which would be
* defined in libssh/ed25519.h otherwise */
#define ED25519_SIG_LEN 64
#else
@@ -57,32 +57,24 @@ struct ssh_key_struct {
const char *type_c; /* Don't free it ! it is static */
int ecdsa_nid;
#if defined(HAVE_LIBGCRYPT)
- gcry_sexp_t dsa;
gcry_sexp_t rsa;
gcry_sexp_t ecdsa;
#elif defined(HAVE_LIBMBEDCRYPTO)
mbedtls_pk_context *rsa;
mbedtls_ecdsa_context *ecdsa;
- void *dsa;
#elif defined(HAVE_LIBCRYPTO)
- DSA *dsa;
- RSA *rsa;
- EVP_PKEY *key; /* Saving the OpenSSL context here to save time while converting*/
-# if defined(HAVE_OPENSSL_ECC)
- EC_KEY *ecdsa;
-# else
- void *ecdsa;
-# endif /* HAVE_OPENSSL_EC_H */
-#endif /* HAVE_LIBGCRYPT */
-#ifdef HAVE_OPENSSL_ED25519
+ /* This holds either ENGINE key for PKCS#11 support or just key in
+ * high-level format */
+ EVP_PKEY *key;
uint8_t *ed25519_pubkey;
uint8_t *ed25519_privkey;
-#else
+#endif /* HAVE_LIBGCRYPT */
+#ifndef HAVE_LIBCRYPTO
ed25519_pubkey *ed25519_pubkey;
ed25519_privkey *ed25519_privkey;
-#endif
+#endif /* HAVE_LIBCRYPTO */
ssh_string sk_application;
- void *cert;
+ ssh_buffer cert;
enum ssh_keytypes_e cert_type;
};
@@ -91,16 +83,15 @@ struct ssh_signature_struct {
enum ssh_digest_e hash_type;
const char *type_c;
#if defined(HAVE_LIBGCRYPT)
- gcry_sexp_t dsa_sig;
gcry_sexp_t rsa_sig;
gcry_sexp_t ecdsa_sig;
#elif defined(HAVE_LIBMBEDCRYPTO)
ssh_string rsa_sig;
struct mbedtls_ecdsa_sig ecdsa_sig;
#endif /* HAVE_LIBGCRYPT */
-#ifndef HAVE_OPENSSL_ED25519
+#ifndef HAVE_LIBCRYPTO
ed25519_signature *ed25519_sig;
-#endif
+#endif /* HAVE_LIBGCRYPT */
ssh_string raw_sig;
/* Security Key specific additions */
@@ -110,8 +101,11 @@ struct ssh_signature_struct {
typedef struct ssh_signature_struct *ssh_signature;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* SSH Key Functions */
-ssh_key ssh_key_dup(const ssh_key key);
void ssh_key_clean (ssh_key key);
const char *
@@ -127,10 +121,11 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name);
((t) >= SSH_KEYTYPE_ECDSA_P256 && (t) <= SSH_KEYTYPE_ECDSA_P521)
#define is_cert_type(kt)\
- ((kt) == SSH_KEYTYPE_DSS_CERT01 ||\
- (kt) == SSH_KEYTYPE_RSA_CERT01 ||\
- ((kt) >= SSH_KEYTYPE_ECDSA_P256_CERT01 &&\
- (kt) <= SSH_KEYTYPE_ED25519_CERT01))
+ ((kt) == SSH_KEYTYPE_RSA_CERT01 ||\
+ (kt) == SSH_KEYTYPE_SK_ECDSA_CERT01 ||\
+ (kt) == SSH_KEYTYPE_SK_ED25519_CERT01 ||\
+ ((kt) >= SSH_KEYTYPE_ECDSA_P256_CERT01 &&\
+ (kt) <= SSH_KEYTYPE_ED25519_CERT01))
/* SSH Signature Functions */
ssh_signature ssh_signature_new(void);
@@ -158,6 +153,10 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
int ssh_pki_import_cert_blob(const ssh_string cert_blob,
ssh_key *pkey);
+/* SSH Private Key Functions */
+int ssh_pki_export_privkey_blob(const ssh_key key,
+ ssh_string *pblob);
+
/* SSH Signing Functions */
ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf,
@@ -174,9 +173,19 @@ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key);
ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key);
int ssh_key_algorithm_allowed(ssh_session session, const char *type);
+bool ssh_key_size_allowed(ssh_session session, ssh_key key);
+
+/* Return the key size in bits */
+int ssh_key_size(ssh_key key);
/* PKCS11 URI function to check if filename is a path or a PKCS11 URI */
+#ifdef WITH_PKCS11_URI
bool ssh_pki_is_uri(const char *filename);
char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri);
+#endif /* WITH_PKCS11_URI */
+
+#ifdef __cplusplus
+}
+#endif
#endif /* PKI_H_ */
diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index 71418fdc..2061ebd7 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -23,6 +23,10 @@
#include "libssh/pki.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* defined in bcrypt_pbkdf.c */
int bcrypt_pbkdf(const char *pass,
size_t passlen,
@@ -34,8 +38,6 @@ int bcrypt_pbkdf(const char *pass,
#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----"
-#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
-#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----"
#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----"
#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----"
#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
@@ -49,6 +51,8 @@ enum ssh_key_e {
SSH_KEY_PRIVATE
};
+void pki_key_clean(ssh_key key);
+
int pki_key_ecdsa_nid_from_name(const char *name);
const char *pki_key_ecdsa_nid_to_name(int nid);
const char *ssh_key_signature_to_char(enum ssh_keytypes_e type,
@@ -59,7 +63,6 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
/* SSH Key Functions */
ssh_key pki_key_dup(const ssh_key key, int demote);
int pki_key_generate_rsa(ssh_key key, int parameter);
-int pki_key_generate_dss(ssh_key key, int parameter);
int pki_key_generate_ecdsa(ssh_key key, int parameter);
int pki_key_generate_ed25519(ssh_key key);
@@ -85,24 +88,13 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
ssh_key *pkey);
/* SSH Public Key Functions */
-int pki_pubkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey);
int pki_pubkey_build_rsa(ssh_key key,
ssh_string e,
ssh_string n);
int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e);
-ssh_string pki_publickey_to_blob(const ssh_key key);
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type);
/* SSH Private Key Functions */
-int pki_privkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey,
- ssh_string privkey);
int pki_privkey_build_rsa(ssh_key key,
ssh_string n,
ssh_string e,
@@ -114,7 +106,6 @@ int pki_privkey_build_ecdsa(ssh_key key,
int nid,
ssh_string e,
ssh_string exp);
-ssh_string pki_publickey_to_blob(const ssh_key key);
/* SSH Signature Functions */
ssh_signature pki_sign_data(const ssh_key privkey,
@@ -140,15 +131,18 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
const unsigned char *hash,
size_t hlen,
enum ssh_digest_e hash_type);
+#ifndef HAVE_LIBCRYPTO
int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig,
const unsigned char *hash, size_t hlen);
int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig,
const unsigned char *hash, size_t hlen);
+#endif /* HAVE_LIBCRYPTO */
int pki_ed25519_key_cmp(const ssh_key k1,
const ssh_key k2,
enum ssh_keycmp_e what);
-int pki_ed25519_key_dup(ssh_key new, const ssh_key key);
+int pki_ed25519_key_dup(ssh_key new_key, const ssh_key key);
int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key);
+int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey);
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,
@@ -162,7 +156,14 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
const char *passphrase, ssh_auth_callback auth_fn, void *auth_data);
+#ifdef WITH_PKCS11_URI
/* URI Function */
int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type);
+#endif /* WITH_PKCS11_URI */
+
+bool ssh_key_size_allowed_rsa(int min_size, ssh_key key);
+#ifdef __cplusplus
+}
+#endif
#endif /* PKI_PRIV_H_ */
diff --git a/include/libssh/poll.h b/include/libssh/poll.h
index 3aa9a49b..8e30676e 100644
--- a/include/libssh/poll.h
+++ b/include/libssh/poll.h
@@ -114,6 +114,10 @@ typedef unsigned long int nfds_t;
#endif /* WIN32 */
#endif /* HAVE_POLL */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void ssh_poll_init(void);
void ssh_poll_cleanup(void);
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout);
@@ -158,4 +162,8 @@ ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session);
int ssh_event_add_poll(ssh_event event, ssh_poll_handle p);
void ssh_event_remove_poll(ssh_event event, ssh_poll_handle p);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* POLL_H_ */
diff --git a/include/libssh/poly1305.h b/include/libssh/poly1305.h
index 513f1b99..a22fea87 100644
--- a/include/libssh/poly1305.h
+++ b/include/libssh/poly1305.h
@@ -7,6 +7,10 @@
#define POLY1305_H
#include "libssh/chacha20-poly1305-common.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void poly1305_auth(uint8_t out[POLY1305_TAGLEN], const uint8_t *m, size_t inlen,
const uint8_t key[POLY1305_KEYLEN])
#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
@@ -16,4 +20,8 @@ void poly1305_auth(uint8_t out[POLY1305_TAGLEN], const uint8_t *m, size_t inlen,
#endif
;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* POLY1305_H */
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index b4729bb8..bfef771d 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -47,6 +47,14 @@
# endif
#endif /* !defined(HAVE_STRTOULL) */
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#if !defined(HAVE_STRNDUP)
char *strndup(const char *s, size_t n);
#endif /* ! HAVE_STRNDUP */
@@ -152,10 +160,26 @@ char *strndup(const char *s, size_t n);
# endif /* _MSC_VER */
struct timeval;
-int gettimeofday(struct timeval *__p, void *__t);
+int ssh_gettimeofday(struct timeval *__p, void *__t);
+
+#define gettimeofday ssh_gettimeofday
#define _XCLOSESOCKET closesocket
+# ifdef HAVE_IO_H
+# include <io.h>
+# undef open
+# define open _open
+# undef close
+# define close _close
+# undef read
+# define read _read
+# undef write
+# define write _write
+# undef unlink
+# define unlink _unlink
+# endif /* HAVE_IO_H */
+
#else /* _WIN32 */
#include <unistd.h>
@@ -284,6 +308,7 @@ int ssh_auth_reply_success(ssh_session session, int partial);
/* client.c */
int ssh_send_banner(ssh_session session, int is_server);
+void ssh_session_socket_close(ssh_session session);
/* connect.c */
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
@@ -299,7 +324,7 @@ int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen);
/* match.c */
int match_pattern_list(const char *string, const char *pattern,
- unsigned int len, int dolower);
+ size_t len, int dolower);
int match_hostname(const char *host, const char *pattern, unsigned int len);
/* connector.c */
@@ -429,4 +454,15 @@ void ssh_agent_state_free(void *data);
bool is_ssh_initialized(void);
+#define SSH_ERRNO_MSG_MAX 1024
+char *ssh_strerror(int err_num, char *buf, size_t buflen);
+
+/** 55 defined options (5 bytes each) + terminator */
+#define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
+int encode_current_tty_opts(unsigned char *buf, size_t buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _LIBSSH_PRIV_H */
diff --git a/include/libssh/sc25519.h b/include/libssh/sc25519.h
index 5a2c1b85..43b09a05 100644
--- a/include/libssh/sc25519.h
+++ b/include/libssh/sc25519.h
@@ -35,6 +35,10 @@ typedef struct {
uint32_t v[16];
} shortsc25519;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]);
void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]);
@@ -71,4 +75,8 @@ void sc25519_window5(signed char r[51], const sc25519 *s);
void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/include/libssh/scp.h b/include/libssh/scp.h
index d356d89b..089fcfc9 100644
--- a/include/libssh/scp.h
+++ b/include/libssh/scp.h
@@ -47,9 +47,17 @@ struct ssh_scp_struct {
int request_mode;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len);
int ssh_scp_integer_mode(const char *mode);
char *ssh_scp_string_mode(int mode);
int ssh_scp_response(ssh_scp scp, char **response);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/include/libssh/server.h b/include/libssh/server.h
index 1c18b38c..885ef576 100644
--- a/include/libssh/server.h
+++ b/include/libssh/server.h
@@ -40,12 +40,12 @@ enum ssh_bind_options_e {
SSH_BIND_OPTIONS_BINDPORT,
SSH_BIND_OPTIONS_BINDPORT_STR,
SSH_BIND_OPTIONS_HOSTKEY,
- SSH_BIND_OPTIONS_DSAKEY,
- SSH_BIND_OPTIONS_RSAKEY,
+ SSH_BIND_OPTIONS_DSAKEY, /* deprecated */
+ SSH_BIND_OPTIONS_RSAKEY, /* deprecated */
SSH_BIND_OPTIONS_BANNER,
SSH_BIND_OPTIONS_LOG_VERBOSITY,
SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
- SSH_BIND_OPTIONS_ECDSAKEY,
+ SSH_BIND_OPTIONS_ECDSAKEY, /* deprecated */
SSH_BIND_OPTIONS_IMPORT_KEY,
SSH_BIND_OPTIONS_KEY_EXCHANGE,
SSH_BIND_OPTIONS_CIPHERS_C_S,
@@ -57,6 +57,7 @@ enum ssh_bind_options_e {
SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS,
SSH_BIND_OPTIONS_PROCESS_CONFIG,
SSH_BIND_OPTIONS_MODULI,
+ SSH_BIND_OPTIONS_RSA_MIN_SIZE,
};
typedef struct ssh_bind_struct* ssh_bind;
@@ -116,7 +117,7 @@ LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o);
*
* @param[in] userdata A pointer to private data to pass to the callbacks.
*
- * @return SSH_OK on success, SSH_ERROR if an error occured.
+ * @return SSH_OK on success, SSH_ERROR if an error occurred.
*
* @code
* struct ssh_callbacks_struct cb = {
@@ -221,6 +222,9 @@ LIBSSH_API int ssh_server_init_kex(ssh_session session);
/**
* @brief Free a ssh servers bind.
*
+ * Note that this will also free options that have been set on the bind,
+ * including keys set with SSH_BIND_OPTIONS_IMPORT_KEY.
+ *
* @param ssh_bind_o The ssh server bind to free.
*/
LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o);
@@ -244,6 +248,18 @@ LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o);
*/
LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
+/**
+ * @brief Send the server's issue-banner to client.
+ *
+ *
+ * @param[in] session The server session.
+ *
+ * @param[in] banner The server's banner.
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+LIBSSH_API int ssh_send_issue_banner(ssh_session session, const ssh_string banner);
+
/**********************************************************
* SERVER MESSAGING
**********************************************************/
@@ -267,7 +283,7 @@ LIBSSH_API int ssh_message_reply_default(ssh_message msg);
*
* @param[in] msg The message to get the username from.
*
- * @return The username or NULL if an error occured.
+ * @return The username or NULL if an error occurred.
*
* @see ssh_message_get()
* @see ssh_message_type()
@@ -279,12 +295,14 @@ LIBSSH_API const char *ssh_message_auth_user(ssh_message msg);
*
* @param[in] msg The message to get the password from.
*
- * @return The username or NULL if an error occured.
+ * @return The password or NULL if an error occurred.
*
* @see ssh_message_get()
* @see ssh_message_type()
+ * @deprecated This function should not be used anymore as there is a
+ * callback based server implementation now auth_password_function.
*/
-LIBSSH_API const char *ssh_message_auth_password(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API const char *ssh_message_auth_password(ssh_message msg);
/**
* @brief Get the publickey of the authenticated user.
@@ -299,11 +317,21 @@ LIBSSH_API const char *ssh_message_auth_password(ssh_message msg);
* @see ssh_key_cmp()
* @see ssh_message_get()
* @see ssh_message_type()
+ * @deprecated This function should not be used anymore as there is a
+ * callback based server implementation auth_pubkey_function.
*/
-LIBSSH_API ssh_key ssh_message_auth_pubkey(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API ssh_key ssh_message_auth_pubkey(ssh_message msg);
LIBSSH_API int ssh_message_auth_kbdint_is_response(ssh_message msg);
-LIBSSH_API enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg);
+
+/**
+ * @param[in] msg The message to get the public key state from.
+ *
+ * @deprecated This function should not be used anymore as there is a
+ * callback based server implementation auth_pubkey_function
+ */
+SSH_DEPRECATED LIBSSH_API enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg);
+
LIBSSH_API int ssh_message_auth_reply_success(ssh_message msg,int partial);
LIBSSH_API int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey);
LIBSSH_API int ssh_message_auth_reply_pk_ok_simple(ssh_message msg);
@@ -332,11 +360,12 @@ LIBSSH_API int ssh_message_channel_request_open_destination_port(ssh_message msg
LIBSSH_API ssh_channel ssh_message_channel_request_channel(ssh_message msg);
-LIBSSH_API const char *ssh_message_channel_request_pty_term(ssh_message msg);
-LIBSSH_API int ssh_message_channel_request_pty_width(ssh_message msg);
-LIBSSH_API int ssh_message_channel_request_pty_height(ssh_message msg);
-LIBSSH_API int ssh_message_channel_request_pty_pxwidth(ssh_message msg);
-LIBSSH_API int ssh_message_channel_request_pty_pxheight(ssh_message msg);
+/* Replaced by callback based server implementation function channel_pty_request_function*/
+SSH_DEPRECATED LIBSSH_API const char *ssh_message_channel_request_pty_term(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API int ssh_message_channel_request_pty_width(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API int ssh_message_channel_request_pty_height(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API int ssh_message_channel_request_pty_pxwidth(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API int ssh_message_channel_request_pty_pxheight(ssh_message msg);
LIBSSH_API const char *ssh_message_channel_request_env_name(ssh_message msg);
LIBSSH_API const char *ssh_message_channel_request_env_value(ssh_message msg);
@@ -345,17 +374,18 @@ LIBSSH_API const char *ssh_message_channel_request_command(ssh_message msg);
LIBSSH_API const char *ssh_message_channel_request_subsystem(ssh_message msg);
-LIBSSH_API int ssh_message_channel_request_x11_single_connection(ssh_message msg);
-LIBSSH_API const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg);
-LIBSSH_API const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg);
-LIBSSH_API int ssh_message_channel_request_x11_screen_number(ssh_message msg);
+/* Replaced by callback based server implementation function channel_open_request_x11_function*/
+SSH_DEPRECATED LIBSSH_API int ssh_message_channel_request_x11_single_connection(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg);
+SSH_DEPRECATED LIBSSH_API int ssh_message_channel_request_x11_screen_number(ssh_message msg);
LIBSSH_API const char *ssh_message_global_request_address(ssh_message msg);
LIBSSH_API int ssh_message_global_request_port(ssh_message msg);
LIBSSH_API int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
int remoteport, const char *sourcehost, int localport);
-LIBSSH_API int ssh_channel_open_x11(ssh_channel channel,
+LIBSSH_API int ssh_channel_open_x11(ssh_channel channel,
const char *orig_addr, int orig_port);
LIBSSH_API int ssh_channel_request_send_exit_status(ssh_channel channel,
diff --git a/include/libssh/session.h b/include/libssh/session.h
index ddd52fd6..27da7a83 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include "libssh/priv.h"
+#include "libssh/callbacks.h"
#include "libssh/kex.h"
#include "libssh/packet.h"
#include "libssh/pcap.h"
@@ -70,10 +71,24 @@ enum ssh_pending_call_e {
};
/* libssh calls may block an undefined amount of time */
-#define SSH_SESSION_FLAG_BLOCKING 1
+#define SSH_SESSION_FLAG_BLOCKING 0x0001
/* Client successfully authenticated */
-#define SSH_SESSION_FLAG_AUTHENTICATED 2
+#define SSH_SESSION_FLAG_AUTHENTICATED 0x0002
+
+/* Do not accept new session channels (no-more-sessions@openssh.com) */
+#define SSH_SESSION_FLAG_NO_MORE_SESSIONS 0x0004
+
+/* The KEXINIT message can be sent first by either of the parties so this flag
+ * indicates that the message was already sent to make sure it is sent and avoid
+ * sending it twice during key exchange to simplify the state machine. */
+#define SSH_SESSION_FLAG_KEXINIT_SENT 0x0008
+
+/* The current SSH2 session implements the "strict KEX" feature and should behave
+ * differently on SSH2_MSG_NEWKEYS. */
+#define SSH_SESSION_FLAG_KEX_STRICT 0x0010
+/* Unexpected packets have been sent while the session was still unencrypted */
+#define SSH_SESSION_FLAG_KEX_TAINTED 0x0020
/* codes to use with ssh_handle_packets*() */
/* Infinite timeout */
@@ -92,6 +107,13 @@ enum ssh_pending_call_e {
#define SSH_OPT_FLAG_KBDINT_AUTH 0x4
#define SSH_OPT_FLAG_GSSAPI_AUTH 0x8
+/* Escape expansion of different variables */
+#define SSH_OPT_EXP_FLAG_KNOWNHOSTS 0x1
+#define SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS 0x2
+#define SSH_OPT_EXP_FLAG_PROXYCOMMAND 0x4
+#define SSH_OPT_EXP_FLAG_IDENTITY 0x8
+#define SSH_OPT_EXP_FLAG_CONTROL_PATH 0x10
+
/* extensions flags */
/* negotiation enabled */
#define SSH_EXT_NEGOTIATION 0x01
@@ -131,10 +153,9 @@ struct ssh_session_struct {
/* Extensions negotiated using RFC 8308 */
uint32_t extensions;
- ssh_string banner; /* that's the issue banner from
- the server */
- char *discon_msg; /* disconnect message from
- the remote host */
+ ssh_string banner; /* that's the issue banner from the server */
+ char *peer_discon_msg; /* disconnect message from the remote host */
+ char *disconnect_message; /* disconnect message to be set */
ssh_buffer in_buffer;
PACKET in_packet;
ssh_buffer out_buffer;
@@ -158,32 +179,39 @@ struct ssh_session_struct {
uint32_t current_method;
} auth;
+ /* Sending this flag before key exchange to save one round trip during the
+ * key exchange. This might make sense on high-latency connections.
+ * So far internal only for testing. Usable only on the client side --
+ * there is no key exchange method that would start with server message */
+ bool send_first_kex_follows;
/*
* RFC 4253, 7.1: if the first_kex_packet_follows flag was set in
* the received SSH_MSG_KEXINIT, but the guess was wrong, this
* field will be set such that the following guessed packet will
- * be ignored. Once that packet has been received and ignored,
- * this field is cleared.
+ * be ignored on the receiving side. Once that packet has been received and
+ * ignored, this field is cleared.
+ * On the sending side, this is set after we got peer KEXINIT message and we
+ * need to resend the initial message of the negotiated KEX algorithm.
*/
- int first_kex_follows_guess_wrong;
+ bool first_kex_follows_guess_wrong;
ssh_buffer in_hashbuf;
ssh_buffer out_hashbuf;
struct ssh_crypto_struct *current_crypto;
- struct ssh_crypto_struct *next_crypto; /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */
+ /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */
+ struct ssh_crypto_struct *next_crypto;
struct ssh_list *channels; /* linked list of channels */
- int maxchannel;
+ uint32_t maxchannel;
ssh_agent agent; /* ssh agent */
-/* keyb interactive data */
+ /* keyboard interactive data */
struct ssh_kbdint_struct *kbdint;
struct ssh_gssapi_struct *gssapi;
/* server host keys */
struct {
ssh_key rsa_key;
- ssh_key dsa_key;
ssh_key ecdsa_key;
ssh_key ed25519_key;
/* The type of host key wanted by client */
@@ -193,7 +221,8 @@ struct ssh_session_struct {
/* auths accepted by server */
struct ssh_list *ssh_message_list; /* list of delayed SSH messages */
- int (*ssh_message_callback)( struct ssh_session_struct *session, ssh_message msg, void *userdata);
+ int (*ssh_message_callback)(struct ssh_session_struct *session,
+ ssh_message msg, void *userdata);
void *ssh_message_callback_data;
ssh_server_callbacks server_callbacks;
void (*ssh_connection_callback)( struct ssh_session_struct *session);
@@ -207,6 +236,9 @@ struct ssh_session_struct {
#endif
struct {
struct ssh_list *identity;
+ struct ssh_list *identity_non_exp;
+ struct ssh_list *certificate;
+ struct ssh_list *certificate_non_exp;
char *username;
char *host;
char *bindaddr; /* bind the client to an ip addr */
@@ -218,9 +250,10 @@ struct ssh_session_struct {
char *ProxyCommand;
char *custombanner;
char *moduli_file;
+ char *agent_socket;
unsigned long timeout; /* seconds */
unsigned long timeout_usec;
- unsigned int port;
+ uint16_t port;
socket_t fd;
int StrictHostKeyChecking;
char compressionlevel;
@@ -228,11 +261,16 @@ struct ssh_session_struct {
char *gss_client_identity;
int gss_delegate_creds;
int flags;
+ int exp_flags;
int nodelay;
bool config_processed;
uint8_t options_seen[SOC_MAX];
uint64_t rekey_data;
uint32_t rekey_time;
+ int rsa_min_size;
+ bool identities_only;
+ int control_master;
+ char *control_path;
} opts;
/* counters */
ssh_counter socket_counter;
@@ -247,7 +285,7 @@ struct ssh_session_struct {
typedef int (*ssh_termination_function)(void *user);
int ssh_handle_packets(ssh_session session, int timeout);
int ssh_handle_packets_termination(ssh_session session,
- long timeout,
+ int timeout,
ssh_termination_function fct,
void *user);
void ssh_socket_exception_callback(int code, int errno_code, void *user);
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
index c855df8a..754a54c3 100644
--- a/include/libssh/sftp.h
+++ b/include/libssh/sftp.h
@@ -77,6 +77,8 @@ typedef struct sftp_request_queue_struct* sftp_request_queue;
typedef struct sftp_session_struct* sftp_session;
typedef struct sftp_status_message_struct* sftp_status_message;
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
+typedef struct sftp_limits_struct* sftp_limits_t;
+typedef struct sftp_aio_struct* sftp_aio;
struct sftp_session_struct {
ssh_session session;
@@ -90,6 +92,7 @@ struct sftp_session_struct {
void **handles;
sftp_ext ext;
sftp_packet read_packet;
+ sftp_limits_t limits;
};
struct sftp_packet_struct {
@@ -201,6 +204,16 @@ struct sftp_statvfs_struct {
};
/**
+ * @brief SFTP limits structure.
+ */
+struct sftp_limits_struct {
+ uint64_t max_packet_length; /** maximum number of bytes in a single sftp packet */
+ uint64_t max_read_length; /** maximum length in a SSH_FXP_READ packet */
+ uint64_t max_write_length; /** maximum length in a SSH_FXP_WRITE packet */
+ uint64_t max_open_handles; /** maximum number of active handles allowed by server */
+};
+
+/**
* @brief Creates a new sftp session.
*
* This function creates a new sftp session and allocates a new sftp channel
@@ -476,13 +489,18 @@ LIBSSH_API void sftp_file_set_blocking(sftp_file handle);
/**
* @brief Read from a file using an opened sftp file handle.
*
+ * This function caps the length a user is allowed to read from an sftp file.
+ *
+ * The value used for the cap is same as the value of the max_read_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
* @param file The opened sftp file handle to be read from.
*
* @param buf Pointer to buffer to receive read data.
*
* @param count Size of the buffer in bytes.
*
- * @return Number of bytes written, < 0 on error with ssh and sftp
+ * @return Number of bytes read, < 0 on error with ssh and sftp
* error set.
*
* @see sftp_get_error()
@@ -520,7 +538,8 @@ LIBSSH_API ssize_t sftp_read(sftp_file file, void *buf, size_t count);
* @see sftp_async_read()
* @see sftp_open()
*/
-LIBSSH_API int sftp_async_read_begin(sftp_file file, uint32_t len);
+SSH_DEPRECATED LIBSSH_API int sftp_async_read_begin(sftp_file file,
+ uint32_t len);
/**
* @brief Wait for an asynchronous read to complete and save the data.
@@ -545,11 +564,19 @@ LIBSSH_API int sftp_async_read_begin(sftp_file file, uint32_t len);
*
* @see sftp_async_read_begin()
*/
-LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_t id);
+SSH_DEPRECATED LIBSSH_API int sftp_async_read(sftp_file file,
+ void *data,
+ uint32_t len,
+ uint32_t id);
/**
* @brief Write to a file using an opened sftp file handle.
*
+ * This function caps the length a user is allowed to write to an sftp file.
+ *
+ * The value used for the cap is same as the value of the max_write_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
* @param file Open sftp file handle to write to.
*
* @param buf Pointer to buffer to write data.
@@ -566,6 +593,229 @@ LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_
LIBSSH_API ssize_t sftp_write(sftp_file file, const void *buf, size_t count);
/**
+ * @brief Deallocate memory corresponding to a sftp aio handle.
+ *
+ * This function deallocates memory corresponding to the aio handle returned
+ * by the sftp_aio_begin_*() functions. Users can use this function to free
+ * memory corresponding to an aio handle for an outstanding async i/o request
+ * on encountering some error.
+ *
+ * @param aio sftp aio handle corresponding to which memory has
+ * to be deallocated.
+ *
+ * @see sftp_aio_begin_read()
+ * @see sftp_aio_wait_read()
+ * @see sftp_aio_begin_write()
+ * @see sftp_aio_wait_write()
+ */
+LIBSSH_API void sftp_aio_free(sftp_aio aio);
+#define SFTP_AIO_FREE(x) \
+ do { if(x != NULL) {sftp_aio_free(x); x = NULL;} } while(0)
+
+/**
+ * @brief Start an asynchronous read from a file using an opened sftp
+ * file handle.
+ *
+ * Its goal is to avoid the slowdowns related to the request/response pattern
+ * of a synchronous read. To do so, you must call 2 functions :
+ *
+ * sftp_aio_begin_read() and sftp_aio_wait_read().
+ *
+ * - The first step is to call sftp_aio_begin_read(). This function sends a
+ * read request to the sftp server, dynamically allocates memory to store
+ * information about the sent request and provides the caller an sftp aio
+ * handle to that memory.
+ *
+ * - The second step is to call sftp_aio_wait_read() and pass it the address
+ * of a location storing the sftp aio handle provided by
+ * sftp_aio_begin_read().
+ *
+ * These two functions do not close the open sftp file handle passed to
+ * sftp_aio_begin_read() irrespective of whether they fail or not.
+ *
+ * It is the responsibility of the caller to ensure that the open sftp file
+ * handle passed to sftp_aio_begin_read() must not be closed before the
+ * corresponding call to sftp_aio_wait_read(). After sftp_aio_wait_read()
+ * returns, it is caller's decision whether to immediately close the file by
+ * calling sftp_close() or to keep it open and perform some more operations
+ * on it.
+ *
+ * This function caps the length a user is allowed to read from an sftp file,
+ * the value of len parameter after capping is returned on success.
+ *
+ * The value used for the cap is same as the value of the max_read_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
+ * @param file The opened sftp file handle to be read from.
+ *
+ * @param len Number of bytes to read.
+ *
+ * @param aio Pointer to a location where the sftp aio handle
+ * (corresponding to the sent request) should be stored.
+ *
+ * @returns On success, the number of bytes the server is
+ * requested to read (value of len parameter after
+ * capping). On error, SSH_ERROR with sftp and ssh
+ * errors set.
+ *
+ * @warning When calling this function, the internal file offset is
+ * updated corresponding to the number of bytes requested
+ * to read.
+ *
+ * @warning A call to sftp_aio_begin_read() sends a request to
+ * the server. When the server answers, libssh allocates
+ * memory to store it until sftp_aio_wait_read() is called.
+ * Not calling sftp_aio_wait_read() will lead to memory
+ * leaks.
+ *
+ * @see sftp_aio_wait_read()
+ * @see sftp_aio_free()
+ * @see sftp_open()
+ * @see sftp_close()
+ * @see sftp_get_error()
+ * @see ssh_get_error()
+ */
+LIBSSH_API ssize_t sftp_aio_begin_read(sftp_file file,
+ size_t len,
+ sftp_aio *aio);
+
+/**
+ * @brief Wait for an asynchronous read to complete and store the read data
+ * in the supplied buffer.
+ *
+ * A pointer to an sftp aio handle should be passed while calling
+ * this function. Except when the return value is SSH_AGAIN,
+ * this function releases the memory corresponding to the supplied
+ * aio handle and assigns NULL to that aio handle using the passed
+ * pointer to that handle.
+ *
+ * If the file is opened in non-blocking mode and the request hasn't been
+ * executed yet, this function returns SSH_AGAIN and must be called again
+ * using the same sftp aio handle.
+ *
+ * @param aio Pointer to the sftp aio handle returned by
+ * sftp_aio_begin_read().
+ *
+ * @param buf Pointer to the buffer in which read data will be stored.
+ *
+ * @param buf_size Size of the buffer in bytes. It should be bigger or
+ * equal to the length parameter of the
+ * sftp_aio_begin_read() call.
+ *
+ * @return Number of bytes read, 0 on EOF, SSH_ERROR if an error
+ * occurred, SSH_AGAIN if the file is opened in nonblocking
+ * mode and the request hasn't been executed yet.
+ *
+ * @warning A call to this function with an invalid sftp aio handle
+ * may never return.
+ *
+ * @see sftp_aio_begin_read()
+ * @see sftp_aio_free()
+ */
+LIBSSH_API ssize_t sftp_aio_wait_read(sftp_aio *aio,
+ void *buf,
+ size_t buf_size);
+
+/**
+ * @brief Start an asynchronous write to a file using an opened sftp
+ * file handle.
+ *
+ * Its goal is to avoid the slowdowns related to the request/response pattern
+ * of a synchronous write. To do so, you must call 2 functions :
+ *
+ * sftp_aio_begin_write() and sftp_aio_wait_write().
+ *
+ * - The first step is to call sftp_aio_begin_write(). This function sends a
+ * write request to the sftp server, dynamically allocates memory to store
+ * information about the sent request and provides the caller an sftp aio
+ * handle to that memory.
+ *
+ * - The second step is to call sftp_aio_wait_write() and pass it the address
+ * of a location storing the sftp aio handle provided by
+ * sftp_aio_begin_write().
+ *
+ * These two functions do not close the open sftp file handle passed to
+ * sftp_aio_begin_write() irrespective of whether they fail or not.
+ *
+ * It is the responsibility of the caller to ensure that the open sftp file
+ * handle passed to sftp_aio_begin_write() must not be closed before the
+ * corresponding call to sftp_aio_wait_write(). After sftp_aio_wait_write()
+ * returns, it is caller's decision whether to immediately close the file by
+ * calling sftp_close() or to keep it open and perform some more operations
+ * on it.
+ *
+ * This function caps the length a user is allowed to write to an sftp file,
+ * the value of len parameter after capping is returned on success.
+ *
+ * The value used for the cap is same as the value of the max_write_length
+ * field of the sftp_limits_t returned by sftp_limits().
+ *
+ * @param file The opened sftp file handle to write to.
+ *
+ * @param buf Pointer to the buffer containing data to write.
+ *
+ * @param len Number of bytes to write.
+ *
+ * @param aio Pointer to a location where the sftp aio handle
+ * (corresponding to the sent request) should be stored.
+ *
+ * @returns On success, the number of bytes the server is
+ * requested to write (value of len parameter after
+ * capping). On error, SSH_ERROR with sftp and ssh errors
+ * set.
+ *
+ * @warning When calling this function, the internal file offset is
+ * updated corresponding to the number of bytes requested
+ * to write.
+ *
+ * @warning A call to sftp_aio_begin_write() sends a request to
+ * the server. When the server answers, libssh allocates
+ * memory to store it until sftp_aio_wait_write() is
+ * called. Not calling sftp_aio_wait_write() will lead to
+ * memory leaks.
+ *
+ * @see sftp_aio_wait_write()
+ * @see sftp_aio_free()
+ * @see sftp_open()
+ * @see sftp_close()
+ * @see sftp_get_error()
+ * @see ssh_get_error()
+ */
+LIBSSH_API ssize_t sftp_aio_begin_write(sftp_file file,
+ const void *buf,
+ size_t len,
+ sftp_aio *aio);
+
+/**
+ * @brief Wait for an asynchronous write to complete.
+ *
+ * A pointer to an sftp aio handle should be passed while calling
+ * this function. Except when the return value is SSH_AGAIN,
+ * this function releases the memory corresponding to the supplied
+ * aio handle and assigns NULL to that aio handle using the passed
+ * pointer to that handle.
+ *
+ * If the file is opened in non-blocking mode and the request hasn't
+ * been executed yet, this function returns SSH_AGAIN and must be called
+ * again using the same sftp aio handle.
+ *
+ * @param aio Pointer to the sftp aio handle returned by
+ * sftp_aio_begin_write().
+ *
+ * @return Number of bytes written on success, SSH_ERROR
+ * if an error occurred, SSH_AGAIN if the file is
+ * opened in nonblocking mode and the request hasn't
+ * been executed yet.
+ *
+ * @warning A call to this function with an invalid sftp aio handle
+ * may never return.
+ *
+ * @see sftp_aio_begin_write()
+ * @see sftp_aio_free()
+ */
+LIBSSH_API ssize_t sftp_aio_wait_write(sftp_aio *aio);
+
+/**
* @brief Seek to a specific location in a file.
*
* @param file Open sftp file handle to seek in.
@@ -605,8 +855,7 @@ LIBSSH_API unsigned long sftp_tell(sftp_file file);
* @param file Open sftp file handle.
*
* @return The offset of the current byte relative to the beginning
- * of the file associated with the file descriptor. < 0 on
- * error.
+ * of the file associated with the file descriptor.
*/
LIBSSH_API uint64_t sftp_tell64(sftp_file file);
@@ -681,6 +930,11 @@ LIBSSH_API int sftp_rename(sftp_session sftp, const char *original, const char
/**
* @brief Set file attributes on a file, directory or symbolic link.
*
+ * Note, that this function can only set time values using 32 bit values due to
+ * the restrictions in the SFTP protocol version 3 implemented by libssh.
+ * The support for 64 bit time values was introduced in SFTP version 5, which is
+ * not implemented by libssh nor any major SFTP servers.
+ *
* @param sftp The sftp session handle.
*
* @param file The file which attributes should be changed.
@@ -767,12 +1021,30 @@ LIBSSH_API int sftp_symlink(sftp_session sftp, const char *target, const char *d
* @param path Specifies the path name of the symlink to be read.
*
* @return The target of the link, NULL on error.
+ * The caller needs to free the memory
+ * using ssh_string_free_char().
*
* @see sftp_get_error()
*/
LIBSSH_API char *sftp_readlink(sftp_session sftp, const char *path);
/**
+ * @brief Create a hard link.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param oldpath Specifies the pathname of the file for
+ * which the new hardlink is to be created.
+ *
+ * @param newpath Specifies the pathname of the hardlink to be created.
+ *
+ * @return 0 on success, -1 on error with ssh and sftp error set.
+ *
+ * @see sftp_get_error()
+ */
+LIBSSH_API int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath);
+
+/**
* @brief Get information about a mounted file system.
*
* @param sftp The sftp session handle.
@@ -820,6 +1092,24 @@ LIBSSH_API void sftp_statvfs_free(sftp_statvfs_t statvfs_o);
LIBSSH_API int sftp_fsync(sftp_file file);
/**
+ * @brief Get information about the various limits the server might impose.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @return A limits structure or NULL on error.
+ *
+ * @see sftp_get_error()
+ */
+LIBSSH_API sftp_limits_t sftp_limits(sftp_session sftp);
+
+/**
+ * @brief Free the memory of an allocated limits.
+ *
+ * @param limits The limits to free.
+ */
+LIBSSH_API void sftp_limits_free(sftp_limits_t limits);
+
+/**
* @brief Canonicalize a sftp path.
*
* @param sftp The sftp session handle.
@@ -841,6 +1131,19 @@ LIBSSH_API char *sftp_canonicalize_path(sftp_session sftp, const char *path);
*/
LIBSSH_API int sftp_server_version(sftp_session sftp);
+/**
+ * @brief Canonicalize path using expand-path@openssh.com extension
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param path The path to be canonicalized.
+ *
+ * @return A pointer to the newly allocated canonicalized path,
+ * NULL on error. The caller needs to free the memory
+ * using ssh_string_free_char().
+ */
+LIBSSH_API char *sftp_expand_path(sftp_session sftp, const char *path);
+
#ifdef WITH_SERVER
/**
* @brief Create a new sftp server session.
@@ -860,7 +1163,7 @@ LIBSSH_API sftp_session sftp_server_new(ssh_session session, ssh_channel chan);
*
* @return 0 on success, < 0 on error.
*/
-LIBSSH_API int sftp_server_init(sftp_session sftp);
+SSH_DEPRECATED LIBSSH_API int sftp_server_init(sftp_session sftp);
/**
* @brief Close and deallocate a sftp server session.
diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h
index 83925191..8470a8ad 100644
--- a/include/libssh/sftp_priv.h
+++ b/include/libssh/sftp_priv.h
@@ -21,12 +21,63 @@
#ifndef SFTP_PRIV_H
#define SFTP_PRIV_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
sftp_packet sftp_packet_read(sftp_session sftp);
-ssize_t sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload);
+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);
+/**
+ * @brief Reply to the SSH_FXP_INIT message with the SSH_FXP_VERSION message
+ *
+ * @param client_msg The pointer to client message.
+ *
+ * @return 0 on success, < 0 on error with ssh and sftp error set.
+ *
+ * @see sftp_get_error()
+ */
+int sftp_reply_version(sftp_client_message client_msg);
+/**
+ * @brief Decode the data from channel buffer into sftp read_packet.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param data The pointer to the data buffer of channel.
+ * @param len The data buffer length
+ *
+ * @return Length of data decoded.
+ */
+int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data, uint32_t len);
+
+void sftp_set_error(sftp_session sftp, int errnum);
+
+void sftp_message_free(sftp_message msg);
+
+int sftp_read_and_dispatch(sftp_session sftp);
+
+sftp_message sftp_dequeue(sftp_session sftp, uint32_t id);
+
+/*
+ * Assigns a new SFTP ID for new requests and assures there is no collision
+ * between them.
+ * Returns a new ID ready to use in a request
+ */
+static inline uint32_t sftp_get_new_id(sftp_session session)
+{
+ return ++session->id_counter;
+}
+
+sftp_status_message parse_status_msg(sftp_message msg);
+
+void status_msg_free(sftp_status_message status);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* SFTP_PRIV_H */
diff --git a/include/libssh/sftpserver.h b/include/libssh/sftpserver.h
new file mode 100644
index 00000000..d7ed6e49
--- /dev/null
+++ b/include/libssh/sftpserver.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2022 Zeyu Sheng <shengzeyu19_98@163.com>
+ * Copyright (c) 2023 Red Hat, Inc.
+ *
+ * Authors: Jakub Jelen <jjelen@redhat.com>
+ *
+ * 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_SERVER_H
+#define SFTP_SERVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+/**
+ * @defgroup libssh_sftp_server The libssh SFTP server API
+ *
+ * @brief SFTP server handling functions
+ *
+ * TODO
+ *
+ * @{
+ */
+
+#define SSH_SFTP_CALLBACK(name) \
+ static int name(sftp_client_message message)
+
+typedef int (*sftp_server_message_callback)(sftp_client_message message);
+
+struct sftp_message_handler
+{
+ const char *name;
+ const char *extended_name;
+ uint8_t type;
+
+ sftp_server_message_callback cb;
+};
+
+LIBSSH_API int sftp_channel_default_subsystem_request(ssh_session session,
+ ssh_channel channel,
+ const char *subsystem,
+ void *userdata);
+LIBSSH_API int sftp_channel_default_data_callback(ssh_session session,
+ ssh_channel channel,
+ void *data,
+ uint32_t len,
+ int is_stderr,
+ void *userdata);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SFTP_SERVER_H */
diff --git a/include/libssh/socket.h b/include/libssh/socket.h
index 5e345c68..cdd3c837 100644
--- a/include/libssh/socket.h
+++ b/include/libssh/socket.h
@@ -35,13 +35,14 @@ void ssh_socket_reset(ssh_socket s);
void ssh_socket_free(ssh_socket s);
void ssh_socket_set_fd(ssh_socket s, socket_t fd);
socket_t ssh_socket_get_fd(ssh_socket s);
-#ifndef _WIN32
+void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p);
int ssh_socket_unix(ssh_socket s, const char *path);
void ssh_execute_command(const char *command, socket_t in, socket_t out);
+#ifndef _WIN32
int ssh_socket_connect_proxycommand(ssh_socket s, const char *command);
#endif
void ssh_socket_close(ssh_socket s);
-int ssh_socket_write(ssh_socket s,const void *buffer, int len);
+int ssh_socket_write(ssh_socket s,const void *buffer, uint32_t len);
int ssh_socket_is_open(ssh_socket s);
int ssh_socket_fd_isset(ssh_socket s, fd_set *set);
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd);
diff --git a/include/libssh/string.h b/include/libssh/string.h
index 8c7db1df..35b2ea2e 100644
--- a/include/libssh/string.h
+++ b/include/libssh/string.h
@@ -22,6 +22,10 @@
#define STRING_H_
#include "libssh/priv.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* must be 32 bits number + immediately our data */
#ifdef _MSC_VER
#pragma pack(1)
@@ -38,4 +42,8 @@ __attribute__ ((packed))
#endif
;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* STRING_H_ */
diff --git a/include/libssh/threads.h b/include/libssh/threads.h
index 522f91d5..47340d17 100644
--- a/include/libssh/threads.h
+++ b/include/libssh/threads.h
@@ -49,6 +49,10 @@
#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int ssh_threads_init(void);
void ssh_threads_finalize(void);
const char *ssh_threads_get_type(void);
@@ -60,4 +64,8 @@ struct ssh_threads_callbacks_struct *ssh_threads_get_default(void);
int crypto_thread_init(struct ssh_threads_callbacks_struct *user_callbacks);
void crypto_thread_finalize(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* THREADS_H_ */
diff --git a/include/libssh/token.h b/include/libssh/token.h
index 9896fb06..550ad792 100644
--- a/include/libssh/token.h
+++ b/include/libssh/token.h
@@ -31,6 +31,10 @@ struct ssh_tokens_st {
char **tokens;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct ssh_tokens_st *ssh_tokenize(const char *chain, char separator);
void ssh_tokens_free(struct ssh_tokens_st *tokens);
@@ -45,4 +49,13 @@ char *ssh_remove_duplicates(const char *list);
char *ssh_append_without_duplicates(const char *list,
const char *appended_list);
+char *ssh_prefix_without_duplicates(const char *list,
+ const char *prefixed_list);
+char *ssh_remove_all_matching(const char *list,
+ const char *remove_list);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* TOKEN_H_ */
diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h
index df6544ee..b3e28eac 100644
--- a/include/libssh/wrapper.h
+++ b/include/libssh/wrapper.h
@@ -29,6 +29,10 @@
#include "libssh/libgcrypt.h"
#include "libssh/libmbedcrypto.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
enum ssh_kdf_digest {
SSH_KDF_SHA1=1,
SSH_KDF_SHA256,
@@ -68,42 +72,42 @@ struct ssh_crypto_struct;
typedef struct ssh_mac_ctx_struct *ssh_mac_ctx;
MD5CTX md5_init(void);
-void md5_update(MD5CTX c, const void *data, unsigned long len);
-void md5_final(unsigned char *md,MD5CTX c);
+void md5_ctx_free(MD5CTX);
+int md5_update(MD5CTX c, const void *data, size_t len);
+int md5_final(unsigned char *md, MD5CTX c);
SHACTX sha1_init(void);
-void sha1_update(SHACTX c, const void *data, unsigned long len);
-void sha1_final(unsigned char *md,SHACTX c);
-void sha1(const unsigned char *digest,int len,unsigned char *hash);
+void sha1_ctx_free(SHACTX);
+int sha1_update(SHACTX c, const void *data, size_t len);
+int sha1_final(unsigned char *md,SHACTX c);
+int sha1(const unsigned char *digest,size_t len, unsigned char *hash);
SHA256CTX sha256_init(void);
-void sha256_update(SHA256CTX c, const void *data, unsigned long len);
-void sha256_final(unsigned char *md,SHA256CTX c);
-void sha256(const unsigned char *digest, int len, unsigned char *hash);
+void sha256_ctx_free(SHA256CTX);
+int sha256_update(SHA256CTX c, const void *data, size_t len);
+int sha256_final(unsigned char *md,SHA256CTX c);
+int sha256(const unsigned char *digest, size_t len, unsigned char *hash);
SHA384CTX sha384_init(void);
-void sha384_update(SHA384CTX c, const void *data, unsigned long len);
-void sha384_final(unsigned char *md,SHA384CTX c);
-void sha384(const unsigned char *digest, int len, unsigned char *hash);
+void sha384_ctx_free(SHA384CTX);
+int sha384_update(SHA384CTX c, const void *data, size_t len);
+int sha384_final(unsigned char *md,SHA384CTX c);
+int sha384(const unsigned char *digest, size_t len, unsigned char *hash);
SHA512CTX sha512_init(void);
-void sha512_update(SHA512CTX c, const void *data, unsigned long len);
-void sha512_final(unsigned char *md,SHA512CTX c);
-void sha512(const unsigned char *digest, int len, unsigned char *hash);
-
-void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen);
-EVPCTX evp_init(int nid);
-void evp_update(EVPCTX ctx, const void *data, unsigned long len);
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen);
-
-HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type);
-void hmac_update(HMACCTX c, const void *data, unsigned long len);
-void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len);
+void sha512_ctx_free(SHA512CTX);
+int sha512_update(SHA512CTX c, const void *data, size_t len);
+int sha512_final(unsigned char *md,SHA512CTX c);
+int sha512(const unsigned char *digest, size_t len, unsigned char *hash);
+
+HMACCTX hmac_init(const void *key,size_t len, enum ssh_hmac_e type);
+int hmac_update(HMACCTX c, const void *data, size_t len);
+int hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, size_t *len);
size_t hmac_digest_len(enum ssh_hmac_e type);
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len);
int crypt_set_algorithms_client(ssh_session session);
@@ -120,4 +124,15 @@ struct ssh_hmac_struct *ssh_get_hmactab(void);
struct ssh_cipher_struct *ssh_get_ciphertab(void);
const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type, bool etm);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld, EVP_PKEY **pkey, int selection);
+int evp_dup_dsa_pkey(const ssh_key key, ssh_key new_key, int demote);
+int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote);
+int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote);
+#endif /* HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER */
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* WRAPPER_H_ */
diff --git a/libssh.pc.cmake b/libssh.pc.cmake
index b37cb3fc..f288b94d 100644
--- a/libssh.pc.cmake
+++ b/libssh.pc.cmake
@@ -1,6 +1,10 @@
-Name: ${PROJECT_NAME}
-Description: The SSH Library
-Version: ${PROJECT_VERSION}
-Libs: -L${CMAKE_INSTALL_FULL_LIBDIR} -lssh
-Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR}
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+Name: @PROJECT_NAME@
+Description: The SSH Library
+Version: @PROJECT_VERSION@
+Libs: -L${libdir} -lssh
+Cflags: -I${includedir}
diff --git a/src/ABI/current b/src/ABI/current
index 28715673..b617d997 100644
--- a/src/ABI/current
+++ b/src/ABI/current
@@ -1 +1 @@
-4.8.1 \ No newline at end of file
+4.9.0 \ No newline at end of file
diff --git a/src/ABI/libssh-4.9.0.symbols b/src/ABI/libssh-4.9.0.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.0.symbols
@@ -0,0 +1,427 @@
+_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_port
+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_dup
+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_issue_banner
+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_set_disconnect_message
+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_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+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 4f350d9d..93ecb5e7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,6 @@
set(LIBSSH_PUBLIC_INCLUDE_DIRS ${libssh_SOURCE_DIR}/include)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${libssh_BINARY_DIR}/include
${libssh_BINARY_DIR}
)
@@ -9,24 +8,9 @@ set(LIBSSH_LINK_LIBRARIES
${LIBSSH_REQUIRED_LIBRARIES}
)
-if (WIN32)
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ws2_32
- )
-endif (WIN32)
-
-if (OPENSSL_CRYPTO_LIBRARIES)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${OPENSSL_INCLUDE_DIR}
- )
-
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${OPENSSL_CRYPTO_LIBRARIES}
- )
-endif (OPENSSL_CRYPTO_LIBRARIES)
+if (TARGET OpenSSL::Crypto)
+ list(APPEND LIBSSH_LINK_LIBRARIES OpenSSL::Crypto)
+endif ()
if (MBEDTLS_CRYPTO_LIBRARY)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
@@ -51,15 +35,7 @@ if (GCRYPT_LIBRARIES)
endif()
if (WITH_ZLIB)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${ZLIB_INCLUDE_DIR}
- )
-
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${ZLIB_LIBRARY}
- )
+ list(APPEND LIBSSH_LINK_LIBRARIES ZLIB::ZLIB)
endif (WITH_ZLIB)
if (WITH_GSSAPI AND GSSAPI_FOUND)
@@ -93,6 +69,16 @@ if (MINGW AND Threads_FOUND)
)
endif()
+# The ws2_32 needs to be last for mingw to build
+# https://gitlab.com/libssh/libssh-mirror/-/issues/84
+if (WIN32)
+ set(LIBSSH_LINK_LIBRARIES
+ ${LIBSSH_LINK_LIBRARIES}
+ iphlpapi
+ ws2_32
+ )
+endif (WIN32)
+
if (BUILD_STATIC_LIB)
set(LIBSSH_STATIC_LIBRARY
ssh_static
@@ -141,6 +127,7 @@ set(libssh_SRCS
socket.c
string.c
threads.c
+ ttyopts.c
wrapper.c
external/bcrypt_pbkdf.c
external/blowfish.c
@@ -182,6 +169,8 @@ if (WITH_GCRYPT)
gcrypt_missing.c
pki_gcrypt.c
ecdh_gcrypt.c
+ getrandom_gcrypt.c
+ md_gcrypt.c
dh_key.c
pki_ed25519.c
external/ed25519.c
@@ -205,6 +194,8 @@ elseif (WITH_MBEDTLS)
mbedcrypto_missing.c
pki_mbedcrypto.c
ecdh_mbedcrypto.c
+ getrandom_mbedcrypto.c
+ md_mbedcrypto.c
dh_key.c
pki_ed25519.c
external/ed25519.c
@@ -227,36 +218,27 @@ else (WITH_GCRYPT)
threads/libcrypto.c
pki_crypto.c
ecdh_crypto.c
+ getrandom_crypto.c
+ md_crypto.c
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 (NOT (HAVE_OPENSSL_EVP_CHACHA20 AND HAVE_OPENSSL_EVP_POLY1305))
+ if (NOT HAVE_OPENSSL_EVP_CHACHA20)
set(libssh_SRCS
${libssh_SRCS}
external/chacha.c
external/poly1305.c
chachapoly.c
)
- endif (NOT (HAVE_OPENSSL_EVP_CHACHA20 AND HAVE_OPENSSL_EVP_POLY1305))
- if(OPENSSL_VERSION VERSION_LESS "1.1.0")
- set(libssh_SRCS ${libssh_SRCS} libcrypto-compat.c)
- endif()
+ endif (NOT HAVE_OPENSSL_EVP_CHACHA20)
endif (WITH_GCRYPT)
if (WITH_SFTP)
set(libssh_SRCS
${libssh_SRCS}
sftp.c
+ sftp_common.c
+ sftp_aio.c
)
if (WITH_SERVER)
@@ -298,12 +280,12 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
endif (WITH_GSSAPI AND GSSAPI_FOUND)
if (NOT WITH_NACL)
- if (NOT HAVE_OPENSSL_ED25519)
+ if (NOT HAVE_LIBCRYPTO)
set(libssh_SRCS
${libssh_SRCS}
external/curve25519_ref.c
)
- endif (NOT HAVE_OPENSSL_ED25519)
+ endif()
endif (NOT WITH_NACL)
# Set the path to the default map file
@@ -342,11 +324,14 @@ endif (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT AND ABIMAP_FOUND)
add_library(ssh ${libssh_SRCS})
target_compile_options(ssh
PRIVATE
- ${DEFAULT_C_COMPILE_FLAGS}
- -D_GNU_SOURCE)
+ ${DEFAULT_C_COMPILE_FLAGS})
+if (CYGWIN)
+ target_compile_definitions(ssh PRIVATE _GNU_SOURCE)
+endif ()
target_include_directories(ssh
PUBLIC
$<BUILD_INTERFACE:${libssh_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${libssh_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS})
@@ -386,6 +371,10 @@ if (MINGW)
target_link_libraries(ssh PRIVATE "-Wl,--enable-stdcall-fixup")
target_compile_definitions(ssh PRIVATE "_POSIX_SOURCE")
endif ()
+if (WITH_COVERAGE)
+ include(CodeCoverage)
+ append_coverage_compiler_flags_to_target(ssh)
+endif (WITH_COVERAGE)
install(TARGETS ssh
@@ -402,12 +391,15 @@ if (BUILD_STATIC_LIB)
add_library(ssh-static STATIC ${libssh_SRCS})
target_compile_options(ssh-static
PRIVATE
- ${DEFAULT_C_COMPILE_FLAGS}
- -D_GNU_SOURCE)
+ ${DEFAULT_C_COMPILE_FLAGS})
+ if (CYGWIN)
+ target_compile_definitions(ssh-static PRIVATE _GNU_SOURCE)
+ endif ()
target_include_directories(ssh-static
PUBLIC
$<BUILD_INTERFACE:${libssh_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${libssh_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS})
target_link_libraries(ssh-static
@@ -435,6 +427,9 @@ if (BUILD_STATIC_LIB)
if (WIN32)
target_compile_definitions(ssh-static PUBLIC "LIBSSH_STATIC")
endif (WIN32)
+ if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(ssh-static)
+ endif (WITH_COVERAGE)
endif (BUILD_STATIC_LIB)
message(STATUS "Threads_FOUND=${Threads_FOUND}")
diff --git a/src/agent.c b/src/agent.c
index 3bc99a73..1c79c6eb 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -33,8 +33,6 @@
* the agent returns the signed data
*/
-#ifndef _WIN32
-
#include "config.h"
#include <stdlib.h>
@@ -46,8 +44,14 @@
#include <unistd.h>
#endif
+#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
#include "libssh/agent.h"
#include "libssh/priv.h"
@@ -63,9 +67,9 @@
(((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \
((x) == SSH2_AGENT_FAILURE))
-static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int do_read) {
+static uint32_t atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read) {
char *b = buf;
- size_t pos = 0;
+ uint32_t pos = 0;
ssize_t res;
ssh_pollfd_t pfd;
ssh_channel channel = agent->channel;
@@ -79,9 +83,9 @@ static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int
while (n > pos) {
if (do_read) {
- res = read(fd, b + pos, n - pos);
+ res = recv(fd, b + pos, n - pos, 0);
} else {
- res = write(fd, b + pos, n - pos);
+ res = send(fd, b + pos, n - pos, 0);
}
switch (res) {
case -1:
@@ -102,7 +106,7 @@ static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int
errno = do_read ? 0 : EPIPE;
return pos;
default:
- pos += (size_t) res;
+ pos += (uint32_t) res;
}
}
return pos;
@@ -117,7 +121,7 @@ static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int
continue;
if (res == SSH_ERROR)
return 0;
- pos += (size_t)res;
+ pos += (uint32_t)res;
}
return pos;
}
@@ -146,11 +150,21 @@ static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channe
agent->channel = channel;
}
+/**
+ * @addtogroup libssh_auth
+ *
+ * @{
+ */
+
/** @brief sets the SSH agent channel.
* The SSH agent channel will be used to authenticate this client using
* an agent through a channel, from another session. The most likely use
* is to implement SSH Agent forwarding into a SSH proxy.
+ *
+ * @param session the session
+ *
* @param[in] channel a SSH channel from another session.
+ *
* @returns SSH_OK in case of success
* SSH_ERROR in case of an error
*/
@@ -185,6 +199,10 @@ int ssh_set_agent_socket(ssh_session session, socket_t fd){
return SSH_OK;
}
+/**
+ * @}
+ */
+
void ssh_agent_close(struct ssh_agent_struct *agent) {
if (agent == NULL) {
return;
@@ -216,7 +234,8 @@ static int agent_connect(ssh_session session) {
if (session->agent->channel != NULL)
return 0;
- auth_sock = getenv("SSH_AUTH_SOCK");
+ auth_sock = session->opts.agent_socket ?
+ session->opts.agent_socket : getenv("SSH_AUTH_SOCK");
if (auth_sock && *auth_sock) {
if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) {
@@ -253,29 +272,30 @@ static int agent_talk(struct ssh_session_struct *session,
uint32_t len = 0;
uint8_t tmpbuf[4];
uint8_t *payload = tmpbuf;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
len = ssh_buffer_get_len(request);
- SSH_LOG(SSH_LOG_TRACE, "Request length: %u", len);
+ SSH_LOG(SSH_LOG_TRACE, "Request length: %" PRIu32, len);
PUSH_BE_U32(payload, 0, len);
/* send length and then the request packet */
if (atomicio(session->agent, payload, 4, 0) == 4) {
if (atomicio(session->agent, ssh_buffer_get(request), len, 0)
!= len) {
- SSH_LOG(SSH_LOG_WARN, "atomicio sending request failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "atomicio sending request failed: %s",
strerror(errno));
return -1;
}
} else {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"atomicio sending request length failed: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
/* wait for response, read the length of the response packet */
if (atomicio(session->agent, payload, 4, 1) != 4) {
- SSH_LOG(SSH_LOG_WARN, "atomicio read response length failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "atomicio read response length failed: %s",
strerror(errno));
return -1;
}
@@ -283,19 +303,19 @@ static int agent_talk(struct ssh_session_struct *session,
len = PULL_BE_U32(payload, 0);
if (len > 256 * 1024) {
ssh_set_error(session, SSH_FATAL,
- "Authentication response too long: %u", len);
+ "Authentication response too long: %" PRIu32, len);
return -1;
}
- SSH_LOG(SSH_LOG_TRACE, "Response length: %u", len);
+ SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
payload = ssh_buffer_allocate(reply, len);
if (payload == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Not enough space");
+ SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
return -1;
}
if (atomicio(session->agent, payload, len, 1) != len) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_DEBUG,
"Error reading response from authentication socket.");
/* Rollback the unused space */
ssh_buffer_pass_bytes_end(reply, len);
@@ -311,7 +331,7 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
ssh_buffer reply = NULL;
unsigned int type = 0;
uint32_t count = 0;
- int rc;
+ uint32_t rc;
/* send message to the agent requesting the list of identities */
request = ssh_buffer_new();
@@ -343,7 +363,7 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
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);
+ "Bad authentication reply size: %" PRIu32, rc);
SSH_BUFFER_FREE(reply);
return 0;
}
@@ -351,7 +371,7 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
type = bswap_32(type);
#endif
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Answer type: %d, expected answer: %d",
type, SSH2_AGENT_IDENTITIES_ANSWER);
@@ -384,15 +404,13 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
return 0;
}
- if (session->agent->ident) {
- ssh_buffer_reinit(session->agent->ident);
- }
+ ssh_buffer_free(session->agent->ident);
session->agent->ident = reply;
return session->agent->count;
}
-/* caller has to free commment */
+/* caller has to free comment */
ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
char **comment) {
if (ssh_agent_get_ident_count(session) > 0) {
@@ -402,7 +420,7 @@ ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
return NULL;
}
-/* caller has to free commment */
+/* caller has to free comment */
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
char **comment) {
struct ssh_key_struct *key;
@@ -571,7 +589,7 @@ ssh_string ssh_agent_sign_data(ssh_session session,
#endif
if (agent_failed(type)) {
- SSH_LOG(SSH_LOG_WARN, "Agent reports failure in signing the key");
+ SSH_LOG(SSH_LOG_DEBUG, "Agent reports failure in signing the key");
SSH_BUFFER_FREE(reply);
return NULL;
} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
@@ -588,5 +606,3 @@ ssh_string ssh_agent_sign_data(ssh_session session,
return sig_blob;
}
-
-#endif /* _WIN32 */
diff --git a/src/auth.c b/src/auth.c
index ccf75756..0acd9686 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -47,7 +47,7 @@
#include "libssh/legacy.h"
/**
- * @defgroup libssh_auth The SSH authentication functions.
+ * @defgroup libssh_auth The SSH authentication functions
* @ingroup libssh
*
* Functions to authenticate with a server.
@@ -58,7 +58,7 @@
/**
* @internal
*
- * @brief Ask access to the ssh-userauth service.
+ * @brief Ask for access to the ssh-userauth service.
*
* @param[in] session The SSH session handle.
*
@@ -66,19 +66,21 @@
* @returns SSH_AGAIN on nonblocking mode, if calling that function
* again is necessary
*/
-static int ssh_userauth_request_service(ssh_session session) {
+static int ssh_userauth_request_service(ssh_session session)
+{
int rc;
rc = ssh_service_request(session, "ssh-userauth");
if ((rc != SSH_OK) && (rc != SSH_AGAIN)) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to request \"ssh-userauth\" service");
}
return rc;
}
-static int ssh_auth_response_termination(void *user) {
+static int ssh_auth_response_termination(void *user)
+{
ssh_session session = (ssh_session)user;
switch (session->auth.state) {
case SSH_AUTH_STATE_NONE:
@@ -139,7 +141,8 @@ static const char *ssh_auth_get_current_method(ssh_session session)
* SSH_AUTH_AGAIN In nonblocking mode, call has to be made again
* SSH_AUTH_ERROR Error during the process.
*/
-static int ssh_userauth_get_response(ssh_session session) {
+static int ssh_userauth_get_response(ssh_session session)
+{
int rc = SSH_AUTH_ERROR;
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
@@ -199,7 +202,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_banner) {
banner = ssh_buffer_get_ssh_string(packet);
if (banner == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid SSH_USERAUTH_BANNER packet");
} else {
SSH_LOG(SSH_LOG_DEBUG,
@@ -237,7 +240,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
if (partial) {
session->auth.state = SSH_AUTH_STATE_PARTIAL;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Partial success for '%s'. Authentication that can continue: %s",
current_method,
auth_methods);
@@ -247,7 +250,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
"Access denied for '%s'. Authentication that can continue: %s",
current_method,
auth_methods);
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"%s",
ssh_get_error(session));
@@ -364,7 +367,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok) {
*
* @param[in] username Deprecated, set to NULL.
*
- * @returns A bitfield of the fllowing values:
+ * @returns A bitfield of the following values:
* - SSH_AUTH_METHOD_PASSWORD
* - SSH_AUTH_METHOD_PUBLICKEY
* - SSH_AUTH_METHOD_HOSTBASED
@@ -403,10 +406,11 @@ int ssh_userauth_list(ssh_session session, const char *username)
* authentication. The username should only be set with ssh_options_set() only
* before you connect to the server.
*/
-int ssh_userauth_none(ssh_session session, const char *username) {
+int ssh_userauth_none(ssh_session session, const char *username)
+{
int rc;
- switch(session->pending_call_state) {
+ switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_NONE:
@@ -492,6 +496,7 @@ int ssh_userauth_try_publickey(ssh_session session,
{
ssh_string pubkey_s = NULL;
const char *sig_type_c = NULL;
+ bool allowed;
int rc;
if (session == NULL) {
@@ -513,7 +518,7 @@ int ssh_userauth_try_publickey(ssh_session session,
SSH_FATAL,
"Wrong state (%d) during pending SSH call",
session->pending_call_state);
- return SSH_ERROR;
+ return SSH_AUTH_ERROR;
}
/* Check if the given public key algorithm is allowed */
@@ -523,13 +528,21 @@ int ssh_userauth_try_publickey(ssh_session session,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, pubkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ return SSH_AUTH_DENIED;
+ }
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@@ -544,6 +557,7 @@ int ssh_userauth_try_publickey(ssh_session session,
goto fail;
}
+ SSH_LOG(SSH_LOG_TRACE, "Trying signature type %s", sig_type_c);
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
SSH2_MSG_USERAUTH_REQUEST,
@@ -611,6 +625,7 @@ int ssh_userauth_publickey(ssh_session session,
const ssh_key privkey)
{
ssh_string str = NULL;
+ bool allowed;
int rc;
const char *sig_type_c = NULL;
enum ssh_keytypes_e key_type;
@@ -647,13 +662,21 @@ int ssh_userauth_publickey(ssh_session session,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, privkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(privkey));
+ return SSH_AUTH_DENIED;
+ }
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@@ -668,6 +691,7 @@ int ssh_userauth_publickey(ssh_session session,
goto fail;
}
+ SSH_LOG(SSH_LOG_TRACE, "Sending signature type %s", sig_type_c);
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
SSH2_MSG_USERAUTH_REQUEST,
@@ -722,7 +746,6 @@ fail:
return SSH_AUTH_ERROR;
}
-#ifndef _WIN32
static int ssh_userauth_agent_publickey(ssh_session session,
const char *username,
ssh_key pubkey)
@@ -730,18 +753,20 @@ static int ssh_userauth_agent_publickey(ssh_session session,
ssh_string pubkey_s = NULL;
ssh_string sig_blob = NULL;
const char *sig_type_c = NULL;
+ bool allowed;
int rc;
- switch(session->pending_call_state) {
- case SSH_PENDING_CALL_NONE:
- break;
- case SSH_PENDING_CALL_AUTH_AGENT:
- goto pending;
- default:
- ssh_set_error(session,
- SSH_FATAL,
- "Bad call during pending SSH call in ssh_userauth_try_publickey");
- return SSH_ERROR;
+ switch (session->pending_call_state) {
+ case SSH_PENDING_CALL_NONE:
+ break;
+ case SSH_PENDING_CALL_AUTH_AGENT:
+ goto pending;
+ default:
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Bad call during pending SSH call in %s",
+ __func__);
+ return SSH_ERROR;
}
rc = ssh_userauth_request_service(session);
@@ -765,7 +790,8 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
@@ -773,17 +799,25 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, pubkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ SSH_STRING_FREE(pubkey_s);
+ return SSH_AUTH_DENIED;
+ }
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
- SSH2_MSG_USERAUTH_REQUEST,
- username ? username : session->opts.username,
- "ssh-connection",
- "publickey",
- 1, /* private key */
- sig_type_c, /* algo */
- pubkey_s /* public key */
- );
+ SSH2_MSG_USERAUTH_REQUEST,
+ username ? username : session->opts.username,
+ "ssh-connection",
+ "publickey",
+ 1, /* private key */
+ sig_type_c, /* algo */
+ pubkey_s /* public key */
+ );
SSH_STRING_FREE(pubkey_s);
if (rc < 0) {
goto fail;
@@ -827,6 +861,7 @@ fail:
enum ssh_agent_state_e {
SSH_AGENT_STATE_NONE = 0,
SSH_AGENT_STATE_PUBKEY,
+ SSH_AGENT_STATE_CERT,
SSH_AGENT_STATE_AUTH
};
@@ -837,7 +872,8 @@ struct ssh_agent_state_struct {
};
/* Internal function */
-void ssh_agent_state_free(void *data) {
+void ssh_agent_state_free(void *data)
+{
struct ssh_agent_state_struct *state = data;
if (state) {
@@ -870,9 +906,15 @@ void ssh_agent_state_free(void *data) {
* before you connect to the server.
*/
int ssh_userauth_agent(ssh_session session,
- const char *username) {
+ const char *username)
+{
int rc = SSH_AUTH_ERROR;
- struct ssh_agent_state_struct *state;
+ struct ssh_agent_state_struct *state = NULL;
+ ssh_key *configKeys = NULL;
+ ssh_key *configCerts = NULL;
+ size_t configKeysCount = 0;
+ size_t configCertsCount = 0;
+ size_t i;
if (session == NULL) {
return SSH_AUTH_ERROR;
@@ -889,7 +931,7 @@ int ssh_userauth_agent(ssh_session session,
return SSH_AUTH_ERROR;
}
ZERO_STRUCTP(session->agent_state);
- session->agent_state->state=SSH_AGENT_STATE_NONE;
+ session->agent_state->state = SSH_AGENT_STATE_NONE;
}
state = session->agent_state;
@@ -901,70 +943,239 @@ int ssh_userauth_agent(ssh_session session,
return SSH_AUTH_DENIED;
}
+ if (session->opts.identities_only) {
+ /*
+ * Read keys mentioned in the config, so we can check if key from agent
+ * is in there.
+ */
+ size_t identityLen = ssh_list_count(session->opts.identity);
+ size_t certsLen = ssh_list_count(session->opts.certificate);
+ struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
+
+ configKeys = malloc(identityLen * sizeof(ssh_key));
+ configCerts = malloc((certsLen + identityLen) * sizeof(ssh_key));
+ if (configKeys == NULL || configCerts == NULL) {
+ free(configKeys);
+ free(configCerts);
+ ssh_set_error_oom(session);
+ return SSH_AUTH_ERROR;
+ }
+
+ while (it != NULL && configKeysCount < identityLen) {
+ const char *privkeyFile = it->data;
+ size_t certPathLen;
+ char *certFile = NULL;
+ ssh_key pubkey = NULL;
+ ssh_key cert = NULL;
+
+ /*
+ * Read the private key file listed in the config, but we're only
+ * interested in the public key. Don't try to decrypt private key.
+ */
+ rc = ssh_pki_import_pubkey_file(privkeyFile, &pubkey);
+ if (rc == SSH_OK) {
+ configKeys[configKeysCount++] = pubkey;
+ } else {
+ char *pubkeyFile = NULL;
+ size_t pubkeyPathLen = strlen(privkeyFile) + sizeof(".pub");
+
+ SSH_KEY_FREE(pubkey);
+
+ /*
+ * If we couldn't get the public key from the private key file,
+ * try a .pub file instead.
+ */
+ pubkeyFile = malloc(pubkeyPathLen);
+ if (!pubkeyFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(pubkeyFile, pubkeyPathLen, "%s.pub", privkeyFile);
+ rc = ssh_pki_import_pubkey_file(pubkeyFile, &pubkey);
+ free(pubkeyFile);
+ if (rc == SSH_OK) {
+ configKeys[configKeysCount++] = pubkey;
+ } else if (pubkey) {
+ SSH_KEY_FREE(pubkey);
+ }
+ }
+ /* Now try to see if there is a certificate with default name
+ * do not merge it yet with the key as we need to try first the
+ * non-certified key */
+ certPathLen = strlen(privkeyFile) + sizeof("-cert.pub");
+ certFile = malloc(certPathLen);
+ if (!certFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(certFile, certPathLen, "%s-cert.pub", privkeyFile);
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ free(certFile);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ /* And now load separately-listed certificates. */
+ it = ssh_list_get_iterator(session->opts.certificate);
+ while (it != NULL && configCertsCount < certsLen + identityLen) {
+ const char *certFile = it->data;
+ ssh_key cert = NULL;
+
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ }
+
while (state->pubkey != NULL) {
if (state->state == SSH_AGENT_STATE_NONE) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying identity %s", state->comment);
+ "Trying identity %s",
+ state->comment);
+ if (session->opts.identities_only) {
+ /* Check if this key is one of the keys listed in the config */
+ bool found_key = false;
+ for (i = 0; i < configKeysCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configKeys[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+ /* or in separate certificates */
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+
+ if (!found_key) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Identities only is enabled and identity %s was "
+ "not listed in config, skipping",
+ 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);
+
+ if (state->pubkey == NULL) {
+ rc = SSH_AUTH_DENIED;
+ }
+ continue;
+ }
+ }
}
if (state->state == SSH_AGENT_STATE_NONE ||
- state->state == SSH_AGENT_STATE_PUBKEY) {
+ state->state == SSH_AGENT_STATE_PUBKEY ||
+ state->state == SSH_AGENT_STATE_CERT) {
rc = ssh_userauth_try_publickey(session, username, state->pubkey);
if (rc == SSH_AUTH_ERROR) {
- ssh_agent_state_free (state);
+ ssh_agent_state_free(state);
session->agent_state = NULL;
- return rc;
+ goto done;
} else if (rc == SSH_AUTH_AGAIN) {
- state->state = SSH_AGENT_STATE_PUBKEY;
- return rc;
+ state->state = (state->state == SSH_AGENT_STATE_NONE ?
+ SSH_AGENT_STATE_PUBKEY : state->state);
+ goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s refused by server", state->comment);
+ "Public key of %s refused by server",
+ state->comment);
+ if (state->state == SSH_AGENT_STATE_PUBKEY) {
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Retry with matching certificate");
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_key_dup(configCerts[i]);
+ state->state = SSH_AGENT_STATE_CERT;
+ continue;
+ }
+ }
+ }
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_agent_get_next_ident(session,
+ &state->comment);
state->state = SSH_AGENT_STATE_NONE;
continue;
}
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s accepted by server", state->comment);
+ "Public key of %s accepted by server",
+ state->comment);
state->state = SSH_AGENT_STATE_AUTH;
}
if (state->state == SSH_AGENT_STATE_AUTH) {
rc = ssh_userauth_agent_publickey(session, username, state->pubkey);
- if (rc == SSH_AUTH_AGAIN)
- return rc;
+ if (rc == SSH_AUTH_AGAIN) {
+ goto done;
+ }
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);
+ ssh_agent_state_free(session->agent_state);
session->agent_state = NULL;
- return rc;
+ goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Server accepted public key but refused the signature");
- ssh_key_free(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_agent_get_next_ident(session,
+ &state->comment);
state->state = SSH_AGENT_STATE_NONE;
continue;
}
ssh_agent_state_free (session->agent_state);
session->agent_state = NULL;
- return SSH_AUTH_SUCCESS;
+ rc = SSH_AUTH_SUCCESS;
+ goto done;
}
}
ssh_agent_state_free (session->agent_state);
session->agent_state = NULL;
+done:
+ for (i = 0; i < configKeysCount; i++) {
+ ssh_key_free(configKeys[i]);
+ }
+ free(configKeys);
+ for (i = 0; i < configCertsCount; i++) {
+ ssh_key_free(configCerts[i]);
+ }
+ free(configCerts);
return rc;
}
-#endif
enum ssh_auth_auto_state_e {
SSH_AUTH_AUTO_STATE_NONE = 0,
SSH_AUTH_AUTO_STATE_PUBKEY,
SSH_AUTH_AUTO_STATE_KEY_IMPORTED,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION,
SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED
};
@@ -973,10 +1184,12 @@ struct ssh_auth_auto_state_struct {
struct ssh_iterator *it;
ssh_key privkey;
ssh_key pubkey;
+ ssh_key cert;
+ struct ssh_iterator *cert_it;
};
/**
- * @brief Get the identity that is currenly being processed by
+ * @brief Get the identity that is currently being processed by
* ssh_userauth_publickey_auto()
*
* This is meant to be used by a callback that happens as part of the
@@ -1007,7 +1220,8 @@ int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
return SSH_ERROR;
}
- if (session->auth.auto_state != NULL && session->auth.auto_state->it != NULL) {
+ if (session->auth.auto_state != NULL &&
+ session->auth.auto_state->it != NULL) {
id = session->auth.auto_state->it->data;
}
@@ -1051,6 +1265,9 @@ int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
* @note Most server implementations do not permit changing the username during
* authentication. The username should only be set with ssh_options_set() only
* before you connect to the server.
+ *
+ * The OpenSSH iterates over the identities and first try the plain public key
+ * and then the certificate if it is in place.
*/
int ssh_userauth_publickey_auto(ssh_session session,
const char *username,
@@ -1058,7 +1275,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
{
ssh_auth_callback auth_fn = NULL;
void *auth_data = NULL;
- struct ssh_auth_auto_state_struct *state;
+ struct ssh_auth_auto_state_struct *state = NULL;
int rc;
if (session == NULL) {
@@ -1085,15 +1302,13 @@ int ssh_userauth_publickey_auto(ssh_session session,
}
state = session->auth.auto_state;
if (state->state == SSH_AUTH_AUTO_STATE_NONE) {
-#ifndef _WIN32
/* Try authentication with ssh-agent first */
rc = ssh_userauth_agent(session, username);
if (rc == SSH_AUTH_SUCCESS ||
rc == SSH_AUTH_PARTIAL ||
- rc == SSH_AUTH_AGAIN ) {
+ rc == SSH_AUTH_AGAIN) {
return rc;
}
-#endif
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
}
if (state->it == NULL) {
@@ -1106,10 +1321,13 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying to authenticate with %s", privkey_file);
+ "Trying to authenticate with %s",
+ privkey_file);
+ state->cert = NULL;
state->privkey = NULL;
state->pubkey = NULL;
+#ifdef WITH_PKCS11_URI
if (ssh_pki_is_uri(privkey_file)) {
char *pub_uri_from_priv = NULL;
SSH_LOG(SSH_LOG_INFO,
@@ -1118,84 +1336,165 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (pub_uri_from_priv == NULL) {
return SSH_ERROR;
} else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s",
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s",
pub_uri_from_priv);
SAFE_FREE(pub_uri_from_priv);
}
- } else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file);
+ } else
+#endif /* WITH_PKCS11_URI */
+ {
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s.pub",
+ privkey_file);
}
rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey);
if (rc == SSH_ERROR) {
ssh_set_error(session,
- SSH_FATAL,
- "Failed to import public key: %s",
- pubkey_file);
+ SSH_FATAL,
+ "Failed to import public key: %s",
+ pubkey_file);
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_ERROR;
} else if (rc == SSH_EOF) {
/* Read the private key and save the public key to file */
rc = ssh_pki_import_privkey_file(privkey_file,
- passphrase,
- auth_fn,
- auth_data,
- &state->privkey);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ SSH_FATAL,
+ "Failed to read private key: %s",
+ privkey_file);
+ state->it = state->it->next;
continue;
} else if (rc == SSH_EOF) {
/* If the file doesn't exist, continue */
SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
- state->it=state->it->next;
+ state->it = state->it->next;
continue;
}
- rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey);
+ rc = ssh_pki_export_privkey_to_pubkey(state->privkey,
+ &state->pubkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->privkey);
+ SSH_KEY_FREE(state->privkey);
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_ERROR;
}
rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not write public key to file: %s",
pubkey_file);
}
}
state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED;
}
- if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
- rc = ssh_userauth_try_publickey(session, username, state->pubkey);
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ ssh_key k = state->pubkey;
+ if (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ k = state->cert;
+ }
+ rc = ssh_userauth_try_publickey(session, username, k);
if (rc == SSH_AUTH_ERROR) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Public key authentication error for %s",
privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
return rc;
} else if (rc == SSH_AUTH_AGAIN) {
return rc;
} else if (rc != SSH_AUTH_SUCCESS) {
+ int r; /* do not reuse `rc` as it is used to return from here */
+ SSH_KEY_FREE(state->cert);
SSH_LOG(SSH_LOG_DEBUG,
- "Public key for %s refused by server",
- privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
- state->it=state->it->next;
+ "Public key for %s%s refused by server",
+ privkey_file,
+ (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED
+ ? " (with certificate)" : ""));
+ /* Try certificate file by appending -cert.pub (if present) */
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ char cert_file[PATH_MAX] = {0};
+ ssh_key cert = NULL;
+
+ snprintf(cert_file,
+ sizeof(cert_file),
+ "%s-cert.pub",
+ privkey_file);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (default path)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ /* TODO check the pubkey and certs match */
+ SSH_LOG(SSH_LOG_TRACE,
+ "Certificate loaded %s. Retry the authentication.",
+ cert_file);
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE;
+ SSH_KEY_FREE(state->cert);
+ state->cert = cert;
+ /* try to authenticate with this certificate */
+ continue;
+ }
+ /* if the file does not exists, try configuration options */
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ }
+ /* Try certificate files loaded through options */
+ if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ SSH_KEY_FREE(state->cert);
+ if (state->cert_it == NULL) {
+ state->cert_it = ssh_list_get_iterator(session->opts.certificate);
+ }
+ while (state->cert_it != NULL) {
+ const char *cert_file = state->cert_it->data;
+ ssh_key cert = NULL;
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (options)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ int cmp = ssh_key_cmp(cert,
+ state->pubkey,
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp != 0) {
+ state->cert_it = state->cert_it->next;
+ SSH_KEY_FREE(cert);
+ continue; /* with next cert */
+ }
+ SSH_LOG(SSH_LOG_TRACE,
+ "Found matching certificate %s in options. Retry the authentication.",
+ cert_file);
+ state->cert = cert;
+ cert = NULL;
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ /* try to authenticate with this identity */
+ break; /* try this cert */
+ }
+ /* continue with next identity */
+ }
+ if (state->cert != NULL) {
+ continue; /* retry with the certificate */
+ }
+ }
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
}
@@ -1205,25 +1504,25 @@ int ssh_userauth_publickey_auto(ssh_session session,
/* Public key has been accepted by the server */
if (state->privkey == NULL) {
rc = ssh_pki_import_privkey_file(privkey_file,
- passphrase,
- auth_fn,
- auth_data,
- &state->privkey);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->pubkey);
- state->pubkey=NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ SSH_FATAL,
+ "Failed to read private key: %s",
+ privkey_file);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
} else if (rc == SSH_EOF) {
/* If the file doesn't exist, continue */
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
+ SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
state->it = state->it->next;
@@ -1231,16 +1530,33 @@ int ssh_userauth_publickey_auto(ssh_session session,
continue;
}
}
+ if (state->cert != NULL && !is_cert_type(state->privkey->cert_type)) {
+ rc = ssh_pki_copy_cert_to_privkey(state->cert, state->privkey);
+ if (rc != SSH_OK) {
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to copy cert to private key");
+ state->it = state->it->next;
+ state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
+ continue;
+ }
+ }
rc = ssh_userauth_publickey(session, username, state->privkey);
if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) {
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ bool cert_used = (state->cert != NULL);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
if (rc == SSH_AUTH_SUCCESS) {
- SSH_LOG(SSH_LOG_INFO,
- "Successfully authenticated using %s",
- privkey_file);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Successfully authenticated using %s%s",
+ privkey_file,
+ (cert_used ? " and certificate" : ""));
}
return rc;
}
@@ -1248,18 +1564,19 @@ int ssh_userauth_publickey_auto(ssh_session session,
return rc;
}
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_DEBUG,
"The server accepted the public key but refused the signature");
state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
/* continue */
}
}
- SSH_LOG(SSH_LOG_INFO,
- "Tried every public key, none matched");
+ SSH_LOG(SSH_LOG_WARN,
+ "Access denied: Tried every public key, none matched");
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_DENIED;
}
@@ -1298,10 +1615,11 @@ int ssh_userauth_publickey_auto(ssh_session session,
*/
int ssh_userauth_password(ssh_session session,
const char *username,
- const char *password) {
+ const char *password)
+{
int rc;
- switch(session->pending_call_state) {
+ switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_PASSWORD:
@@ -1359,7 +1677,6 @@ fail:
return SSH_AUTH_ERROR;
}
-#ifndef _WIN32
/* LEGACY */
int ssh_userauth_agent_pubkey(ssh_session session,
const char *username,
@@ -1376,20 +1693,26 @@ int ssh_userauth_agent_pubkey(ssh_session session,
key->type = publickey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PUBLIC;
- key->dsa = publickey->dsa_pub;
+#ifndef HAVE_LIBCRYPTO
key->rsa = publickey->rsa_pub;
+#else
+ key->key = publickey->key_pub;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_userauth_agent_publickey(session, username, key);
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return rc;
}
-#endif /* _WIN32 */
-ssh_kbdint ssh_kbdint_new(void) {
+ssh_kbdint ssh_kbdint_new(void)
+{
ssh_kbdint kbd;
kbd = calloc(1, sizeof(struct ssh_kbdint_struct));
@@ -1401,7 +1724,8 @@ ssh_kbdint ssh_kbdint_new(void) {
}
-void ssh_kbdint_free(ssh_kbdint kbd) {
+void ssh_kbdint_free(ssh_kbdint kbd)
+{
size_t i, n;
if (kbd == NULL) {
@@ -1437,7 +1761,8 @@ void ssh_kbdint_free(ssh_kbdint kbd) {
SAFE_FREE(kbd);
}
-void ssh_kbdint_clean(ssh_kbdint kbd) {
+void ssh_kbdint_clean(ssh_kbdint kbd)
+{
size_t i, n;
if (kbd == NULL) {
@@ -1637,10 +1962,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
}
SSH_LOG(SSH_LOG_DEBUG,
- "%d keyboard-interactive prompts", nprompts);
+ "%" PRIu32 " keyboard-interactive prompts", nprompts);
if (nprompts > KBDINT_MAX_PROMPT) {
ssh_set_error(session, SSH_FATAL,
- "Too much prompts requested by the server: %u (0x%.4x)",
+ "Too much prompts requested by the server: %" PRIu32 " (0x%.4" PRIx32 ")",
nprompts, nprompts);
ssh_kbdint_free(session->kbdint);
session->kbdint = NULL;
@@ -1715,7 +2040,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
* @see ssh_userauth_kbdint_setanswer()
*/
int ssh_userauth_kbdint(ssh_session session, const char *user,
- const char *submethods) {
+ const char *submethods)
+{
int rc = SSH_AUTH_ERROR;
if (session == NULL) {
@@ -1757,7 +2083,8 @@ int ssh_userauth_kbdint(ssh_session session, const char *user,
*
* @returns The number of prompts.
*/
-int ssh_userauth_kbdint_getnprompts(ssh_session session) {
+int ssh_userauth_kbdint_getnprompts(ssh_session session)
+{
if (session == NULL) {
return SSH_ERROR;
}
@@ -1779,7 +2106,8 @@ int ssh_userauth_kbdint_getnprompts(ssh_session session) {
*
* @returns The name of the message block. Do not free it.
*/
-const char *ssh_userauth_kbdint_getname(ssh_session session) {
+const char *ssh_userauth_kbdint_getname(ssh_session session)
+{
if (session == NULL) {
return NULL;
}
@@ -1802,7 +2130,8 @@ const char *ssh_userauth_kbdint_getname(ssh_session session) {
* @returns The instruction of the message block.
*/
-const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
+const char *ssh_userauth_kbdint_getinstruction(ssh_session session)
+{
if (session == NULL)
return NULL;
if (session->kbdint == NULL) {
@@ -1837,8 +2166,9 @@ const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
* if (echo) ...
* @endcode
*/
-const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
- char *echo) {
+const char *
+ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo)
+{
if (session == NULL)
return NULL;
if (session->kbdint == NULL) {
@@ -1865,7 +2195,8 @@ const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
*
* @returns The number of answers.
*/
-int ssh_userauth_kbdint_getnanswers(ssh_session session) {
+int ssh_userauth_kbdint_getnanswers(ssh_session session)
+{
if (session == NULL || session->kbdint == NULL) {
return SSH_ERROR;
}
@@ -1873,15 +2204,17 @@ int ssh_userauth_kbdint_getnanswers(ssh_session session) {
}
/**
- * @brief Get the answer for a question from a message block.
+ * @brief Get the answer to a question from a message block.
*
* @param[in] session The ssh session to use.
*
* @param[in] i index The number of the ith answer.
*
- * @return 0 on success, < 0 on error.
+ * @return The answer string, or NULL if the answer is not
+ * available. Do not free the string.
*/
-const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
+const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i)
+{
if (session == NULL || session->kbdint == NULL
|| session->kbdint->answers == NULL) {
return NULL;
@@ -1912,8 +2245,10 @@ const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
*
* @return 0 on success, < 0 on error.
*/
-int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
- const char *answer) {
+int
+ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
+ const char *answer)
+{
if (session == NULL) {
return -1;
}
@@ -1959,7 +2294,8 @@ int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
* SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again
* later.
*/
-int ssh_userauth_gssapi(ssh_session session) {
+int ssh_userauth_gssapi(ssh_session session)
+{
int rc = SSH_AUTH_DENIED;
#ifdef WITH_GSSAPI
switch(session->pending_call_state) {
@@ -1981,7 +2317,7 @@ int ssh_userauth_gssapi(ssh_session session) {
} else if (rc == SSH_ERROR) {
return SSH_AUTH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi-with-mic");
+ SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-with-mic");
session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_MIC;
session->auth.state = SSH_AUTH_STATE_NONE;
diff --git a/src/bignum.c b/src/bignum.c
index ef8de31f..72429460 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -44,7 +44,7 @@ ssh_string ssh_make_bignum_string(bignum num) {
#ifdef DEBUG_CRYPTO
SSH_LOG(SSH_LOG_TRACE,
- "%zu bits, %zu bytes, %zu padding\n",
+ "%zu bits, %zu bytes, %zu padding",
bits, len, pad);
#endif /* DEBUG_CRYPTO */
@@ -70,7 +70,7 @@ bignum ssh_make_string_bn(ssh_string string)
#ifdef DEBUG_CRYPTO
SSH_LOG(SSH_LOG_TRACE,
- "Importing a %zu bits, %zu bytes object ...\n",
+ "Importing a %zu bits, %zu bytes object ...",
len * 8, len);
#endif /* DEBUG_CRYPTO */
@@ -86,12 +86,7 @@ void ssh_print_bignum(const char *name, const_bignum num)
if (num != NULL) {
bignum_bn2hex(num, &hex);
}
- fprintf(stderr, "%s value: %s\n", name, (hex == NULL) ? "(null)" : (char *) hex);
-#ifdef HAVE_LIBGCRYPT
- SAFE_FREE(hex);
-#elif defined HAVE_LIBCRYPTO
- OPENSSL_free(hex);
-#elif defined HAVE_LIBMBEDCRYPTO
- SAFE_FREE(hex);
-#endif
+ SSH_LOG(SSH_LOG_DEBUG, "%s value: %s", name,
+ (hex == NULL) ? "(null)" : (char *)hex);
+ ssh_crypto_free(hex);
}
diff --git a/src/bind.c b/src/bind.c
index 691a363f..c2917865 100644
--- a/src/bind.c
+++ b/src/bind.c
@@ -79,6 +79,7 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
int opt = 1;
socket_t s;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ZERO_STRUCT(hints);
@@ -98,7 +99,8 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
ai->ai_socktype,
ai->ai_protocol);
if (s == SSH_INVALID_SOCKET) {
- ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno));
+ ssh_set_error(sshbind, SSH_FATAL, "%s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
freeaddrinfo (ai);
return -1;
}
@@ -108,7 +110,7 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
ssh_set_error(sshbind,
SSH_FATAL,
"Setting socket options failed: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
freeaddrinfo (ai);
CLOSE_SOCKET(s);
return -1;
@@ -120,7 +122,7 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
"Binding to %s:%d: %s",
hostname,
port,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
freeaddrinfo (ai);
CLOSE_SOCKET(s);
return -1;
@@ -147,15 +149,6 @@ ssh_bind ssh_bind_new(void) {
static int ssh_bind_import_keys(ssh_bind sshbind) {
int rc;
- if (sshbind->ecdsakey == NULL &&
- sshbind->dsakey == NULL &&
- sshbind->rsakey == NULL &&
- sshbind->ed25519key == NULL) {
- ssh_set_error(sshbind, SSH_FATAL,
- "ECDSA, ED25519, DSA, or RSA host key file must be set");
- return SSH_ERROR;
- }
-
#ifdef HAVE_ECC
if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
@@ -179,30 +172,6 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
}
#endif
-#ifdef HAVE_DSA
- if (sshbind->dsa == NULL && sshbind->dsakey != NULL) {
- rc = ssh_pki_import_privkey_file(sshbind->dsakey,
- NULL,
- NULL,
- NULL,
- &sshbind->dsa);
- if (rc == SSH_ERROR || rc == SSH_EOF) {
- ssh_set_error(sshbind, SSH_FATAL,
- "Failed to import private DSA host key");
- return SSH_ERROR;
- }
-
- if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) {
- ssh_set_error(sshbind, SSH_FATAL,
- "The DSA host key has the wrong type: %d",
- ssh_key_type(sshbind->dsa));
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
- return SSH_ERROR;
- }
- }
-#endif
-
if (sshbind->rsa == NULL && sshbind->rsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->rsakey,
NULL,
@@ -249,97 +218,124 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
}
int ssh_bind_listen(ssh_bind sshbind) {
- const char *host;
- socket_t fd;
- int rc;
+ const char *host;
+ socket_t fd;
+ int rc;
- if (sshbind->rsa == NULL &&
- sshbind->dsa == NULL &&
- sshbind->ecdsa == NULL &&
- sshbind->ed25519 == NULL) {
- rc = ssh_bind_import_keys(sshbind);
- if (rc != SSH_OK) {
- return SSH_ERROR;
- }
- }
+ /* Apply global bind configurations, if it hasn't been applied before */
+ rc = ssh_bind_options_parse_config(sshbind, NULL);
+ if (rc != 0) {
+ ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
+ return SSH_ERROR;
+ }
- if (sshbind->bindfd == SSH_INVALID_SOCKET) {
- host = sshbind->bindaddr;
- if (host == NULL) {
- host = "0.0.0.0";
- }
+ /* Set default hostkey paths if no hostkey was found before */
+ if (sshbind->ecdsakey == NULL &&
+ sshbind->rsakey == NULL &&
+ sshbind->ed25519key == NULL) {
- fd = bind_socket(sshbind, host, sshbind->bindport);
- if (fd == SSH_INVALID_SOCKET) {
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
- ssh_key_free(sshbind->rsa);
- sshbind->rsa = NULL;
- /* XXX should this clear also other structures that were allocated */
- return -1;
- }
+ sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
+ sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
+ sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
+ }
- if (listen(fd, 10) < 0) {
- ssh_set_error(sshbind, SSH_FATAL,
- "Listening to socket %d: %s",
- fd, strerror(errno));
- CLOSE_SOCKET(fd);
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
- ssh_key_free(sshbind->rsa);
- sshbind->rsa = NULL;
- /* XXX should this clear also other structures that were allocated */
- return -1;
- }
+ /* Apply global bind configurations, if it hasn't been applied before */
+ rc = ssh_bind_options_parse_config(sshbind, NULL);
+ if (rc != 0) {
+ ssh_set_error(sshbind, SSH_FATAL, "Could not parse global config");
+ return SSH_ERROR;
+ }
- sshbind->bindfd = fd;
+ /* Set default hostkey paths if no hostkey was found before */
+ if (sshbind->ecdsakey == NULL &&
+ sshbind->rsakey == NULL &&
+ sshbind->ed25519key == NULL) {
+
+ sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
+ sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
+ sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
+ }
+
+ if (sshbind->rsa == NULL &&
+ sshbind->ecdsa == NULL &&
+ sshbind->ed25519 == NULL) {
+ rc = ssh_bind_import_keys(sshbind);
+ if (rc != SSH_OK) {
+ return SSH_ERROR;
+ }
+ }
+
+ if (sshbind->bindfd == SSH_INVALID_SOCKET) {
+ host = sshbind->bindaddr;
+ if (host == NULL) {
+ host = "0.0.0.0";
+ }
+
+ fd = bind_socket(sshbind, host, sshbind->bindport);
+ if (fd == SSH_INVALID_SOCKET) {
+ return SSH_ERROR;
+ }
+
+ if (listen(fd, 10) < 0) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Listening to socket %d: %s",
+ fd,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
+ return SSH_ERROR;
+ }
+
+ sshbind->bindfd = fd;
} else {
- SSH_LOG(SSH_LOG_INFO, "Using app-provided bind socket");
+ SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
}
return 0;
}
-int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks,
- void *userdata){
- if (sshbind == NULL) {
- return SSH_ERROR;
- }
- if (callbacks == NULL) {
- ssh_set_error_invalid(sshbind);
- return SSH_ERROR;
- }
- if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){
- ssh_set_error(sshbind,SSH_FATAL,
- "Invalid callback passed in (badly initialized)");
- return SSH_ERROR;
- }
- sshbind->bind_callbacks = callbacks;
- sshbind->bind_callbacks_userdata=userdata;
- return 0;
+int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, void *userdata)
+{
+ if (sshbind == NULL) {
+ return SSH_ERROR;
+ }
+ if (callbacks == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return SSH_ERROR;
+ }
+ if (callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)) {
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Invalid callback passed in (badly initialized)");
+ return SSH_ERROR;
+ }
+ sshbind->bind_callbacks = callbacks;
+ sshbind->bind_callbacks_userdata = userdata;
+ return 0;
}
/** @internal
* @brief callback being called by poll when an event happens
*
*/
-static int ssh_bind_poll_callback(ssh_poll_handle sshpoll,
- socket_t fd, int revents, void *user){
- ssh_bind sshbind=(ssh_bind)user;
- (void)sshpoll;
- (void)fd;
-
- if(revents & POLLIN){
- /* new incoming connection */
- if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){
- sshbind->bind_callbacks->incoming_connection(sshbind,
- sshbind->bind_callbacks_userdata);
+static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, socket_t fd, int revents, void *user)
+{
+ ssh_bind sshbind = (ssh_bind)user;
+ (void)sshpoll;
+ (void)fd;
+
+ if (revents & POLLIN) {
+ /* new incoming connection */
+ if (ssh_callbacks_exists(sshbind->bind_callbacks, incoming_connection)) {
+ sshbind->bind_callbacks->incoming_connection(sshbind,
+ sshbind->bind_callbacks_userdata);
+ }
}
- }
- return 0;
+ return 0;
}
/** @internal
- * @brief returns the current poll handle, or create it
+ * @brief returns the current poll handle, or creates it
* @param sshbind the ssh_bind object
* @returns a ssh_poll handle suitable for operation
*/
@@ -363,20 +359,24 @@ ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind)
return sshbind->poll;
}
-void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) {
- sshbind->blocking = blocking ? 1 : 0;
+void ssh_bind_set_blocking(ssh_bind sshbind, int blocking)
+{
+ sshbind->blocking = blocking ? 1 : 0;
}
-socket_t ssh_bind_get_fd(ssh_bind sshbind) {
- return sshbind->bindfd;
+socket_t ssh_bind_get_fd(ssh_bind sshbind)
+{
+ return sshbind->bindfd;
}
-void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) {
- sshbind->bindfd = fd;
+void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd)
+{
+ sshbind->bindfd = fd;
}
-void ssh_bind_fd_toaccept(ssh_bind sshbind) {
- sshbind->toaccept = 1;
+void ssh_bind_fd_toaccept(ssh_bind sshbind)
+{
+ sshbind->toaccept = 1;
}
void ssh_bind_free(ssh_bind sshbind){
@@ -398,13 +398,10 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind->config_dir);
SAFE_FREE(sshbind->pubkey_accepted_key_types);
- SAFE_FREE(sshbind->dsakey);
SAFE_FREE(sshbind->rsakey);
SAFE_FREE(sshbind->ecdsakey);
SAFE_FREE(sshbind->ed25519key);
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
ssh_key_free(sshbind->ecdsa);
@@ -421,7 +418,9 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind);
}
-int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
+int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
+{
+ ssh_poll_handle handle = NULL;
int i, rc;
if (sshbind == NULL) {
@@ -433,13 +432,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
return SSH_ERROR;
}
- /* Apply global bind configurations, if it hasn't been applied before */
- rc = ssh_bind_options_parse_config(sshbind, NULL);
- if (rc != 0) {
- ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
- return SSH_ERROR;
- }
-
session->server = 1;
/* Copy options from bind to session */
@@ -503,6 +495,8 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
}
}
+ session->opts.rsa_min_size = sshbind->rsa_min_size;
+
ssh_socket_free(session->socket);
session->socket = ssh_socket_new(session);
if (session->socket == NULL) {
@@ -511,7 +505,12 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
return SSH_ERROR;
}
ssh_socket_set_fd(session->socket, fd);
- ssh_socket_get_poll_handle(session->socket);
+ handle = ssh_socket_get_poll_handle(session->socket);
+ if (handle == NULL) {
+ ssh_set_error_oom(sshbind);
+ return SSH_ERROR;
+ }
+ ssh_socket_set_connected(session->socket, handle);
/* We must try to import any keys that could be imported in case
* we are not using ssh_bind_listen (which is the other place
@@ -519,7 +518,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
* only using ssh_bind_accept_fd to manage sockets ourselves.
*/
if (sshbind->rsa == NULL &&
- sshbind->dsa == NULL &&
sshbind->ecdsa == NULL &&
sshbind->ed25519 == NULL) {
rc = ssh_bind_import_keys(sshbind);
@@ -537,15 +535,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
}
}
#endif
-#ifdef HAVE_DSA
- if (sshbind->dsa) {
- session->srv.dsa_key = ssh_key_dup(sshbind->dsa);
- if (session->srv.dsa_key == NULL) {
- ssh_set_error_oom(sshbind);
- return SSH_ERROR;
- }
- }
-#endif
if (sshbind->rsa) {
session->srv.rsa_key = ssh_key_dup(sshbind->rsa);
if (session->srv.rsa_key == NULL) {
@@ -584,9 +573,16 @@ int ssh_bind_accept(ssh_bind sshbind, ssh_session session)
fd = accept(sshbind->bindfd, NULL, NULL);
if (fd == SSH_INVALID_SOCKET) {
- ssh_set_error(sshbind, SSH_FATAL,
- "Accepting a new connection: %s",
- strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ if (errno == EINTR) {
+ ssh_set_error(sshbind, SSH_EINTR,
+ "Accepting a new connection (child signal error): %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ } else {
+ ssh_set_error(sshbind, SSH_FATAL,
+ "Accepting a new connection: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ }
return SSH_ERROR;
}
rc = ssh_bind_accept_fd(sshbind, session, fd);
diff --git a/src/bind_config.c b/src/bind_config.c
index ace5a0ac..a4c7a8d7 100644
--- a/src/bind_config.c
+++ b/src/bind_config.c
@@ -189,18 +189,29 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *line,
unsigned int count,
uint32_t *parser_flags,
- uint8_t *seen);
-
-static void local_parse_file(ssh_bind bind,
- const char *filename,
- uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth);
+
+#define LIBSSH_BIND_CONF_MAX_DEPTH 16
+static void
+local_parse_file(ssh_bind bind,
+ const char *filename,
+ uint32_t *parser_flags,
+ uint8_t *seen,
+ unsigned int depth)
{
FILE *f;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
+ if (depth > LIBSSH_BIND_CONF_MAX_DEPTH) {
+ ssh_set_error(bind, SSH_FATAL,
+ "ERROR - Too many levels of configuration includes "
+ "when processing file '%s'", filename);
+ return;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
@@ -213,7 +224,7 @@ static void local_parse_file(ssh_bind bind,
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen);
+ rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen, depth);
if (rv < 0) {
fclose(f);
return;
@@ -228,7 +239,8 @@ static void local_parse_file(ssh_bind bind,
static void local_parse_glob(ssh_bind bind,
const char *fileglob,
uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth)
{
glob_t globbuf = {
.gl_flags = 0,
@@ -248,7 +260,7 @@ static void local_parse_glob(ssh_bind bind,
}
for (i = 0; i < globbuf.gl_pathc; i++) {
- local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen);
+ local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen, depth);
}
globfree(&globbuf);
@@ -274,7 +286,8 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *line,
unsigned int count,
uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth)
{
enum ssh_bind_config_opcode_e opcode;
const char *p = NULL;
@@ -288,7 +301,12 @@ ssh_bind_config_parse_line(ssh_bind bind,
return -1;
}
- if ((line == NULL) || (parser_flags == NULL)) {
+ /* Ignore empty lines */
+ if (line == NULL || *line == '\0') {
+ return 0;
+ }
+
+ if (parser_flags == NULL) {
ssh_set_error_invalid(bind);
return -1;
}
@@ -333,9 +351,9 @@ ssh_bind_config_parse_line(ssh_bind bind,
p = ssh_config_get_str_tok(&s, NULL);
if (p && (*parser_flags & PARSING)) {
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
- local_parse_glob(bind, p, parser_flags, seen);
+ local_parse_glob(bind, p, parser_flags, seen, depth + 1);
#else
- local_parse_file(bind, p, parser_flags, seen);
+ local_parse_file(bind, p, parser_flags, seen, depth + 1);
#endif /* HAVE_GLOB */
}
break;
@@ -345,7 +363,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HOSTKEY, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set Hostkey value '%s'",
count, p);
}
@@ -356,7 +374,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set ListenAddress value '%s'",
count, p);
}
@@ -367,7 +385,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT_STR, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set Port value '%s'",
count, p);
}
@@ -378,7 +396,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_C_S, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set C->S Ciphers value '%s'",
count, p);
break;
@@ -386,7 +404,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_S_C, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set S->C Ciphers value '%s'",
count, p);
}
@@ -397,7 +415,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_C_S, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set C->S MAC value '%s'",
count, p);
break;
@@ -405,7 +423,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_S_C, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set S->C MAC value '%s'",
count, p);
}
@@ -419,10 +437,10 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (strcasecmp(p, "quiet") == 0) {
value = SSH_LOG_NONE;
} else if (strcasecmp(p, "fatal") == 0 ||
- strcasecmp(p, "error")== 0 ||
- strcasecmp(p, "info") == 0) {
+ strcasecmp(p, "error")== 0) {
value = SSH_LOG_WARN;
- } else if (strcasecmp(p, "verbose") == 0) {
+ } else if (strcasecmp(p, "verbose") == 0 ||
+ strcasecmp(p, "info") == 0) {
value = SSH_LOG_INFO;
} else if (strcasecmp(p, "DEBUG") == 0 ||
strcasecmp(p, "DEBUG1") == 0) {
@@ -435,7 +453,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY,
&value);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set LogLevel value '%s'",
count, p);
}
@@ -447,7 +465,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_KEY_EXCHANGE, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set KexAlgorithms value '%s'",
count, p);
}
@@ -522,13 +540,13 @@ ssh_bind_config_parse_line(ssh_bind bind,
/* Skip one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
- SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument\n", count, p2);
SAFE_FREE(x);
return -1;
}
args++;
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_DEBUG,
"line %d: Unsupported Match keyword '%s', ignoring\n",
count,
p2);
@@ -558,7 +576,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind,
SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set PubKeyAcceptedKeyTypes value '%s'",
count, p);
}
@@ -570,26 +588,26 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind,
SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set HostkeyAlgorithms value '%s'",
count, p);
}
}
break;
case BIND_CFG_NOT_ALLOWED_IN_MATCH:
- SSH_LOG(SSH_LOG_WARN, "Option not allowed in Match block: %s, line: %d",
+ SSH_LOG(SSH_LOG_DEBUG, "Option not allowed in Match block: %s, line: %d",
keyword, count);
break;
case BIND_CFG_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
keyword, count);
break;
case BIND_CFG_UNSUPPORTED:
- SSH_LOG(SSH_LOG_WARN, "Unsupported option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported option: %s, line: %d",
keyword, count);
break;
case BIND_CFG_NA:
- SSH_LOG(SSH_LOG_WARN, "Option not applicable: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Option not applicable: %s, line: %d",
keyword, count);
break;
default:
@@ -628,7 +646,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
parser_flags = PARSING;
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen);
+ rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen, 0);
if (rv) {
fclose(f);
return -1;
@@ -638,3 +656,64 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
fclose(f);
return 0;
}
+
+/* @brief Parse configuration string and set the options to the given bind session
+ *
+ * @params[in] bind The ssh bind session
+ * @params[in] input Null terminated string containing the configuration
+ *
+ * @returns SSH_OK on successful parsing the configuration string,
+ * SSH_ERROR on error
+ */
+int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
+{
+ char line[MAX_LINE_SIZE] = {0};
+ const char *c = input, *line_start = input;
+ unsigned int line_num = 0, line_len;
+ uint32_t parser_flags;
+ int rv;
+
+ /* This local table is used during the parsing of the current file (and
+ * files included recursively in this file) to prevent an option to be
+ * redefined, i.e. the first value set is kept. But this DO NOT prevent the
+ * option to be redefined later by another file. */
+ uint8_t seen[BIND_CFG_MAX] = {0};
+
+ SSH_LOG(SSH_LOG_DEBUG, "Reading bind configuration data from string:");
+ SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input);
+
+ parser_flags = PARSING;
+ while (1) {
+ line_num++;
+ line_start = c;
+ c = strchr(line_start, '\n');
+ if (c == NULL) {
+ /* if there is no newline at the end of the string */
+ c = strchr(line_start, '\0');
+ }
+ if (c == NULL) {
+ /* should not happen, would mean a string without trailing '\0' */
+ SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string");
+ return SSH_ERROR;
+ }
+ line_len = c - line_start;
+ if (line_len > MAX_LINE_SIZE - 1) {
+ SSH_LOG(SSH_LOG_WARN, "Line %u too long: %u characters",
+ line_num, line_len);
+ return SSH_ERROR;
+ }
+ memcpy(line, line_start, line_len);
+ line[line_len] = '\0';
+ SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
+ rv = ssh_bind_config_parse_line(bind, line, line_num, &parser_flags, seen, 0);
+ if (rv < 0) {
+ return SSH_ERROR;
+ }
+ if (*c == '\0') {
+ break;
+ }
+ c++;
+ }
+
+ return SSH_OK;
+}
diff --git a/src/buffer.c b/src/buffer.c
index f309215a..299eac54 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -26,6 +26,7 @@
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <stdio.h>
#ifndef _WIN32
#include <netinet/in.h>
@@ -46,9 +47,9 @@
*/
struct ssh_buffer_struct {
bool secure;
- size_t used;
- size_t allocated;
- size_t pos;
+ uint32_t used;
+ uint32_t allocated;
+ uint32_t pos;
uint8_t *data;
};
@@ -56,7 +57,7 @@ struct ssh_buffer_struct {
#define BUFFER_SIZE_MAX 0x10000000
/**
- * @defgroup libssh_buffer The SSH buffer functions.
+ * @defgroup libssh_buffer The SSH buffer functions
* @ingroup libssh
*
* Functions to handle SSH buffers.
@@ -83,21 +84,21 @@ static void buffer_verify(ssh_buffer buf)
if (buf->used > buf->allocated) {
fprintf(stderr,
- "BUFFER ERROR: allocated %zu, used %zu\n",
+ "BUFFER ERROR: allocated %u, used %u\n",
buf->allocated,
buf->used);
do_abort = true;
}
if (buf->pos > buf->used) {
fprintf(stderr,
- "BUFFER ERROR: position %zu, used %zu\n",
+ "BUFFER ERROR: position %u, used %u\n",
buf->pos,
buf->used);
do_abort = true;
}
if (buf->pos > buf->allocated) {
fprintf(stderr,
- "BUFFER ERROR: position %zu, allocated %zu\n",
+ "BUFFER ERROR: position %u, allocated %u\n",
buf->pos,
buf->allocated);
do_abort = true;
@@ -129,7 +130,7 @@ struct ssh_buffer_struct *ssh_buffer_new(void)
/*
* Always preallocate 64 bytes.
*
- * -1 for ralloc_buffer magic.
+ * -1 for realloc_buffer magic.
*/
rc = ssh_buffer_allocate_size(buf, 64 - 1);
if (rc != 0) {
@@ -178,9 +179,9 @@ void ssh_buffer_set_secure(ssh_buffer buffer)
buffer->secure = true;
}
-static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed)
+static int realloc_buffer(struct ssh_buffer_struct *buffer, uint32_t needed)
{
- size_t smallest = 1;
+ uint32_t smallest = 1;
uint8_t *new = NULL;
buffer_verify(buffer);
@@ -693,7 +694,7 @@ uint32_t ssh_buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint3
/**
* @internal
*
- * @brief Get a 8 bits unsigned int out of the buffer and adjusts the read
+ * @brief Get a 8 bits unsigned int out of the buffer and adjust the read
* pointer.
*
* @param[in] buffer The buffer to read.
@@ -702,7 +703,7 @@ uint32_t ssh_buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint3
*
* @returns 0 if there is not enough data in buffer, 1 otherwise.
*/
-int ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
+uint32_t ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
return ssh_buffer_get_data(buffer,data,sizeof(uint8_t));
}
@@ -717,7 +718,7 @@ int ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
*
* @returns 0 if there is not enough data in buffer, 4 otherwise.
*/
-int ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
+uint32_t ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
return ssh_buffer_get_data(buffer,data,sizeof(uint32_t));
}
/**
@@ -732,7 +733,7 @@ int ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
*
* @returns 0 if there is not enough data in buffer, 8 otherwise.
*/
-int ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
+uint32_t ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
return ssh_buffer_get_data(buffer,data,sizeof(uint64_t));
}
@@ -747,7 +748,8 @@ int ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
*/
int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len)
{
- if (buffer->pos + len < len || buffer->pos + len > buffer->used) {
+ if (buffer == NULL || buffer->pos + len < len ||
+ buffer->pos + len > buffer->used) {
return SSH_ERROR;
}
@@ -757,7 +759,7 @@ int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len)
/**
* @internal
*
- * @brief Get a SSH String out of the buffer and adjusts the read pointer.
+ * @brief Get an SSH String out of the buffer and adjust the read pointer.
*
* @param[in] buffer The buffer to read.
*
@@ -868,7 +870,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
va_arg(ap, bignum);
/*
* Use a fixed size for a bignum
- * (they should normaly be around 32)
+ * (they should normally be around 32)
*/
needed_size += 64;
break;
@@ -878,7 +880,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
cstring = NULL;
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
rc = SSH_ERROR;
}
if (rc != SSH_OK){
@@ -900,7 +902,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
}
}
- rc = ssh_buffer_allocate_size(buffer, needed_size);
+ rc = ssh_buffer_allocate_size(buffer, (uint32_t)needed_size);
if (rc != 0) {
return SSH_ERROR;
}
@@ -1007,7 +1009,7 @@ int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
cstring = NULL;
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
rc = SSH_ERROR;
}
if (rc != SSH_OK){
@@ -1102,7 +1104,8 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
bignum *bignum;
void **data;
} o;
- size_t len, rlen, max_len;
+ size_t len;
+ uint32_t rlen, max_len;
ssh_string tmp_string = NULL;
va_list ap_copy;
size_t count;
@@ -1238,7 +1241,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
rc = SSH_OK;
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
}
if (rc != SSH_OK) {
break;
diff --git a/src/chachapoly.c b/src/chachapoly.c
index c4827fd8..2cd23854 100644
--- a/src/chachapoly.c
+++ b/src/chachapoly.c
@@ -65,7 +65,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
/**
* @internal
*
- * @brief encrypts an outgoing packet with chacha20 and authenticate it
+ * @brief encrypts an outgoing packet with chacha20 and authenticates it
* with poly1305.
*/
static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
diff --git a/src/channels.c b/src/channels.c
index 112a38f9..9e613715 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -29,6 +29,7 @@
#include <errno.h>
#include <time.h>
#include <stdbool.h>
+#include <string.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
@@ -51,16 +52,19 @@
#include "libssh/server.h"
#endif
-#define WINDOWBASE 1280000
-#define WINDOWLIMIT (WINDOWBASE/2)
-
/*
* All implementations MUST be able to process packets with an
* uncompressed payload length of 32768 bytes or less and a total packet
* size of 35000 bytes or less.
*/
#define CHANNEL_MAX_PACKET 32768
-#define CHANNEL_INITIAL_WINDOW 64000
+
+/*
+ * WINDOW_DEFAULT matches the default OpenSSH session window size.
+ * This controls how much data the peer can send before needing to receive
+ * a round-trip SSH2_MSG_CHANNEL_WINDOW_ADJUST message that increases the window.
+ */
+#define WINDOW_DEFAULT (64*CHANNEL_MAX_PACKET)
/**
* @defgroup libssh_channel The SSH channel functions
@@ -79,6 +83,9 @@ static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet);
* @param[in] session The ssh session to use.
*
* @return A pointer to a newly allocated channel, NULL on error.
+ * The channel needs to be freed with ssh_channel_free().
+ *
+ * @see ssh_channel_free()
*/
ssh_channel ssh_channel_new(ssh_session session)
{
@@ -147,8 +154,9 @@ ssh_channel ssh_channel_new(ssh_session session)
*
* @return The new channel identifier.
*/
-uint32_t ssh_channel_new_id(ssh_session session) {
- return ++(session->maxchannel);
+uint32_t ssh_channel_new_id(ssh_session session)
+{
+ return ++(session->maxchannel);
}
/**
@@ -173,7 +181,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
channel=ssh_channel_from_local(session,channelid);
if(channel==NULL){
ssh_set_error(session, SSH_FATAL,
- "Unknown channel id %"PRIu32,
+ "Unknown channel id %" PRIu32,
(uint32_t) channelid);
/* TODO: Set error marking in channel object */
@@ -187,8 +195,8 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
if (rc != SSH_OK)
goto error;
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Received a CHANNEL_OPEN_CONFIRMATION for channel %" PRIu32 ":%" PRIu32,
channel->local_channel,
channel->remote_channel);
@@ -200,13 +208,21 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Remote window : %"PRIu32", maxpacket : %"PRIu32,
- (uint32_t) channel->remote_window,
- (uint32_t) channel->remote_maxpacket);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Remote window : %" PRIu32 ", maxpacket : %" PRIu32,
+ channel->remote_window,
+ channel->remote_maxpacket);
channel->state = SSH_CHANNEL_STATE_OPEN;
channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_open_response_function,
+ channel->session,
+ channel,
+ true /* is_success */);
+
return SSH_PACKET_USED;
error:
@@ -245,16 +261,25 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
"SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
"state %d",
channel->state);
+ SAFE_FREE(error);
goto error;
}
ssh_set_error(session, SSH_REQUEST_DENIED,
- "Channel opening failure: channel %u error (%"PRIu32") %s",
+ "Channel opening failure: channel %" PRIu32 " error (%" PRIu32 ") %s",
channel->local_channel,
- (uint32_t) code,
+ code,
error);
SAFE_FREE(error);
channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_open_response_function,
+ channel->session,
+ channel,
+ false /* is_success */);
+
return SSH_PACKET_USED;
error:
@@ -262,7 +287,8 @@ error:
return SSH_PACKET_USED;
}
-static int ssh_channel_open_termination(void *c){
+static int ssh_channel_open_termination(void *c)
+{
ssh_channel channel = (ssh_channel) c;
if (channel->state != SSH_CHANNEL_STATE_OPENING ||
channel->session->session_state == SSH_SESSION_STATE_ERROR)
@@ -320,8 +346,8 @@ channel_open(ssh_channel channel,
channel->local_maxpacket = maxpacket;
channel->local_window = window;
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Creating a channel %d with %d window and %d max packet",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Creating a channel %" PRIu32 " with %" PRIu32 " window and %" PRIu32 " max packet",
channel->local_channel, window, maxpacket);
rc = ssh_buffer_pack(session->out_buffer,
@@ -349,7 +375,7 @@ channel_open(ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d",
+ "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %" PRIu32,
type, channel->local_channel);
pending:
@@ -369,7 +395,7 @@ end:
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 */
+ /* Messages were handled correctly, but the channel state is invalid */
err = SSH_ERROR;
}
@@ -396,35 +422,46 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
/**
* @internal
- * @brief grows the local window and send a packet to the other party
+ * @brief grows the local window and sends a packet to the other party
* @param session SSH session
* @param channel SSH channel
- * @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,
- uint32_t minimumsize)
+ ssh_channel channel)
{
- uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE;
+ uint32_t used;
+ uint32_t increment;
int rc;
- if(new_window <= channel->local_window){
- SSH_LOG(SSH_LOG_PROTOCOL,
- "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)",
- channel->local_channel, channel->remote_channel, new_window,
+ /* Calculate the increment taking into account what the peer may still send
+ * (local_window) and what we've already buffered (stdout_buffer and
+ * stderr_buffer).
+ */
+ used = channel->local_window;
+ if (channel->stdout_buffer != NULL) {
+ used += ssh_buffer_get_len(channel->stdout_buffer);
+ }
+ if (channel->stderr_buffer != NULL) {
+ used += ssh_buffer_get_len(channel->stderr_buffer);
+ }
+ /* Avoid a negative increment in case the peer sent more than the window allowed */
+ increment = WINDOW_DEFAULT > used ? WINDOW_DEFAULT - used : 0;
+ /* Don't grow until we can request at least half a window */
+ if (increment < (WINDOW_DEFAULT / 2)) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "growing window (channel %" PRIu32 ":%" PRIu32 ") to %" PRIu32 " bytes : not needed (%" PRIu32 " bytes)",
+ channel->local_channel, channel->remote_channel, WINDOW_DEFAULT,
channel->local_window);
return SSH_OK;
}
- /* WINDOW_ADJUST packet needs a relative increment rather than an absolute
- * value, so we give here the missing bytes needed to reach new_window
- */
+
rc = ssh_buffer_pack(session->out_buffer,
"bdd",
SSH2_MSG_CHANNEL_WINDOW_ADJUST,
channel->remote_channel,
- new_window - channel->local_window);
+ increment);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
goto error;
@@ -434,13 +471,13 @@ static int grow_window(ssh_session session,
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "growing window (channel %d:%d) to %d bytes",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "growing window (channel %" PRIu32 ":%" PRIu32 ") by %" PRIu32 " bytes",
channel->local_channel,
channel->remote_channel,
- new_window);
+ increment);
- channel->local_window = new_window;
+ channel->local_window += increment;
return SSH_OK;
error:
@@ -462,7 +499,8 @@ error:
* @return The related ssh_channel, or NULL if the channel is
* unknown or the packet is invalid.
*/
-static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
+static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet)
+{
ssh_channel channel;
uint32_t chan;
int rc;
@@ -477,7 +515,7 @@ static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
channel = ssh_channel_from_local(session, chan);
if (channel == NULL) {
ssh_set_error(session, SSH_FATAL,
- "Server specified invalid channel %"PRIu32,
+ "Server specified invalid channel %" PRIu32,
(uint32_t) chan);
}
@@ -488,6 +526,8 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) {
ssh_channel channel;
uint32_t bytes;
int rc;
+ bool was_empty;
+
(void)user;
(void)type;
@@ -504,123 +544,153 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) {
return SSH_PACKET_USED;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Adding %d bytes to channel (%d:%d) (from %d bytes)",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Adding %" PRIu32 " bytes to channel (%" PRIu32 ":%" PRIu32 ") (from %" PRIu32 " bytes)",
bytes,
channel->local_channel,
channel->remote_channel,
channel->remote_window);
+ was_empty = channel->remote_window == 0;
+
channel->remote_window += bytes;
+ /* Writing to the channel is non-blocking until the receive window is empty.
+ When the receive window becomes non-zero again, call channel_write_wontblock_function. */
+ if (was_empty && bytes > 0) {
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_write_wontblock_function,
+ session,
+ channel,
+ channel->remote_window);
+ }
+
return SSH_PACKET_USED;
}
/* is_stderr is set to 1 if the data are extended, ie stderr */
-SSH_PACKET_CALLBACK(channel_rcv_data){
- ssh_channel channel;
- ssh_string str;
- ssh_buffer buf;
- size_t len;
- int is_stderr;
- int rest;
- (void)user;
+SSH_PACKET_CALLBACK(channel_rcv_data)
+{
+ ssh_channel channel = NULL;
+ ssh_string str = NULL;
+ ssh_buffer buf = NULL;
+ void *data = NULL;
+ uint32_t len;
+ int extended, is_stderr = 0;
+ int rest;
- if(type==SSH2_MSG_CHANNEL_DATA)
- is_stderr=0;
- else
- is_stderr=1;
+ (void)user;
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS,
- "%s", ssh_get_error(session));
+ if (type == SSH2_MSG_CHANNEL_DATA) {
+ extended = 0;
+ } else { /* SSH_MSG_CHANNEL_EXTENDED_DATA */
+ extended = 1;
+ }
- return SSH_PACKET_USED;
- }
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- if (is_stderr) {
- uint32_t ignore;
- /* uint32 data type code. we can ignore it */
- ssh_buffer_get_u32(packet, &ignore);
- }
+ return SSH_PACKET_USED;
+ }
- str = ssh_buffer_get_ssh_string(packet);
- if (str == NULL) {
- SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!");
+ if (extended) {
+ uint32_t data_type_code, rc;
+ rc = ssh_buffer_get_u32(packet, &data_type_code);
+ if (rc != sizeof(uint32_t)) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Failed to read data type code: rc = %" PRIu32, rc);
- return SSH_PACKET_USED;
- }
- len = ssh_string_len(str);
+ return SSH_PACKET_USED;
+ }
+ is_stderr = 1;
+ data_type_code = ntohl(data_type_code);
+ if (data_type_code != SSH2_EXTENDED_DATA_STDERR) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid data type code %" PRIu32 "!",
+ data_type_code);
+ }
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Channel receiving %zu bytes data in %d (local win=%d remote win=%d)",
- len,
- is_stderr,
- channel->local_window,
- channel->remote_window);
+ str = ssh_buffer_get_ssh_string(packet);
+ if (str == NULL) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!");
- /* What shall we do in this case? Let's accept it anyway */
- if (len > channel->local_window) {
- SSH_LOG(SSH_LOG_RARE,
- "Data packet too big for our window(%zu vs %d)",
- len,
- channel->local_window);
- }
+ return SSH_PACKET_USED;
+ }
+ len = ssh_string_len(str);
- if (channel_default_bufferize(channel, ssh_string_data(str), len,
- is_stderr) < 0) {
- SSH_STRING_FREE(str);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Channel receiving %" PRIu32 " bytes data%s (local win=%" PRIu32
+ " remote win=%" PRIu32 ")",
+ len,
+ is_stderr ? " in stderr" : "",
+ channel->local_window,
+ channel->remote_window);
- return SSH_PACKET_USED;
- }
+ if (len > channel->local_window) {
+ SSH_LOG(SSH_LOG_RARE,
+ "Data packet too big for our window(%" PRIu32 " vs %" PRIu32 ")",
+ len,
+ channel->local_window);
+
+ SSH_STRING_FREE(str);
+
+ ssh_set_error(session, SSH_FATAL, "Window exceeded");
+
+ return SSH_PACKET_USED;
+ }
+
+ data = ssh_string_data(str);
+ if (channel_default_bufferize(channel, data, len, is_stderr) < 0) {
+ SSH_STRING_FREE(str);
+
+ return SSH_PACKET_USED;
+ }
- if (len <= channel->local_window) {
channel->local_window -= len;
- } else {
- channel->local_window = 0; /* buggy remote */
- }
- SSH_LOG(SSH_LOG_PACKET,
- "Channel windows are now (local win=%d remote win=%d)",
- channel->local_window,
- channel->remote_window);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Channel windows are now (local win=%" PRIu32 " remote win=%" PRIu32 ")",
+ channel->local_window,
+ channel->remote_window);
- SSH_STRING_FREE(str);
+ SSH_STRING_FREE(str);
- if (is_stderr) {
- buf = channel->stderr_buffer;
- } else {
- buf = channel->stdout_buffer;
- }
+ if (is_stderr) {
+ buf = channel->stderr_buffer;
+ } else {
+ buf = channel->stdout_buffer;
+ }
- ssh_callbacks_iterate(channel->callbacks,
- ssh_channel_callbacks,
- channel_data_function) {
- if (ssh_buffer_get(buf) == NULL) {
- break;
- }
- rest = ssh_callbacks_iterate_exec(channel_data_function,
- channel->session,
- channel,
- ssh_buffer_get(buf),
- ssh_buffer_get_len(buf),
- is_stderr);
- if (rest > 0) {
- if (channel->counter != NULL) {
- channel->counter->in_bytes += rest;
- }
- ssh_buffer_pass_bytes(buf, rest);
- }
- }
- ssh_callbacks_iterate_end();
+ ssh_callbacks_iterate(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_data_function) {
+ if (ssh_buffer_get(buf) == NULL) {
+ break;
+ }
+ rest = ssh_callbacks_iterate_exec(channel_data_function,
+ channel->session,
+ channel,
+ ssh_buffer_get(buf),
+ ssh_buffer_get_len(buf),
+ is_stderr);
+ if (rest > 0) {
+ int rc;
+ if (channel->counter != NULL) {
+ channel->counter->in_bytes += rest;
+ }
+ ssh_buffer_pass_bytes(buf, rest);
- if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
- return -1;
- }
- }
- return SSH_PACKET_USED;
+ rc = grow_window(session, channel);
+ if (rc == SSH_ERROR) {
+ return -1;
+ }
+ }
+ }
+ ssh_callbacks_iterate_end();
+
+ return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(channel_rcv_eof) {
@@ -636,7 +706,7 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) {
}
SSH_LOG(SSH_LOG_PACKET,
- "Received eof on channel (%d:%d)",
+ "Received eof on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
/* channel->remote_window = 0; */
@@ -681,7 +751,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
}
SSH_LOG(SSH_LOG_PACKET,
- "Received close on channel (%d:%d)",
+ "Received close on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
@@ -717,7 +787,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
SSH_PACKET_CALLBACK(channel_rcv_request) {
ssh_channel channel;
char *request=NULL;
- uint8_t status;
+ uint8_t want_reply;
int rc;
(void)user;
(void)type;
@@ -730,7 +800,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
rc = ssh_buffer_unpack(packet, "sb",
&request,
- &status);
+ &want_reply);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
return SSH_PACKET_USED;
@@ -823,7 +893,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
}
if(strcmp(request,"keepalive@openssh.com")==0){
SAFE_FREE(request);
- SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive");
+ SSH_LOG(SSH_LOG_DEBUG,"Responding to Openssh's keepalive");
rc = ssh_buffer_pack(session->out_buffer,
"bd",
@@ -838,13 +908,34 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
}
if (strcmp(request, "auth-agent-req@openssh.com") == 0) {
+ int status;
+
SAFE_FREE(request);
- SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request");
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_auth_agent_req_function,
- channel->session,
- channel);
+ SSH_LOG(SSH_LOG_DEBUG, "Received an auth-agent-req request");
+
+ status = SSH2_MSG_CHANNEL_FAILURE;
+ ssh_callbacks_iterate(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_auth_agent_req_function) {
+ ssh_callbacks_iterate_exec(channel_auth_agent_req_function,
+ channel->session,
+ channel);
+ /* in lieu of a return value, if the callback exists it's supported */
+ status = SSH2_MSG_CHANNEL_SUCCESS;
+ break;
+ }
+ ssh_callbacks_iterate_end();
+
+ if (want_reply) {
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bd",
+ status,
+ channel->remote_channel);
+ if (rc != SSH_OK) {
+ return SSH_PACKET_USED;
+ }
+ ssh_packet_send(session);
+ }
return SSH_PACKET_USED;
}
@@ -853,11 +944,11 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
* client requests. That means we need to create a ssh message to be passed
* to the user code handling ssh messages
*/
- ssh_message_handle_channel_request(session,channel,packet,request,status);
+ ssh_message_handle_channel_request(session,channel,packet,request,want_reply);
#else
- SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request);
+ SSH_LOG(SSH_LOG_DEBUG, "Unhandled channel request %s", request);
#endif
-
+
SAFE_FREE(request);
return SSH_PACKET_USED;
@@ -870,7 +961,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
* FIXME is the window changed?
*/
int channel_default_bufferize(ssh_channel channel,
- void *data, size_t len,
+ void *data, uint32_t len,
bool is_stderr)
{
ssh_session session;
@@ -887,7 +978,7 @@ int channel_default_bufferize(ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "placing %zu bytes into channel buffer (%s)",
+ "placing %" PRIu32 " bytes into channel buffer (%s)",
len,
is_stderr ? "stderr" : "stdout");
if (!is_stderr) {
@@ -942,14 +1033,15 @@ int channel_default_bufferize(ssh_channel channel,
* @see ssh_channel_request_shell()
* @see ssh_channel_request_exec()
*/
-int ssh_channel_open_session(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_open_session(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
return channel_open(channel,
"session",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
NULL);
}
@@ -969,14 +1061,15 @@ int ssh_channel_open_session(ssh_channel channel) {
*
* @see ssh_channel_open_forward()
*/
-int ssh_channel_open_auth_agent(ssh_channel channel){
- if(channel == NULL) {
+int ssh_channel_open_auth_agent(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
return channel_open(channel,
"auth-agent@openssh.com",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
NULL);
}
@@ -1005,16 +1098,17 @@ int ssh_channel_open_auth_agent(ssh_channel channel){
*
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
- * use channel_read and channel_write for this.
+ * use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
- int remoteport, const char *sourcehost, int localport) {
+ int remoteport, const char *sourcehost, int localport)
+{
ssh_session session;
ssh_buffer payload = NULL;
ssh_string str = NULL;
int rc = SSH_ERROR;
- if(channel == NULL) {
+ if (channel == NULL) {
return rc;
}
@@ -1044,7 +1138,7 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
rc = channel_open(channel,
"direct-tcpip",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -1076,7 +1170,7 @@ error:
*
* @warning This function does not bind the local port and does not
* automatically forward the content of a socket to the channel.
- * You still have to use channel_read and channel_write for this.
+ * You still have to use ssh_channel_read and ssh_channel_write for this.
* @warning Requires support of OpenSSH for UNIX domain socket forwarding.
*/
int ssh_channel_open_forward_unix(ssh_channel channel,
@@ -1127,7 +1221,7 @@ int ssh_channel_open_forward_unix(ssh_channel channel,
rc = channel_open(channel,
"direct-streamlocal@openssh.com",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -1181,7 +1275,7 @@ void ssh_channel_free(ssh_channel channel)
channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL;
/* The idea behind the flags is the following : it is well possible
- * that a client closes a channel that stills exists on the server side.
+ * that a client closes a channel that still exists on the server side.
* We definitively close the channel when we receive a close message *and*
* the user closed it.
*/
@@ -1274,7 +1368,7 @@ int ssh_channel_send_eof(ssh_channel channel)
rc = ssh_packet_send(session);
SSH_LOG(SSH_LOG_PACKET,
- "Sent a EOF on client channel (%d:%d)",
+ "Sent a EOF on client channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
if (rc != SSH_OK) {
@@ -1339,7 +1433,7 @@ int ssh_channel_close(ssh_channel channel)
rc = ssh_packet_send(session);
SSH_LOG(SSH_LOG_PACKET,
- "Sent a close on client channel (%d:%d)",
+ "Sent a close on client channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
@@ -1361,7 +1455,8 @@ error:
}
/* this termination function waits for a window growing condition */
-static int ssh_channel_waitwindow_termination(void *c){
+static int ssh_channel_waitwindow_termination(void *c)
+{
ssh_channel channel = (ssh_channel) c;
if (channel->remote_window > 0 ||
channel->session->session_state == SSH_SESSION_STATE_ERROR ||
@@ -1374,7 +1469,8 @@ static int ssh_channel_waitwindow_termination(void *c){
/* This termination function waits until the session is not in blocked status
* anymore, e.g. because of a key re-exchange.
*/
-static int ssh_waitsession_unblocked(void *s){
+static int ssh_waitsession_unblocked(void *s)
+{
ssh_session session = (ssh_session)s;
switch (session->session_state){
case SSH_SESSION_STATE_DH:
@@ -1394,8 +1490,9 @@ static int ssh_waitsession_unblocked(void *s){
* SSH_ERROR On error.
* SSH_AGAIN Timeout elapsed (or in nonblocking mode).
*/
-int ssh_channel_flush(ssh_channel channel){
- return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT);
+int ssh_channel_flush(ssh_channel channel)
+{
+ return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT);
}
static int channel_write_common(ssh_channel channel,
@@ -1405,7 +1502,6 @@ static int channel_write_common(ssh_channel channel,
ssh_session session;
uint32_t origlen = len;
size_t effectivelen;
- size_t maxpacketlen;
int rc;
if(channel == NULL) {
@@ -1418,20 +1514,14 @@ static int channel_write_common(ssh_channel channel,
}
if (len > INT_MAX) {
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Length (%u) is bigger than INT_MAX", len);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Length (%" PRIu32 ") is bigger than INT_MAX", len);
return SSH_ERROR;
}
- /*
- * Handle the max packet len from remote side, be nice
- * 10 bytes for the headers
- */
- maxpacketlen = channel->remote_maxpacket - 10;
-
if (channel->local_eof) {
ssh_set_error(session, SSH_REQUEST_DENIED,
- "Can't write to channel %d:%d after EOF was sent",
+ "Can't write to channel %" PRIu32 ":%" PRIu32 " after EOF was sent",
channel->local_channel,
channel->remote_channel);
return -1;
@@ -1455,14 +1545,14 @@ static int channel_write_common(ssh_channel channel,
}
while (len > 0) {
if (channel->remote_window < len) {
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Remote window is %d bytes. going to write %d bytes",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Remote window is %" PRIu32 " bytes. going to write %" PRIu32 " bytes",
channel->remote_window,
len);
- /* What happens when the channel window is zero? */
+ /* When the window is zero, wait for it to grow */
if(channel->remote_window == 0) {
/* nothing can be written */
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Wait for a growing window message...");
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT,
ssh_channel_waitwindow_termination,channel);
@@ -1473,12 +1563,17 @@ static int channel_write_common(ssh_channel channel,
goto out;
continue;
}
+ /* When the window is non-zero, accept data up to the window size */
effectivelen = MIN(len, channel->remote_window);
} else {
effectivelen = len;
}
- effectivelen = MIN(effectivelen, maxpacketlen);;
+ /*
+ * Like OpenSSH, don't subtract bytes for the header fields
+ * and allow to send a payload of remote_maxpacket length.
+ */
+ effectivelen = MIN(effectivelen, channel->remote_maxpacket);
rc = ssh_buffer_pack(session->out_buffer,
"bd",
@@ -1516,7 +1611,7 @@ static int channel_write_common(ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "channel_write wrote %ld bytes", (long int) effectivelen);
+ "ssh_channel_write wrote %ld bytes", (long int) effectivelen);
channel->remote_window -= effectivelen;
len -= effectivelen;
@@ -1544,7 +1639,7 @@ error:
/**
* @brief Get the remote window size.
*
- * This is the maximum amounts of bytes the remote side expects us to send
+ * This is the maximum amount of bytes the remote side expects us to send
* before growing the window again.
*
* @param[in] channel The channel to query.
@@ -1558,7 +1653,8 @@ error:
* @warning A zero return value means ssh_channel_write (default settings)
* will block until the window grows back.
*/
-uint32_t ssh_channel_window_size(ssh_channel channel) {
+uint32_t ssh_channel_window_size(ssh_channel channel)
+{
return channel->remote_window;
}
@@ -1575,8 +1671,9 @@ uint32_t ssh_channel_window_size(ssh_channel channel) {
*
* @see ssh_channel_read()
*/
-int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
- return channel_write_common(channel, data, len, 0);
+int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
+{
+ return channel_write_common(channel, data, len, 0);
}
/**
@@ -1588,8 +1685,9 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
*
* @see ssh_channel_is_closed()
*/
-int ssh_channel_is_open(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_is_open(ssh_channel channel)
+{
+ if (channel == NULL) {
return 0;
}
return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0);
@@ -1604,8 +1702,9 @@ int ssh_channel_is_open(ssh_channel channel) {
*
* @see ssh_channel_is_open()
*/
-int ssh_channel_is_closed(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_is_closed(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0);
@@ -1618,15 +1717,16 @@ int ssh_channel_is_closed(ssh_channel channel) {
*
* @return 0 if there is no EOF, nonzero otherwise.
*/
-int ssh_channel_is_eof(ssh_channel channel) {
- if(channel == NULL) {
- return SSH_ERROR;
- }
- if (ssh_channel_has_unread_data(channel)) {
- return 0;
- }
+int ssh_channel_is_eof(ssh_channel channel)
+{
+ if (channel == NULL) {
+ return SSH_ERROR;
+ }
+ if (ssh_channel_has_unread_data(channel)) {
+ return 0;
+ }
- return (channel->remote_eof != 0);
+ return (channel->remote_eof != 0);
}
/**
@@ -1640,11 +1740,12 @@ int ssh_channel_is_eof(ssh_channel channel) {
* in non-blocking mode.
* @see ssh_set_blocking()
*/
-void ssh_channel_set_blocking(ssh_channel channel, int blocking) {
- if(channel == NULL) {
- return;
- }
- ssh_set_blocking(channel->session,blocking);
+void ssh_channel_set_blocking(ssh_channel channel, int blocking)
+{
+ if (channel == NULL) {
+ return;
+ }
+ ssh_set_blocking(channel->session, blocking);
}
/**
@@ -1664,7 +1765,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){
}
SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)",
+ "Received SSH_CHANNEL_SUCCESS on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
@@ -1672,6 +1773,12 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){
channel->request_state);
} else {
channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_request_response_function,
+ channel->session,
+ channel);
}
return SSH_PACKET_USED;
@@ -1695,7 +1802,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){
}
SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_CHANNEL_FAILURE on channel (%d:%d)",
+ "Received SSH_CHANNEL_FAILURE on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
@@ -1703,12 +1810,19 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){
channel->request_state);
} else {
channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_request_response_function,
+ channel->session,
+ channel);
}
return SSH_PACKET_USED;
}
-static int ssh_channel_request_termination(void *c){
+static int ssh_channel_request_termination(void *c)
+{
ssh_channel channel = (ssh_channel)c;
if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING ||
channel->session->session_state == SSH_SESSION_STATE_ERROR)
@@ -1718,7 +1832,8 @@ static int ssh_channel_request_termination(void *c){
}
static int channel_request(ssh_channel channel, const char *request,
- ssh_buffer buffer, int reply) {
+ ssh_buffer buffer, int reply)
+{
ssh_session session = channel->session;
int rc = SSH_ERROR;
int ret;
@@ -1779,7 +1894,7 @@ pending:
rc=SSH_ERROR;
break;
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Channel request %s success",request);
rc=SSH_OK;
break;
@@ -1804,7 +1919,7 @@ error:
/**
* @brief Request a pty with a specific type and size.
*
- * @param[in] channel The channel to sent the request.
+ * @param[in] channel The channel to send the request.
*
* @param[in] terminal The terminal type ("vt100, xterm,...").
*
@@ -1812,13 +1927,18 @@ error:
*
* @param[in] row The number of rows.
*
+ * @param[in] modes Encoded SSH terminal modes for the PTY
+ *
+ * @param[in] modes_len Number of bytes in 'modes'
+ *
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* to be done again.
*/
-int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
- int col, int row) {
+int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal,
+ int col, int row, const unsigned char* modes, size_t modes_len)
+{
ssh_session session;
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -1847,14 +1967,14 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
}
rc = ssh_buffer_pack(buffer,
- "sdddddb",
+ "sdddddP",
terminal,
col,
row,
0, /* pix */
0, /* pix */
- 1, /* add a 0byte string */
- 0);
+ (uint32_t)modes_len,
+ modes_len, modes);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
@@ -1868,6 +1988,23 @@ error:
return rc;
}
+int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
+ int col, int row)
+{
+ /* use modes from the current TTY */
+ unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
+ int rc = encode_current_tty_opts(modes_buf, sizeof(modes_buf));
+ if (rc < 0) {
+ return rc;
+ }
+ return ssh_channel_request_pty_size_modes(channel,
+ terminal,
+ col,
+ row,
+ modes_buf,
+ (size_t)rc);
+}
+
/**
* @brief Request a PTY.
*
@@ -1880,7 +2017,8 @@ error:
*
* @see ssh_channel_request_pty_size()
*/
-int ssh_channel_request_pty(ssh_channel channel) {
+int ssh_channel_request_pty(ssh_channel channel)
+{
return ssh_channel_request_pty_size(channel, "xterm", 80, 24);
}
@@ -1896,10 +2034,11 @@ int ssh_channel_request_pty(ssh_channel channel) {
* @return SSH_OK on success, SSH_ERROR if an error occurred.
*
* @warning Do not call it from a signal handler if you are not sure any other
- * libssh function using the same channel/session is running at same
- * time (not 100% threadsafe).
+ * libssh function using the same channel/session is running at the
+ * same time (not 100% threadsafe).
*/
-int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) {
+int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows)
+{
ssh_session session = channel->session;
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -1938,8 +2077,9 @@ error:
* SSH_AGAIN if in nonblocking mode and call has
* to be done again.
*/
-int ssh_channel_request_shell(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_request_shell(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
@@ -1960,7 +2100,8 @@ int ssh_channel_request_shell(ssh_channel channel) {
*
* @warning You normally don't have to call it for sftp, see sftp_new().
*/
-int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) {
+int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2009,14 +2150,16 @@ error:
*
* @note You should use sftp_new() which does this for you.
*/
-int ssh_channel_request_sftp( ssh_channel channel){
+int ssh_channel_request_sftp( ssh_channel channel)
+{
if(channel == NULL) {
return SSH_ERROR;
}
return ssh_channel_request_subsystem(channel, "sftp");
}
-static char *generate_cookie(void) {
+static char *generate_cookie(void)
+{
static const char *hex = "0123456789abcdef";
char s[36];
unsigned char rnd[16];
@@ -2040,7 +2183,7 @@ static char *generate_cookie(void) {
* @brief Sends the "x11-req" channel request over an existing session channel.
*
* This will enable redirecting the display of the remote X11 applications to
- * local X server over an secure tunnel.
+ * local X server over a secure tunnel.
*
* @param[in] channel An existing session channel where the remote X11
* applications are going to be executed.
@@ -2062,7 +2205,8 @@ static char *generate_cookie(void) {
* to be done again.
*/
int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
- const char *cookie, int screen_number) {
+ const char *cookie, int screen_number)
+{
ssh_buffer buffer = NULL;
char *c = NULL;
int rc = SSH_ERROR;
@@ -2113,7 +2257,8 @@ error:
}
static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
- int timeout_ms, int *destination_port) {
+ int timeout_ms, int *destination_port, char **originator, int *originator_port)
+{
#ifndef _WIN32
static const struct timespec ts = {
.tv_sec = 0,
@@ -2147,6 +2292,12 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
if(destination_port) {
*destination_port=msg->channel_request_open.destination_port;
}
+ if(originator) {
+ *originator=strdup(msg->channel_request_open.originator);
+ }
+ if(originator_port) {
+ *originator_port=msg->channel_request_open.originator_port;
+ }
ssh_message_free(msg);
return channel;
@@ -2177,8 +2328,9 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
* @return A newly created channel, or NULL if no X11 request from
* the server.
*/
-ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) {
- return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL);
+ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms)
+{
+ return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL, NULL, NULL);
}
/**
@@ -2247,7 +2399,8 @@ SSH_PACKET_CALLBACK(ssh_request_denied){
}
-static int ssh_global_request_termination(void *s){
+static int ssh_global_request_termination(void *s)
+{
ssh_session session = (ssh_session) s;
if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING ||
session->session_state == SSH_SESSION_STATE_ERROR)
@@ -2336,7 +2489,7 @@ pending:
}
switch(session->global_req_state){
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
- SSH_LOG(SSH_LOG_PROTOCOL, "Global request %s success",request);
+ SSH_LOG(SSH_LOG_DEBUG, "Global request %s success",request);
rc=SSH_OK;
break;
case SSH_CHANNEL_REQ_STATE_DENIED:
@@ -2426,18 +2579,21 @@ error:
}
/* DEPRECATED */
-int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) {
+int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port)
+{
return ssh_channel_listen_forward(session, address, port, bound_port);
}
/* DEPRECATED */
-ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) {
- return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL);
+ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms)
+{
+ return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL, NULL, NULL);
}
/**
- * @brief Accept an incoming TCP/IP forwarding channel and get information
- * about incomming connection
+ * @brief Accept an incoming TCP/IP forwarding channel and get some information
+ * about incoming connection
+ *
* @param[in] session The ssh session to use.
*
* @param[in] timeout_ms A timeout in milliseconds.
@@ -2448,7 +2604,31 @@ ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) {
* the server
*/
ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) {
- return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port);
+ return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port, NULL, NULL);
+}
+
+/**
+ * @brief Accept an incoming TCP/IP forwarding channel and get information
+ * about incoming connection
+ *
+ * @param[in] session The ssh session to use.
+ *
+ * @param[in] timeout_ms A timeout in milliseconds.
+ *
+ * @param[out] destination_port A pointer to destination port or NULL.
+ *
+ * @param[out] originator A pointer to a pointer to a string of originator host or NULL.
+ * That the caller is responsible for to ssh_string_free_char().
+ *
+ * @param[out] originator_port A pointer to originator port or NULL.
+ *
+ * @return Newly created channel, or NULL if no incoming channel request from
+ * the server
+ *
+ * @see ssh_string_free_char()
+ */
+ssh_channel ssh_channel_open_forward_port(ssh_session session, int timeout_ms, int *destination_port, char **originator, int *originator_port) {
+ return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port, originator, originator_port);
}
/**
@@ -2498,7 +2678,8 @@ error:
}
/* DEPRECATED */
-int ssh_forward_cancel(ssh_session session, const char *address, int port) {
+int ssh_forward_cancel(ssh_session session, const char *address, int port)
+{
return ssh_channel_cancel_forward(session, address, port);
}
@@ -2517,7 +2698,8 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port) {
* to be done again.
* @warning Some environment variables may be refused by security reasons.
*/
-int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) {
+int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2587,7 +2769,8 @@ error:
*
* @see ssh_channel_request_shell()
*/
-int ssh_channel_request_exec(ssh_channel channel, const char *cmd) {
+int ssh_channel_request_exec(ssh_channel channel, const char *cmd)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2631,10 +2814,6 @@ error:
* Sends a signal 'sig' to the remote process.
* Note, that remote system may not support signals concept.
* In such a case this request will be silently ignored.
- * Only SSH-v2 is supported (I'm not sure about SSH-v1).
- *
- * OpenSSH doesn't support signals yet, see:
- * https://bugzilla.mindrot.org/show_bug.cgi?id=1424
*
* @param[in] channel The channel to send signal.
*
@@ -2654,17 +2833,17 @@ error:
* SIGUSR1 -> USR1 \n
* SIGUSR2 -> USR2 \n
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred
- * (including attempts to send signal via SSH-v1 session).
+ * @return SSH_OK on success, SSH_ERROR if an error occurred.
*/
-int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) {
+int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
- if(channel == NULL) {
+ if (channel == NULL) {
return SSH_ERROR;
}
- if(sig == NULL) {
+ if (sig == NULL) {
ssh_set_error_invalid(channel->session);
return rc;
}
@@ -2694,16 +2873,15 @@ error:
* Sends a break signal to the remote process.
* Note, that remote system may not support breaks.
* In such a case this request will be silently ignored.
- * Only SSH-v2 is supported.
*
* @param[in] channel The channel to send the break to.
*
* @param[in] length The break-length in milliseconds to send.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
- * (including attempts to send signal via SSH-v1 session).
*/
-int ssh_channel_request_send_break(ssh_channel channel, uint32_t length) {
+int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2736,7 +2914,7 @@ error:
*
* @param[in] channel The channel to read from.
*
- * @param[in] buffer The buffer which will get the data.
+ * @param[out] buffer The buffer which will get the data.
*
* @param[in] count The count of bytes to be read. If it is bigger than 0,
* the exact size will be read, else (bytes=0) it will
@@ -2751,7 +2929,8 @@ error:
* @see ssh_channel_read
*/
int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
- int is_stderr) {
+ int is_stderr)
+{
ssh_session session;
char *buffer_tmp = NULL;
int r;
@@ -2822,13 +3001,13 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
struct ssh_channel_read_termination_struct {
ssh_channel channel;
- uint32_t count;
ssh_buffer buffer;
};
-static int ssh_channel_read_termination(void *s){
+static int ssh_channel_read_termination(void *s)
+{
struct ssh_channel_read_termination_struct *ctx = s;
- if (ssh_buffer_get_len(ctx->buffer) >= ctx->count ||
+ if (ssh_buffer_get_len(ctx->buffer) >= 1 ||
ctx->channel->remote_eof ||
ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR)
return 1;
@@ -2836,27 +3015,25 @@ static int ssh_channel_read_termination(void *s){
return 0;
}
-/* TODO FIXME Fix the blocking behaviours */
+/* TODO: FIXME Fix the blocking behaviours */
/**
* @brief Reads data from a channel.
*
* @param[in] channel The channel to read from.
*
- * @param[in] dest The destination buffer which will get the data.
+ * @param[out] dest The destination buffer which will get the data.
*
* @param[in] count The count of bytes to be read.
*
* @param[in] is_stderr A boolean value to mark reading from the stderr flow.
*
* @return The number of bytes read, 0 on end of file or SSH_ERROR
- * on error. In nonblocking mode it Can return 0 if no data
+ * on error. In nonblocking mode it can return 0 if no data
* is available or SSH_AGAIN.
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
- * @warning The read function using a buffer has been renamed to
- * channel_read_buffer().
*/
int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
{
@@ -2872,7 +3049,7 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
*
* @param[in] channel The channel to read from.
*
- * @param[in] dest The destination buffer which will get the data.
+ * @param[out] dest The destination buffer which will get the data.
*
* @param[in] count The count of bytes to be read.
*
@@ -2887,8 +3064,6 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
- * @warning The read function using a buffer has been renamed to
- * channel_read_buffer().
*/
int ssh_channel_read_timeout(ssh_channel channel,
void *dest,
@@ -2921,28 +3096,17 @@ int ssh_channel_read_timeout(ssh_channel channel,
stdbuf=channel->stderr_buffer;
}
- /*
- * We may have problem if the window is too small to accept as much data
- * as asked
- */
SSH_LOG(SSH_LOG_PACKET,
- "Read (%d) buffered : %d bytes. Window: %d",
+ "Read (%" PRIu32 ") buffered : %" PRIu32 " bytes. Window: %" PRIu32,
count,
ssh_buffer_get_len(stdbuf),
channel->local_window);
- if (count > ssh_buffer_get_len(stdbuf) + channel->local_window) {
- if (grow_window(session, channel, count - ssh_buffer_get_len(stdbuf)) < 0) {
- return -1;
- }
- }
-
/* block reading until at least one byte has been read
* and ignore the trivial case count=0
*/
ctx.channel = channel;
ctx.buffer = stdbuf;
- ctx.count = 1;
if (timeout_ms < SSH_TIMEOUT_DEFAULT) {
timeout_ms = SSH_TIMEOUT_INFINITE;
@@ -2984,11 +3148,10 @@ int ssh_channel_read_timeout(ssh_channel channel,
if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) {
channel->state = SSH_CHANNEL_STATE_CLOSED;
}
- /* Authorize some buffering while userapp is busy */
- if (channel->local_window < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
- return -1;
- }
+
+ rc = grow_window(session, channel);
+ if (rc == SSH_ERROR) {
+ return -1;
}
return len;
@@ -2998,20 +3161,18 @@ int ssh_channel_read_timeout(ssh_channel channel,
* @brief Do a nonblocking read on the channel.
*
* A nonblocking read on the specified channel. it will return <= count bytes of
- * data read atomically.
+ * data read atomically. It will also trigger any callbacks set on the channel.
*
* @param[in] channel The channel to read from.
*
- * @param[in] dest A pointer to a destination buffer.
+ * @param[out] dest A pointer to a destination buffer.
*
* @param[in] count The count of bytes of data to be read.
*
* @param[in] is_stderr A boolean to select the stderr stream.
*
- * @return The number of bytes read, 0 if nothing is available or
- * SSH_ERROR on error.
- *
- * @warning Don't forget to check for EOF as it would return 0 here.
+ * @return The number of bytes read (0 if nothing is available),
+ * SSH_ERROR on error, and SSH_EOF if the channel is EOF.
*
* @see ssh_channel_is_eof()
*/
@@ -3021,7 +3182,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
int is_stderr)
{
ssh_session session;
- ssize_t to_read;
+ uint32_t to_read;
int rc;
int blocking;
@@ -3035,22 +3196,24 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
session = channel->session;
- to_read = ssh_channel_poll(channel, is_stderr);
+ rc = ssh_channel_poll(channel, is_stderr);
- if (to_read <= 0) {
+ if (rc <= 0) {
if (session->session_state == SSH_SESSION_STATE_ERROR){
return SSH_ERROR;
}
- return to_read; /* may be an error code */
+ return rc; /* may be an error code */
}
- if ((size_t)to_read > count) {
- to_read = (ssize_t)count;
+ to_read = (unsigned int)rc;
+
+ if (to_read > count) {
+ to_read = count;
}
blocking = ssh_is_blocking(session);
ssh_set_blocking(session, 0);
- rc = ssh_channel_read(channel, dest, (uint32_t)to_read, is_stderr);
+ rc = ssh_channel_read(channel, dest, to_read, is_stderr);
ssh_set_blocking(session,blocking);
return rc;
@@ -3065,15 +3228,18 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
*
* @return The number of bytes available for reading, 0 if nothing
* is available or SSH_ERROR on error.
+ * When a channel is freed the function returns
+ * SSH_ERROR immediately.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
*
* @see ssh_channel_is_eof()
*/
-int ssh_channel_poll(ssh_channel channel, int is_stderr){
+int ssh_channel_poll(ssh_channel channel, int is_stderr)
+{
ssh_buffer stdbuf;
- if(channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
@@ -3119,6 +3285,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){
* SSH_ERROR on error.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
+ * When a channel is freed the function returns SSH_ERROR immediately.
*
* @see ssh_channel_is_eof()
*/
@@ -3130,7 +3297,7 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
size_t len;
int rc;
- if (channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
@@ -3142,7 +3309,6 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
}
ctx.buffer = stdbuf;
ctx.channel = channel;
- ctx.count = 1;
rc = ssh_handle_packets_termination(channel->session,
timeout,
ssh_channel_read_termination,
@@ -3182,15 +3348,17 @@ out:
*
* @return The session pointer.
*/
-ssh_session ssh_channel_get_session(ssh_channel channel) {
- if(channel == NULL) {
+ssh_session ssh_channel_get_session(ssh_channel channel)
+{
+ if (channel == NULL) {
return NULL;
}
return channel->session;
}
-static int ssh_channel_exit_status_termination(void *c){
+static int ssh_channel_exit_status_termination(void *c)
+{
ssh_channel channel = c;
if(channel->exit_status != -1 ||
/* When a channel is closed, no exit status message can
@@ -3212,15 +3380,18 @@ static int ssh_channel_exit_status_termination(void *c){
* (yet), or SSH_ERROR on error.
* @warning This function may block until a timeout (or never)
* if the other side is not willing to close the channel.
+ * When a channel is freed the function returns
+ * SSH_ERROR immediately.
*
* If you're looking for an async handling of this register a callback for the
* exit status.
*
* @see ssh_channel_exit_status_callback
*/
-int ssh_channel_get_exit_status(ssh_channel channel) {
+int ssh_channel_get_exit_status(ssh_channel channel)
+{
int rc;
- if(channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
rc = ssh_handle_packets_termination(channel->session,
@@ -3242,8 +3413,11 @@ int ssh_channel_get_exit_status(ssh_channel channel) {
* This is made in two parts: protocol select and network select. The protocol
* select does not use the network functions at all
*/
-static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans,
- ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel *eout) {
+static int
+channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans,
+ ssh_channel *echans, ssh_channel *rout,
+ ssh_channel *wout, ssh_channel *eout)
+{
ssh_channel chan;
int i;
int j = 0;
@@ -3323,7 +3497,8 @@ static size_t count_ptrs(ssh_channel *ptrs)
* function, or SSH_ERROR on error.
*/
int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
- ssh_channel *exceptchans, struct timeval * timeout) {
+ ssh_channel *exceptchans, struct timeval * timeout)
+{
ssh_channel *rchans, *wchans, *echans;
ssh_channel dummy = NULL;
ssh_event event = NULL;
@@ -3427,9 +3602,15 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
firstround=0;
} while(1);
- memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel ));
- memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel ));
- memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel ));
+ if (readchans != &dummy) {
+ memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel));
+ }
+ if (writechans != &dummy) {
+ memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel));
+ }
+ if (exceptchans != &dummy) {
+ memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel));
+ }
SAFE_FREE(rchans);
SAFE_FREE(wchans);
SAFE_FREE(echans);
@@ -3457,7 +3638,8 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
* @param[in] counter Counter for bytes handled by the channel.
*/
void ssh_channel_set_counter(ssh_channel channel,
- ssh_counter counter) {
+ ssh_counter counter)
+{
if (channel != NULL) {
channel->counter = counter;
}
@@ -3476,8 +3658,9 @@ void ssh_channel_set_counter(ssh_channel channel,
*
* @see ssh_channel_read()
*/
-int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
- return channel_write_common(channel, data, len, 1);
+int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len)
+{
+ return channel_write_common(channel, data, len, 1);
}
#if WITH_SERVER
@@ -3504,10 +3687,11 @@ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len
*
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
- * use channel_read and channel_write for this.
+ * use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
- int remoteport, const char *sourcehost, int localport) {
+ int remoteport, const char *sourcehost, int localport)
+{
ssh_session session;
ssh_buffer payload = NULL;
int rc = SSH_ERROR;
@@ -3542,7 +3726,7 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost
pending:
rc = channel_open(channel,
"forwarded-tcpip",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -3567,10 +3751,11 @@ error:
* to be done again.
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
- * use channel_read and channel_write for this.
+ * use shh_channel_read and ssh_channel_write for this.
*/
-int ssh_channel_open_x11(ssh_channel channel,
- const char *orig_addr, int orig_port) {
+int ssh_channel_open_x11(ssh_channel channel,
+ const char *orig_addr, int orig_port)
+{
ssh_session session;
ssh_buffer payload = NULL;
int rc = SSH_ERROR;
@@ -3604,7 +3789,7 @@ int ssh_channel_open_x11(ssh_channel channel,
pending:
rc = channel_open(channel,
"x11",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -3619,16 +3804,15 @@ error:
*
* Sends the exit status to the remote process (as described in RFC 4254,
* section 6.10).
- * Only SSH-v2 is supported (I'm not sure about SSH-v1).
*
* @param[in] channel The channel to send exit status.
*
* @param[in] exit_status The exit status to send
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
- * (including attempts to send exit status via SSH-v1 session).
*/
-int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) {
+int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -3660,7 +3844,6 @@ error:
* This sends the exit status of the remote process.
* Note, that remote system may not support signals concept.
* In such a case this request will be silently ignored.
- * Only SSH-v2 is supported (I'm not sure about SSH-v1).
*
* @param[in] channel The channel to send signal.
*
@@ -3671,10 +3854,10 @@ error:
* @param[in] lang The language used in the message (format: RFC 3066)
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
- * (including attempts to send signal via SSH-v1 session).
*/
int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig,
- int core, const char *errmsg, const char *lang) {
+ int core, const char *errmsg, const char *lang)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -3711,4 +3894,4 @@ error:
#endif
-/* @} */
+/** @} */
diff --git a/src/client.c b/src/client.c
index 292c919e..b3d0fe62 100644
--- a/src/client.c
+++ b/src/client.c
@@ -60,7 +60,8 @@
* @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR
* @param user is a pointer to session
*/
-static void socket_callback_connected(int code, int errno_code, void *user){
+static void socket_callback_connected(int code, int errno_code, void *user)
+{
ssh_session session=(ssh_session)user;
if (session->session_state != SSH_SESSION_STATE_CONNECTING &&
@@ -72,12 +73,14 @@ static void socket_callback_connected(int code, int errno_code, void *user){
return;
}
- SSH_LOG(SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code);
+ SSH_LOG(SSH_LOG_TRACE,"Socket connection callback: %d (%d)",code, errno_code);
if(code == SSH_SOCKET_CONNECTED_OK)
session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED;
else {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
session->session_state=SSH_SESSION_STATE_ERROR;
- ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code));
+ ssh_set_error(session,SSH_FATAL,"%s",
+ ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX));
}
session->ssh_connection_callback(session);
}
@@ -93,12 +96,12 @@ static void socket_callback_connected(int code, int errno_code, void *user){
* @param user is a pointer to session
* @returns Number of bytes processed, or zero if the banner is not complete.
*/
-static int callback_receive_banner(const void *data, size_t len, void *user)
+static size_t callback_receive_banner(const void *data, size_t len, void *user)
{
char *buffer = (char *)data;
- ssh_session session=(ssh_session) user;
+ ssh_session session = (ssh_session) user;
char *str = NULL;
- size_t i;
+ uint32_t i;
int ret=0;
if (session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED) {
@@ -106,7 +109,7 @@ static int callback_receive_banner(const void *data, size_t len, void *user)
"Wrong state in callback_receive_banner : %d",
session->session_state);
- return SSH_ERROR;
+ return 0;
}
for (i = 0; i < len; ++i) {
#ifdef WITH_PCAP
@@ -243,10 +246,13 @@ end:
* @warning this function returning is no proof that DH handshake is
* completed
*/
-static int dh_handshake(ssh_session session) {
-
+int dh_handshake(ssh_session session)
+{
int rc = SSH_AGAIN;
+ SSH_LOG(SSH_LOG_TRACE, "dh_handshake_state = %d, kex_type = %d",
+ session->dh_handshake_state, session->next_crypto->kex_type);
+
switch (session->dh_handshake_state) {
case DH_STATE_INIT:
switch(session->next_crypto->kex_type){
@@ -299,18 +305,25 @@ static int dh_handshake(ssh_session session) {
return rc;
}
-static int ssh_service_request_termination(void *s){
- ssh_session session = (ssh_session)s;
- if(session->session_state == SSH_SESSION_STATE_ERROR ||
- session->auth.service_state != SSH_AUTH_SERVICE_SENT)
- return 1;
- else
- return 0;
+static int ssh_service_request_termination(void *s)
+{
+ ssh_session session = (ssh_session)s;
+
+ if (session->session_state == SSH_SESSION_STATE_ERROR ||
+ session->auth.service_state != SSH_AUTH_SERVICE_SENT)
+ return 1;
+ else
+ return 0;
}
/**
- * @internal
+ * @addtogroup libssh_session
*
+ * @{
+ */
+
+/**
+ * @internal
* @brief Request a service from the SSH server.
*
* Service requests are for example: ssh-userauth, ssh-connection, etc.
@@ -323,8 +336,9 @@ static int ssh_service_request_termination(void *s){
* @return SSH_AGAIN No response received yet
* @bug actually only works with ssh-userauth
*/
-int ssh_service_request(ssh_session session, const char *service) {
- int rc=SSH_ERROR;
+int ssh_service_request(ssh_session session, const char *service)
+{
+ int rc = SSH_ERROR;
if(session->auth.service_state != SSH_AUTH_SERVICE_NONE)
goto pending;
@@ -371,12 +385,6 @@ pending:
}
/**
- * @addtogroup libssh_session
- *
- * @{
- */
-
-/**
* @internal
*
* @brief A function to be called each time a step has been done in the
@@ -386,111 +394,117 @@ static void ssh_client_connection_callback(ssh_session session)
{
int rc;
- switch(session->session_state) {
- case SSH_SESSION_STATE_NONE:
- case SSH_SESSION_STATE_CONNECTING:
- break;
- case SSH_SESSION_STATE_SOCKET_CONNECTED:
- ssh_set_fd_towrite(session);
- ssh_send_banner(session, 0);
+ SSH_LOG(SSH_LOG_DEBUG, "session_state=%d", session->session_state);
- break;
- case SSH_SESSION_STATE_BANNER_RECEIVED:
- if (session->serverbanner == NULL) {
- goto error;
- }
- set_status(session, 0.4f);
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SSH server banner: %s", session->serverbanner);
+ switch (session->session_state) {
+ case SSH_SESSION_STATE_NONE:
+ case SSH_SESSION_STATE_CONNECTING:
+ break;
+ case SSH_SESSION_STATE_SOCKET_CONNECTED:
+ ssh_set_fd_towrite(session);
+ ssh_send_banner(session, 0);
- /* Here we analyze the different protocols the server allows. */
- rc = ssh_analyze_banner(session, 0);
- if (rc < 0) {
- ssh_set_error(session, SSH_FATAL,
- "No version of SSH protocol usable (banner: %s)",
- session->serverbanner);
- goto error;
- }
+ break;
+ case SSH_SESSION_STATE_BANNER_RECEIVED:
+ if (session->serverbanner == NULL) {
+ goto error;
+ }
+ set_status(session, 0.4f);
+ SSH_LOG(SSH_LOG_DEBUG, "SSH server banner: %s", session->serverbanner);
+
+ /* Here we analyze the different protocols the server allows. */
+ rc = ssh_analyze_banner(session, 0);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "No version of SSH protocol usable (banner: %s)",
+ session->serverbanner);
+ goto error;
+ }
- ssh_packet_register_socket_callback(session, session->socket);
+ ssh_packet_register_socket_callback(session, session->socket);
- ssh_packet_set_default_callbacks(session);
- session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
+ ssh_packet_set_default_callbacks(session);
+ session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
+ rc = ssh_set_client_kex(session);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ rc = ssh_send_kex(session);
+ if (rc < 0) {
+ goto error;
+ }
+ set_status(session, 0.5f);
+
+ break;
+ case SSH_SESSION_STATE_INITIAL_KEX:
+ /* TODO: This state should disappear in favor of get_key handle */
+ break;
+ case SSH_SESSION_STATE_KEXINIT_RECEIVED:
+ set_status(session, 0.6f);
+ ssh_list_kex(&session->next_crypto->server_kex);
+ if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
+ /* in rekeying state if next_crypto client_kex might be empty */
rc = ssh_set_client_kex(session);
if (rc != SSH_OK) {
goto error;
}
- rc = ssh_send_kex(session, 0);
+ rc = ssh_send_kex(session);
if (rc < 0) {
goto error;
}
- set_status(session, 0.5f);
-
- break;
- case SSH_SESSION_STATE_INITIAL_KEX:
- /* TODO: This state should disappear in favor of get_key handle */
- break;
- case SSH_SESSION_STATE_KEXINIT_RECEIVED:
- set_status(session,0.6f);
- ssh_list_kex(&session->next_crypto->server_kex);
- if (session->next_crypto->client_kex.methods[0] == NULL) {
- /* in rekeying state if next_crypto client_kex is empty */
- rc = ssh_set_client_kex(session);
- if (rc != SSH_OK) {
- goto error;
- }
- rc = ssh_send_kex(session, 0);
- if (rc < 0) {
- goto error;
- }
- }
- if (ssh_kex_select_methods(session) == SSH_ERROR)
- goto error;
- set_status(session,0.8f);
- session->session_state=SSH_SESSION_STATE_DH;
- if (dh_handshake(session) == SSH_ERROR) {
- goto error;
- }
- FALL_THROUGH;
- case SSH_SESSION_STATE_DH:
- if(session->dh_handshake_state==DH_STATE_FINISHED){
- set_status(session,1.0f);
- session->connected = 1;
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
- session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
- else
- session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
- }
- break;
- case SSH_SESSION_STATE_AUTHENTICATING:
- break;
- case SSH_SESSION_STATE_ERROR:
+ }
+ if (ssh_kex_select_methods(session) == SSH_ERROR)
goto error;
- default:
- ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
+ set_status(session, 0.8f);
+ session->session_state = SSH_SESSION_STATE_DH;
+
+ /* If the init packet was already sent in previous step, this will be no
+ * operation */
+ if (dh_handshake(session) == SSH_ERROR) {
+ goto error;
+ }
+ FALL_THROUGH;
+ case SSH_SESSION_STATE_DH:
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
+ set_status(session, 1.0f);
+ session->connected = 1;
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
+ } else {
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATING;
+ }
+ }
+ break;
+ case SSH_SESSION_STATE_AUTHENTICATING:
+ break;
+ case SSH_SESSION_STATE_ERROR:
+ goto error;
+ default:
+ ssh_set_error(session, SSH_FATAL, "Invalid state %d",
+ session->session_state);
}
return;
error:
- ssh_socket_close(session->socket);
- session->alive = 0;
- session->session_state=SSH_SESSION_STATE_ERROR;
-
+ ssh_session_socket_close(session);
+ SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(session));
}
/** @internal
* @brief describe under which conditions the ssh_connect function may stop
*/
-static int ssh_connect_termination(void *user){
- ssh_session session = (ssh_session)user;
- switch(session->session_state){
+static int ssh_connect_termination(void *user)
+{
+ ssh_session session = (ssh_session)user;
+
+ switch (session->session_state) {
case SSH_SESSION_STATE_ERROR:
case SSH_SESSION_STATE_AUTHENTICATING:
case SSH_SESSION_STATE_DISCONNECTED:
- return 1;
+ return 1;
default:
- return 0;
- }
+ return 0;
+ }
}
/**
@@ -558,7 +572,7 @@ int ssh_connect(ssh_session session)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"libssh %s, using threading %s",
ssh_copyright(),
ssh_threads_get_type());
@@ -593,7 +607,7 @@ int ssh_connect(ssh_session session)
set_status(session, 0.2f);
session->alive = 1;
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Socket connecting, now waiting for the callbacks to work");
pending:
@@ -649,12 +663,13 @@ pending:
*
* @return A newly allocated string with the banner, NULL on error.
*/
-char *ssh_get_issue_banner(ssh_session session) {
- if (session == NULL || session->banner == NULL) {
- return NULL;
- }
+char *ssh_get_issue_banner(ssh_session session)
+{
+ if (session == NULL || session->banner == NULL) {
+ return NULL;
+ }
- return ssh_string_to_char(session->banner);
+ return ssh_string_to_char(session->banner);
}
/**
@@ -675,18 +690,79 @@ char *ssh_get_issue_banner(ssh_session session) {
* }
* @endcode
*/
-int ssh_get_openssh_version(ssh_session session) {
- if (session == NULL) {
- return 0;
- }
+int ssh_get_openssh_version(ssh_session session)
+{
+ if (session == NULL) {
+ return 0;
+ }
- return session->openssh;
+ return session->openssh;
}
/**
+ * @brief Most SSH connections will only ever request a single session, but an
+ * attacker may abuse a running ssh client to surreptitiously open
+ * additional sessions under their control. OpenSSH provides a global
+ * request "no-more-sessions@openssh.com" to mitigate this attack.
+ *
+ * @param[in] session The SSH session to use.
+ *
+ * @returns SSH_OK on success, SSH_ERROR on error.
+ * @returns SSH_AGAIN, if the session is in nonblocking mode,
+ * and call must be done again.
+ */
+int ssh_request_no_more_sessions(ssh_session session)
+{
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ return ssh_global_request(session, "no-more-sessions@openssh.com", NULL, 1);
+}
+
+/**
+ * @brief Add disconnect message when ssh_session is disconnected
+ * To add a disconnect message to give peer a better hint.
+ * @param session The SSH session to use.
+ * @param message The message to send after the session is disconnected.
+ * If no message is passed then a default message i.e
+ * "Bye Bye" will be sent.
+ */
+int
+ssh_session_set_disconnect_message(ssh_session session, const char *message)
+{
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (message == NULL || strlen(message) == 0) {
+ SAFE_FREE(session->disconnect_message); //To free any message set earlier.
+ session->disconnect_message = strdup("Bye Bye") ;
+ if (session->disconnect_message == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+ }
+ SAFE_FREE(session->disconnect_message); //To free any message set earlier.
+ session->disconnect_message = strdup(message);
+ if (session->disconnect_message == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+
+/**
* @brief Disconnect from a session (client or server).
+ *
* The session can then be reused to open a new session.
*
+ * @note Note that this function won't close the socket if it was set with
+ * ssh_options_set and SSH_OPTIONS_FD. You're responsible for closing the
+ * socket. This is new behavior in libssh 0.10.
+ *
* @param[in] session The SSH session to use.
*/
void
@@ -699,12 +775,20 @@ ssh_disconnect(ssh_session session)
return;
}
+ if (session->disconnect_message == NULL) {
+ session->disconnect_message = strdup("Bye Bye") ;
+ if (session->disconnect_message == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ }
+
if (session->socket != NULL && ssh_socket_is_open(session->socket)) {
rc = ssh_buffer_pack(session->out_buffer,
"bdss",
SSH2_MSG_DISCONNECT,
SSH2_DISCONNECT_BY_APPLICATION,
- "Bye Bye",
+ session->disconnect_message,
""); /* language tag */
if (rc != SSH_OK) {
ssh_set_error_oom(session);
@@ -712,7 +796,7 @@ ssh_disconnect(ssh_session session)
}
ssh_packet_send(session);
- ssh_socket_close(session->socket);
+ ssh_session_socket_close(session);
}
error:
@@ -756,6 +840,7 @@ error:
session->auth.supported_methods = 0;
SAFE_FREE(session->serverbanner);
SAFE_FREE(session->clientbanner);
+ SAFE_FREE(session->disconnect_message);
if (session->ssh_message_list) {
ssh_message msg = NULL;
@@ -774,8 +859,16 @@ error:
}
}
-const char *ssh_copyright(void) {
- return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2021 "
+/**
+ * @brief Copyright information
+ *
+ * Returns copyright information
+ *
+ * @returns SSH_STRING copyright
+ */
+const char *ssh_copyright(void)
+{
+ return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2024 "
"Aris Adamantiadis, Andreas Schneider "
"and libssh contributors. "
"Distributed under the LGPL, please refer to COPYING "
diff --git a/src/config.c b/src/config.c
index a082e994..7135c3b1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -68,7 +68,6 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "macs", SOC_MACS },
{ "compression", SOC_COMPRESSION },
{ "connecttimeout", SOC_TIMEOUT },
- { "protocol", SOC_PROTOCOL },
{ "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK },
{ "userknownhostsfile", SOC_KNOWNHOSTS },
{ "proxycommand", SOC_PROXYCOMMAND },
@@ -81,7 +80,6 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "loglevel", SOC_LOGLEVEL},
{ "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS},
{ "kexalgorithms", SOC_KEXALGORITHMS},
- { "mac", SOC_UNSUPPORTED}, /* SSHv1 */
{ "gssapiauthentication", SOC_GSSAPIAUTHENTICATION},
{ "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION},
{ "passwordauthentication", SOC_PASSWORDAUTHENTICATION},
@@ -94,24 +92,19 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "canonicalizehostname", SOC_UNSUPPORTED},
{ "canonicalizemaxdots", SOC_UNSUPPORTED},
{ "canonicalizepermittedcnames", SOC_UNSUPPORTED},
- { "certificatefile", SOC_UNSUPPORTED},
- { "challengeresponseauthentication", SOC_UNSUPPORTED},
+ { "certificatefile", SOC_CERTIFICATE},
+ { "kbdinteractiveauthentication", SOC_UNSUPPORTED},
{ "checkhostip", SOC_UNSUPPORTED},
- { "cipher", SOC_UNSUPPORTED}, /* SSHv1 */
- { "compressionlevel", SOC_UNSUPPORTED}, /* SSHv1 */
{ "connectionattempts", SOC_UNSUPPORTED},
{ "enablesshkeysign", SOC_UNSUPPORTED},
{ "fingerprinthash", SOC_UNSUPPORTED},
{ "forwardagent", SOC_UNSUPPORTED},
- { "gssapikeyexchange", SOC_UNSUPPORTED},
- { "gssapirenewalforcesrekey", SOC_UNSUPPORTED},
- { "gssapitrustdns", SOC_UNSUPPORTED},
{ "hashknownhosts", SOC_UNSUPPORTED},
{ "hostbasedauthentication", SOC_UNSUPPORTED},
- { "hostbasedkeytypes", SOC_UNSUPPORTED},
+ { "hostbasedacceptedalgorithms", SOC_UNSUPPORTED},
{ "hostkeyalias", SOC_UNSUPPORTED},
- { "identitiesonly", SOC_UNSUPPORTED},
- { "identityagent", SOC_UNSUPPORTED},
+ { "identitiesonly", SOC_IDENTITIESONLY},
+ { "identityagent", SOC_IDENTITYAGENT},
{ "ipqos", SOC_UNSUPPORTED},
{ "kbdinteractivedevices", SOC_UNSUPPORTED},
{ "nohostauthenticationforlocalhost", SOC_UNSUPPORTED},
@@ -120,12 +113,10 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "preferredauthentications", SOC_UNSUPPORTED},
{ "proxyjump", SOC_PROXYJUMP},
{ "proxyusefdpass", SOC_UNSUPPORTED},
- { "pubkeyacceptedtypes", SOC_PUBKEYACCEPTEDTYPES},
+ { "pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES},
{ "rekeylimit", SOC_REKEYLIMIT},
{ "remotecommand", SOC_UNSUPPORTED},
{ "revokedhostkeys", SOC_UNSUPPORTED},
- { "rhostsrsaauthentication", SOC_UNSUPPORTED},
- { "rsaauthentication", SOC_UNSUPPORTED}, /* SSHv1 */
{ "serveralivecountmax", SOC_UNSUPPORTED},
{ "serveraliveinterval", SOC_UNSUPPORTED},
{ "streamlocalbindmask", SOC_UNSUPPORTED},
@@ -133,13 +124,12 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "syslogfacility", SOC_UNSUPPORTED},
{ "tcpkeepalive", SOC_UNSUPPORTED},
{ "updatehostkeys", SOC_UNSUPPORTED},
- { "useprivilegedport", SOC_UNSUPPORTED},
{ "verifyhostkeydns", SOC_UNSUPPORTED},
{ "visualhostkey", SOC_UNSUPPORTED},
{ "clearallforwardings", SOC_NA},
- { "controlmaster", SOC_NA},
+ { "controlmaster", SOC_CONTROLMASTER},
{ "controlpersist", SOC_NA},
- { "controlpath", SOC_NA},
+ { "controlpath", SOC_CONTROLPATH},
{ "dynamicforward", SOC_NA},
{ "escapechar", SOC_NA},
{ "exitonforwardfailure", SOC_NA},
@@ -157,7 +147,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "tunnel", SOC_NA},
{ "tunneldevice", SOC_NA},
{ "xauthlocation", SOC_NA},
- { "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDTYPES},
+ { "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES},
{ NULL, SOC_UNKNOWN }
};
@@ -191,7 +181,7 @@ static struct ssh_config_match_keyword_table_s ssh_config_match_keyword_table[]
};
static int ssh_config_parse_line(ssh_session session, const char *line,
- unsigned int count, int *parsing);
+ unsigned int count, int *parsing, unsigned int depth, bool global);
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
int i;
@@ -205,16 +195,26 @@ static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
return SOC_UNKNOWN;
}
+#define LIBSSH_CONF_MAX_DEPTH 16
static void
local_parse_file(ssh_session session,
const char *filename,
- int *parsing)
+ int *parsing,
+ unsigned int depth,
+ bool global)
{
FILE *f;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
+ if (depth > LIBSSH_CONF_MAX_DEPTH) {
+ ssh_set_error(session, SSH_FATAL,
+ "ERROR - Too many levels of configuration includes "
+ "when processing file '%s'", filename);
+ return;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
@@ -225,7 +225,7 @@ local_parse_file(ssh_session session,
SSH_LOG(SSH_LOG_PACKET, "Reading additional configuration data from %s", filename);
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_config_parse_line(session, line, count, parsing);
+ rv = ssh_config_parse_line(session, line, count, parsing, depth, global);
if (rv < 0) {
fclose(f);
return;
@@ -239,7 +239,9 @@ local_parse_file(ssh_session session,
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
static void local_parse_glob(ssh_session session,
const char *fileglob,
- int *parsing)
+ int *parsing,
+ unsigned int depth,
+ bool global)
{
glob_t globbuf = {
.gl_flags = 0,
@@ -259,7 +261,7 @@ static void local_parse_glob(ssh_session session,
}
for (i = 0; i < globbuf.gl_pathc; i++) {
- local_parse_file(session, globbuf.gl_pathv[i], parsing);
+ local_parse_file(session, globbuf.gl_pathv[i], parsing, depth, global);
}
globfree(&globbuf);
@@ -317,6 +319,7 @@ ssh_exec_shell(char *cmd)
char *shell = NULL;
pid_t pid;
int status, devnull, rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
shell = getenv("SHELL");
if (shell == NULL || shell[0] == '\0') {
@@ -332,7 +335,8 @@ ssh_exec_shell(char *cmd)
/* Need this to redirect subprocess stdin/out */
devnull = open("/dev/null", O_RDWR);
if (devnull == -1) {
- SSH_LOG(SSH_LOG_WARN, "Failed to open(/dev/null): %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "Failed to open(/dev/null): %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
@@ -344,13 +348,15 @@ ssh_exec_shell(char *cmd)
/* Redirect child stdin and stdout. Leave stderr */
rc = dup2(devnull, STDIN_FILENO);
if (rc == -1) {
- SSH_LOG(SSH_LOG_WARN, "dup2: %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "dup2: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
exit(1);
}
rc = dup2(devnull, STDOUT_FILENO);
if (rc == -1) {
- SSH_LOG(SSH_LOG_WARN, "dup2: %s", strerror(errno));
- exit(1);
+ SSH_LOG(SSH_LOG_WARN, "dup2: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ exit(1);
}
if (devnull > STDERR_FILENO) {
close(devnull);
@@ -364,7 +370,7 @@ ssh_exec_shell(char *cmd)
rc = execv(argv[0], argv);
if (rc == -1) {
SSH_LOG(SSH_LOG_WARN, "Failed to execute command '%s': %s", cmd,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
/* Die with signal to make this error apparent to parent. */
signal(SIGTERM, SIG_DFL);
kill(getpid(), SIGTERM);
@@ -375,19 +381,21 @@ ssh_exec_shell(char *cmd)
/* Parent */
close(devnull);
if (pid == -1) { /* Error */
- SSH_LOG(SSH_LOG_WARN, "Failed to fork child: %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "Failed to fork child: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
- SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
}
if (!WIFEXITED(status)) {
- SSH_LOG(SSH_LOG_WARN, "Command %s exitted abnormally", cmd);
+ SSH_LOG(SSH_LOG_WARN, "Command %s exited abnormally", cmd);
return -1;
}
SSH_LOG(SSH_LOG_TRACE, "Command '%s' returned %d", cmd, WEXITSTATUS(status));
@@ -456,7 +464,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
if (parse_entry) {
/* We actually care only about the first item */
- rv = ssh_config_parse_uri(cp, &username, &hostname, &port);
+ rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
/* The rest of the list needs to be passed on */
if (endp != NULL) {
next = strdup(endp + 1);
@@ -467,7 +475,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} else {
/* The rest is just sanity-checked to avoid failures later */
- rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
+ rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
}
if (rv != SSH_OK) {
goto out;
@@ -483,7 +491,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
if (hostname != NULL && do_parsing) {
char com[512] = {0};
- rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W [%%h]:%%p %s",
+ rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W '[%%h]:%%p' %s",
username ? " -l " : "",
username ? username : "",
port ? " -p " : "",
@@ -492,7 +500,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
next ? next : "",
hostname);
if (rv < 0 || rv >= (int)sizeof(com)) {
- SSH_LOG(SSH_LOG_WARN, "Too long ProxyJump configuration line");
+ SSH_LOG(SSH_LOG_TRACE, "Too long ProxyJump configuration line");
rv = SSH_ERROR;
goto out;
}
@@ -509,11 +517,68 @@ out:
return rv;
}
+static char *
+ssh_config_make_absolute(ssh_session session,
+ const char *path,
+ bool global)
+{
+ size_t outlen = 0;
+ char *out = NULL;
+ int rv;
+
+ /* Looks like absolute path */
+ if (path[0] == '/') {
+ return strdup(path);
+ }
+
+ /* relative path */
+ if (global) {
+ /* Parsing global config */
+ outlen = strlen(path) + strlen("/etc/ssh/") + 1;
+ out = malloc(outlen);
+ if (out == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ rv = snprintf(out, outlen, "/etc/ssh/%s", path);
+ if (rv < 1) {
+ free(out);
+ return NULL;
+ }
+ return out;
+ }
+
+ /* paths starting with tilde are already absolute */
+ if (path[0] == '~') {
+ return ssh_path_expand_tilde(path);
+ }
+
+ /* Parsing user config relative to home directory (generally ~/.ssh) */
+ if (session->opts.sshdir == NULL) {
+ ssh_set_error_invalid(session);
+ return NULL;
+ }
+ outlen = strlen(path) + strlen(session->opts.sshdir) + 1 + 1;
+ out = malloc(outlen);
+ if (out == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ rv = snprintf(out, outlen, "%s/%s", session->opts.sshdir, path);
+ if (rv < 1) {
+ free(out);
+ return NULL;
+ }
+ return out;
+}
+
static int
ssh_config_parse_line(ssh_session session,
const char *line,
unsigned int count,
- int *parsing)
+ int *parsing,
+ unsigned int depth,
+ bool global)
{
enum ssh_config_opcode_e opcode;
const char *p = NULL, *p2 = NULL;
@@ -558,7 +623,9 @@ ssh_config_parse_line(ssh_session session,
opcode != SOC_MATCH &&
opcode != SOC_INCLUDE &&
opcode != SOC_IDENTITY &&
- opcode > SOC_UNSUPPORTED) { /* Ignore all unknown types here */
+ opcode != SOC_CERTIFICATE &&
+ opcode > SOC_UNSUPPORTED &&
+ opcode < SOC_MAX) { /* Ignore all unknown types here */
/* Skip all the options that were already applied */
if (seen[opcode] != 0) {
SAFE_FREE(x);
@@ -572,11 +639,19 @@ ssh_config_parse_line(ssh_session session,
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
+ char *path = ssh_config_make_absolute(session, p, global);
+ if (path == NULL) {
+ SSH_LOG(SSH_LOG_WARN, "line %d: Failed to allocate memory "
+ "for the include path expansion", count);
+ SAFE_FREE(x);
+ return -1;
+ }
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
- local_parse_glob(session, p, parsing);
+ local_parse_glob(session, path, parsing, depth + 1, global);
#else
- local_parse_file(session, p, parsing);
+ local_parse_file(session, path, parsing, depth + 1, global);
#endif /* HAVE_GLOB */
+ free(path);
}
break;
@@ -594,7 +669,7 @@ ssh_config_parse_line(ssh_session session,
break;
}
args++;
- SSH_LOG(SSH_LOG_TRACE, "line %d: Processing Match keyword '%s'",
+ SSH_LOG(SSH_LOG_DEBUG, "line %d: Processing Match keyword '%s'",
count, p);
/* If the option is prefixed with ! the result should be negated */
@@ -626,7 +701,7 @@ ssh_config_parse_line(ssh_session session,
case MATCH_FINAL:
case MATCH_CANONICAL:
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"line %d: Unsupported Match keyword '%s', skipping",
count,
p);
@@ -638,13 +713,13 @@ ssh_config_parse_line(ssh_session session,
/* Skip one argument (including in quotes) */
p = ssh_config_get_token(&s);
if (p == NULL || p[0] == '\0') {
- SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument", count, p2);
SAFE_FREE(x);
return -1;
}
if (result != 1) {
- SSH_LOG(SSH_LOG_INFO, "line %d: Skipped match exec "
+ SSH_LOG(SSH_LOG_DEBUG, "line %d: Skipped match exec "
"'%s' as previous conditions already failed.",
count, p2);
continue;
@@ -665,7 +740,7 @@ ssh_config_parse_line(ssh_session session,
}
localuser = ssh_get_local_username();
if (localuser == NULL) {
- SSH_LOG(SSH_LOG_WARN, "line %d: Can not get local username "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Can not get local username "
"for conditional matching.", count);
SAFE_FREE(x);
return -1;
@@ -679,13 +754,13 @@ ssh_config_parse_line(ssh_session session,
/* Skip one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
- SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument", count, p2);
SAFE_FREE(x);
return -1;
}
args++;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Unsupported Match keyword '%s', ignoring",
count,
p2);
@@ -815,34 +890,6 @@ ssh_config_parse_line(ssh_session session,
}
}
break;
- case SOC_PROTOCOL:
- p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- char *a, *b;
- b = strdup(p);
- if (b == NULL) {
- SAFE_FREE(x);
- ssh_set_error_oom(session);
- return -1;
- }
- i = 0;
- ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
-
- for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
- switch (atoi(a)) {
- case 1:
- break;
- case 2:
- i = 1;
- ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
- break;
- default:
- break;
- }
- }
- SAFE_FREE(b);
- }
- break;
case SOC_TIMEOUT:
l = ssh_config_get_long(&s, -1);
if (l >= 0 && *parsing) {
@@ -920,10 +967,10 @@ ssh_config_parse_line(ssh_session session,
if (strcasecmp(p, "quiet") == 0) {
value = SSH_LOG_NONE;
} else if (strcasecmp(p, "fatal") == 0 ||
- strcasecmp(p, "error")== 0 ||
- strcasecmp(p, "info") == 0) {
+ strcasecmp(p, "error")== 0) {
value = SSH_LOG_WARN;
- } else if (strcasecmp(p, "verbose") == 0) {
+ } else if (strcasecmp(p, "verbose") == 0 ||
+ strcasecmp(p, "info") == 0) {
value = SSH_LOG_INFO;
} else if (strcasecmp(p, "DEBUG") == 0 ||
strcasecmp(p, "DEBUG1") == 0) {
@@ -943,7 +990,7 @@ ssh_config_parse_line(ssh_session session,
ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
}
break;
- case SOC_PUBKEYACCEPTEDTYPES:
+ case SOC_PUBKEYACCEPTEDKEYTYPES:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
@@ -961,20 +1008,20 @@ ssh_config_parse_line(ssh_session session,
if (p == NULL) {
break;
} else if (strcmp(p, "default") == 0) {
- /* Default rekey limits enforced automaticaly */
+ /* Default rekey limits enforced automatically */
ll = 0;
} else {
char *endp = NULL;
ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) {
/* No number or negative */
- SSH_LOG(SSH_LOG_WARN, "Invalid argument to rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
break;
}
switch (*endp) {
case 'G':
if (ll > LLONG_MAX / 1024) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -982,7 +1029,7 @@ ssh_config_parse_line(ssh_session session,
FALL_THROUGH;
case 'M':
if (ll > LLONG_MAX / 1024) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -990,7 +1037,7 @@ ssh_config_parse_line(ssh_session session,
FALL_THROUGH;
case 'K':
if (ll > LLONG_MAX / 1024) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1006,7 +1053,7 @@ ssh_config_parse_line(ssh_session session,
break;
}
if (*endp != ' ' && *endp != '\0') {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid trailing characters after the rekey limit: %s",
endp);
break;
@@ -1027,14 +1074,14 @@ ssh_config_parse_line(ssh_session session,
ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) {
/* No number or negative */
- SSH_LOG(SSH_LOG_WARN, "Invalid argument to rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
break;
}
switch (*endp) {
case 'w':
case 'W':
if (ll > LLONG_MAX / 7) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1043,7 +1090,7 @@ ssh_config_parse_line(ssh_session session,
case 'd':
case 'D':
if (ll > LLONG_MAX / 24) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1052,7 +1099,7 @@ ssh_config_parse_line(ssh_session session,
case 'h':
case 'H':
if (ll > LLONG_MAX / 60) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1061,7 +1108,7 @@ ssh_config_parse_line(ssh_session session,
case 'm':
case 'M':
if (ll > LLONG_MAX / 60) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1080,7 +1127,7 @@ ssh_config_parse_line(ssh_session session,
break;
}
if (*endp != '\0') {
- SSH_LOG(SSH_LOG_WARN, "Invalid trailing characters after the"
+ SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the"
" rekey limit: %s", endp);
break;
}
@@ -1116,17 +1163,68 @@ ssh_config_parse_line(ssh_session session,
}
break;
case SOC_NA:
- SSH_LOG(SSH_LOG_INFO, "Unapplicable option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d",
keyword, count);
break;
case SOC_UNSUPPORTED:
- SSH_LOG(SSH_LOG_INFO, "Unsupported option: %s, line: %d",
+ SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d",
keyword, count);
break;
case SOC_UNKNOWN:
- SSH_LOG(SSH_LOG_INFO, "Unknown option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
keyword, count);
break;
+ case SOC_IDENTITYAGENT:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p);
+ }
+ break;
+ case SOC_IDENTITIESONLY:
+ i = ssh_config_get_yesno(&s, -1);
+ if (i >= 0 && *parsing) {
+ bool b = i;
+ ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
+ }
+ break;
+ case SOC_CONTROLMASTER:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ int value = -1;
+
+ if (strcasecmp(p, "auto") == 0) {
+ value = SSH_CONTROL_MASTER_AUTO;
+ } else if (strcasecmp(p, "yes") == 0) {
+ value = SSH_CONTROL_MASTER_YES;
+ } else if (strcasecmp(p, "no") == 0) {
+ value = SSH_CONTROL_MASTER_NO;
+ } else if (strcasecmp(p, "autoask") == 0) {
+ value = SSH_CONTROL_MASTER_AUTOASK;
+ } else if (strcasecmp(p, "ask") == 0) {
+ value = SSH_CONTROL_MASTER_ASK;
+ }
+
+ if (value != -1) {
+ ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
+ }
+ }
+ break;
+ case SOC_CONTROLPATH:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p == NULL) {
+ SAFE_FREE(x);
+ return -1;
+ }
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, p);
+ }
+ break;
+ case SOC_CERTIFICATE:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
+ }
+ break;
default:
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
opcode);
@@ -1152,18 +1250,24 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
unsigned int count = 0;
FILE *f;
int parsing, rv;
+ bool global = 0;
f = fopen(filename, "r");
if (f == NULL) {
return 0;
}
+ rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
+ if (rv == 0) {
+ global = true;
+ }
+
SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
parsing = 1;
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_config_parse_line(session, line, count, &parsing);
+ rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
if (rv < 0) {
fclose(f);
return -1;
@@ -1198,24 +1302,24 @@ int ssh_config_parse_string(ssh_session session, const char *input)
line_start = c;
c = strchr(line_start, '\n');
if (c == NULL) {
- /* if there is no newline in the end of the string */
+ /* if there is no newline at the end of the string */
c = strchr(line_start, '\0');
}
if (c == NULL) {
/* should not happen, would mean a string without trailing '\0' */
- SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string");
+ SSH_LOG(SSH_LOG_TRACE, "No trailing '\\0' in config string");
return SSH_ERROR;
}
line_len = c - line_start;
if (line_len > MAX_LINE_SIZE - 1) {
- SSH_LOG(SSH_LOG_WARN, "Line %u too long: %u characters",
+ SSH_LOG(SSH_LOG_TRACE, "Line %u too long: %u characters",
line_num, line_len);
return SSH_ERROR;
}
memcpy(line, line_start, line_len);
line[line_len] = '\0';
SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
- rv = ssh_config_parse_line(session, line, line_num, &parsing);
+ rv = ssh_config_parse_line(session, line, line_num, &parsing, 0, false);
if (rv < 0) {
return SSH_ERROR;
}
diff --git a/src/config_parser.c b/src/config_parser.c
index 2f91d39f..bd6ab9d7 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -30,9 +30,10 @@
#include "libssh/config_parser.h"
#include "libssh/priv.h"
+#include "libssh/misc.h"
/* Returns the original string after skipping the leading whitespace
- * and optional quotes.
+ * until finding LF.
* This is useful in case we need to get the rest of the line (for example
* external command).
*/
@@ -48,15 +49,6 @@ char *ssh_config_get_cmd(char **str)
}
}
- if (*c == '\"') {
- for (r = ++c; *c; c++) {
- if (*c == '\"') {
- *c = '\0';
- goto out;
- }
- }
- }
-
for (r = c; *c; c++) {
if (*c == '\n') {
*c = '\0';
@@ -170,12 +162,14 @@ int ssh_config_get_yesno(char **str, int notfound)
}
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port)
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port)
{
char *endp = NULL;
long port_n;
+ int rc;
/* Sanitize inputs */
if (username != NULL) {
@@ -189,7 +183,7 @@ int ssh_config_parse_uri(const char *tok,
}
/* Username part (optional) */
- endp = strchr(tok, '@');
+ endp = strrchr(tok, '@');
if (endp != NULL) {
/* Zero-length username is not valid */
if (tok == endp) {
@@ -200,6 +194,10 @@ int ssh_config_parse_uri(const char *tok,
if (*username == NULL) {
goto error;
}
+ rc = ssh_check_username_syntax(*username);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
tok = endp + 1;
/* If there is second @ character, this does not look like our URI */
@@ -217,12 +215,17 @@ int ssh_config_parse_uri(const char *tok,
if (endp == NULL) {
goto error;
}
- } else {
- /* Hostnames or aliases expand to the last colon or to the end */
+ } else if (!ignore_port) {
+ /* Hostnames or aliases expand to the last colon (if port is requested)
+ * or to the end */
endp = strrchr(tok, ':');
if (endp == NULL) {
endp = strchr(tok, '\0');
}
+ } else {
+ /* If no port is requested, expand to the end of line
+ * (to accommodate the IPv6 addresses) */
+ endp = strchr(tok, '\0');
}
if (tok == endp) {
/* Zero-length hostnames are not valid */
@@ -233,6 +236,14 @@ int ssh_config_parse_uri(const char *tok,
if (*hostname == NULL) {
goto error;
}
+ /* if not an ip, check syntax */
+ rc = ssh_is_ipaddr(*hostname);
+ if (rc == 0) {
+ rc = ssh_check_hostname_syntax(*hostname);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
}
/* Skip also the closing bracket */
if (*endp == ']') {
@@ -246,7 +257,7 @@ int ssh_config_parse_uri(const char *tok,
/* Verify the port is valid positive number */
port_n = strtol(endp + 1, &port_end, 10);
if (port_n < 1 || *port_end != '\0') {
- SSH_LOG(SSH_LOG_WARN, "Failed to parse port number."
+ SSH_LOG(SSH_LOG_TRACE, "Failed to parse port number."
" The value '%ld' is invalid or there are some"
" trailing characters: '%s'", port_n, port_end);
goto error;
diff --git a/src/connect.c b/src/connect.c
index ce4d58df..dd3bbcf5 100644
--- a/src/connect.c
+++ b/src/connect.c
@@ -54,11 +54,6 @@
#define NTDDI_VERSION 0x05010000 /* NTDDI_WINXP */
#endif
-#if _MSC_VER >= 1400
-#include <io.h>
-#undef close
-#define close _close
-#endif /* _MSC_VER */
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -136,7 +131,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
#endif
}
- if (ssh_is_ipaddr(host)) {
+ if (ssh_is_ipaddr(host) == 1) {
/* this is an IP address */
SSH_LOG(SSH_LOG_PACKET, "host %s matches an IP address", host);
hints.ai_flags |= AI_NUMERICHOST;
@@ -168,7 +163,7 @@ static int set_tcp_nodelay(socket_t socket)
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
const char *bind_addr, int port)
{
- socket_t s = -1;
+ socket_t s = -1, first = -1;
int rc;
struct addrinfo *ai = NULL;
struct addrinfo *itr = NULL;
@@ -183,11 +178,13 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
}
for (itr = ai; itr != NULL; itr = itr->ai_next) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
/* create socket */
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
if (s < 0) {
ssh_set_error(session, SSH_FATAL,
- "Socket create failed: %s", strerror(errno));
+ "Socket create failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
continue;
}
@@ -214,7 +211,8 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
{
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
ssh_set_error(session, SSH_FATAL,
- "Binding local address: %s", strerror(errno));
+ "Binding local address: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
continue;
} else {
break;
@@ -246,7 +244,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to set TCP_NODELAY on socket: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
ssh_connect_socket_close(s);
s = -1;
continue;
@@ -255,11 +253,22 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
errno = 0;
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
- if (rc == -1 && (errno != 0) && (errno != EINPROGRESS)) {
- ssh_set_error(session, SSH_FATAL,
- "Failed to connect: %s", strerror(errno));
- ssh_connect_socket_close(s);
- s = -1;
+ if (rc == -1) {
+ if ((errno != 0) && (errno != EINPROGRESS)) {
+ ssh_set_error(session, SSH_FATAL,
+ "Failed to connect: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ ssh_connect_socket_close(s);
+ s = -1;
+ } else {
+ if (first == -1) {
+ first = s;
+ } else { /* errno == EINPROGRESS */
+ /* save only the first "working" socket */
+ ssh_connect_socket_close(s);
+ s = -1;
+ }
+ }
continue;
}
@@ -268,6 +277,12 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
freeaddrinfo(ai);
+ /* first let's go through all the addresses looking for immediate
+ * connection, otherwise return the first address without error or error */
+ if (s == -1) {
+ s = first;
+ }
+
return s;
}
@@ -289,14 +304,14 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata)
/**
* @brief A wrapper for the select syscall
*
- * This functions acts more or less like the select(2) syscall.\n
+ * This function acts more or less like the select(2) syscall.\n
* There is no support for writing or exceptions.\n
*
* @param[in] channels Arrays of channels pointers terminated by a NULL.
* It is never rewritten.
*
- * @param[out] outchannels Arrays of same size that "channels", there is no need
- * to initialize it.
+ * @param[out] outchannels Arrays of the same size as "channels", there is no
+ * need to initialize it.
*
* @param[in] maxfd Maximum +1 file descriptor from readfds.
*
diff --git a/src/connector.c b/src/connector.c
index 4e96b200..21c4d79b 100644
--- a/src/connector.c
+++ b/src/connector.c
@@ -34,19 +34,7 @@
#define CHUNKSIZE 4096
#endif
-#ifdef _WIN32
-# ifdef HAVE_IO_H
-# include <io.h>
-# undef open
-# define open _open
-# undef close
-# define close _close
-# undef read
-# define read _read
-# undef unlink
-# define unlink _unlink
-# endif /* HAVE_IO_H */
-#else
+#ifndef _WIN32
# include <sys/types.h>
# include <sys/socket.h>
#endif
@@ -85,7 +73,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
void *userdata);
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
- size_t bytes,
+ uint32_t bytes,
void *userdata);
static ssize_t ssh_connector_fd_read(ssh_connector connector,
void *buffer,
@@ -216,7 +204,7 @@ static void ssh_connector_except_channel(ssh_connector connector,
/**
* @internal
*
- * @brief Reset the poll events to be followed for each file descriptors.
+ * @brief Reset the poll events to be followed for each file descriptor.
*/
static void ssh_connector_reset_pollevents(ssh_connector connector)
{
@@ -248,14 +236,14 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
uint32_t toread = CHUNKSIZE;
ssize_t r;
ssize_t w;
- int total = 0;
+ ssize_t total = 0;
int rc;
SSH_LOG(SSH_LOG_TRACE, "connector POLLIN event for fd %d", connector->in_fd);
if (connector->out_wontblock) {
if (connector->out_channel != NULL) {
- size_t size = ssh_channel_window_size(connector->out_channel);
+ uint32_t size = ssh_channel_window_size(connector->out_channel);
/* Don't attempt reading more than the window */
toread = MIN(size, CHUNKSIZE);
@@ -326,35 +314,40 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
/** @internal
* @brief Callback called when a poll event is received on an output fd
*/
-static void ssh_connector_fd_out_cb(ssh_connector connector){
+static void
+ssh_connector_fd_out_cb(ssh_connector connector)
+{
unsigned char buffer[CHUNKSIZE];
- int r;
- int w;
- int total = 0;
- SSH_LOG(SSH_LOG_TRACE, "connector POLLOUT event for fd %d", connector->out_fd);
+ ssize_t r;
+ ssize_t w;
+ ssize_t total = 0;
+ SSH_LOG(SSH_LOG_TRACE, "connector POLLOUT event for fd %d",
+ connector->out_fd);
- if(connector->in_available){
- if (connector->in_channel != NULL){
- r = ssh_channel_read_nonblocking(connector->in_channel, buffer, CHUNKSIZE, 0);
- if(r == SSH_ERROR){
+ if (connector->in_available) {
+ if (connector->in_channel != NULL) {
+ r = ssh_channel_read_nonblocking(connector->in_channel, buffer,
+ CHUNKSIZE, 0);
+ if (r == SSH_ERROR) {
ssh_connector_except_channel(connector, connector->in_channel);
return;
- } else if(r == 0 && ssh_channel_is_eof(connector->in_channel)){
+ } else if (r == 0 && ssh_channel_is_eof(connector->in_channel)) {
close(connector->out_fd);
connector->out_fd = SSH_INVALID_SOCKET;
return;
- } else if(r>0) {
+ } else if (r > 0) {
/* loop around write in case the write blocks even for CHUNKSIZE bytes */
- while (total != r){
- w = ssh_connector_fd_write(connector, buffer + total, r - total);
- if (w < 0){
+ while (total != r) {
+ w = ssh_connector_fd_write(connector, buffer + total,
+ r - total);
+ if (w < 0) {
ssh_connector_except(connector, connector->out_fd);
return;
}
total += w;
}
}
- } else if (connector->in_fd != SSH_INVALID_SOCKET){
+ } else if (connector->in_fd != SSH_INVALID_SOCKET) {
/* fallback on the socket input callback */
connector->out_wontblock = 1;
ssh_connector_fd_in_cb(connector);
@@ -432,7 +425,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
{
ssh_connector connector = userdata;
int w;
- size_t window;
+ uint32_t window;
(void) session;
(void) channel;
@@ -453,7 +446,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
if (connector->out_wontblock) {
if (connector->out_channel != NULL) {
- int window_len;
+ uint32_t window_len;
window = ssh_channel_window_size(connector->out_channel);
window_len = MIN(window, len);
@@ -517,7 +510,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
*/
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
- size_t bytes,
+ uint32_t bytes,
void *userdata)
{
ssh_connector connector = userdata;
@@ -529,7 +522,7 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
SSH_LOG(SSH_LOG_TRACE, "Channel write won't block");
if (connector->in_available) {
if (connector->in_channel != NULL) {
- size_t len = MIN(CHUNKSIZE, bytes);
+ uint32_t len = MIN(CHUNKSIZE, bytes);
r = ssh_channel_read_nonblocking(connector->in_channel,
buffer,
diff --git a/src/crypto_common.c b/src/crypto_common.c
index 8213ddb9..5dc883f6 100644
--- a/src/crypto_common.c
+++ b/src/crypto_common.c
@@ -23,12 +23,14 @@
int secure_memcmp(const void *s1, const void *s2, size_t n)
{
- int rc = 0;
- const unsigned char *p1 = s1;
- const unsigned char *p2 = s2;
- for (; n > 0; --n) {
- rc |= *p1++ ^ *p2++;
+ size_t i;
+ uint8_t status = 0;
+ const uint8_t *p1 = s1;
+ const uint8_t *p2 = s2;
+
+ for (i = 0; i < n; i++) {
+ status |= (p1[i] ^ p2[i]);
}
- return (rc != 0);
-}
+ return (status != 0);
+}
diff --git a/src/curve25519.c b/src/curve25519.c
index d2517551..3f57f25d 100644
--- a/src/curve25519.c
+++ b/src/curve25519.c
@@ -39,7 +39,7 @@
#include "libssh/pki.h"
#include "libssh/bignum.h"
-#ifdef HAVE_OPENSSL_X25519
+#ifdef HAVE_LIBCRYPTO
#include <openssl/err.h>
#endif
@@ -59,7 +59,7 @@ static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = {
static int ssh_curve25519_init(ssh_session session)
{
int rc;
-#ifdef HAVE_OPENSSL_X25519
+#ifdef HAVE_LIBCRYPTO
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL;
size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
@@ -136,7 +136,7 @@ static int ssh_curve25519_init(ssh_session session)
crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey,
session->next_crypto->curve25519_privkey);
}
-#endif /* HAVE_OPENSSL_X25519 */
+#endif /* HAVE_LIBCRYPTO */
return SSH_OK;
}
@@ -172,11 +172,16 @@ int ssh_client_curve25519_init(ssh_session session)
return rc;
}
+void ssh_client_curve25519_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
+}
+
static int ssh_curve25519_build_k(ssh_session session)
{
ssh_curve25519_pubkey k;
-#ifdef HAVE_OPENSSL_X25519
+#ifdef HAVE_LIBCRYPTO
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL, *pubkey = NULL;
size_t shared_key_len = sizeof(k);
@@ -255,7 +260,7 @@ out:
crypto_scalarmult(k, session->next_crypto->curve25519_privkey,
session->next_crypto->curve25519_server_pubkey);
}
-#endif /* HAVE_OPENSSL_X25519 */
+#endif /* HAVE_LIBCRYPTO */
bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret);
if (session->next_crypto->shared_secret == NULL) {
@@ -285,7 +290,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
+ ssh_client_curve25519_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) {
@@ -330,16 +335,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
@@ -381,7 +380,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
ssh_string q_s_string = NULL;
ssh_string server_pubkey_blob = NULL;
- /* SSH host keys (rsa,dsa,ecdsa) */
+ /* SSH host keys (rsa, ed25519 and ecdsa) */
ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
@@ -408,8 +407,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
memcpy(session->next_crypto->curve25519_client_pubkey,
ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE);
SSH_STRING_FREE(q_c_string);
- /* Build server's keypair */
+ /* Build server's key pair */
rc = ssh_curve25519_init(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Failed to generate curve25519 keys");
@@ -491,24 +490,19 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_ECDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_ECDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
return SSH_ERROR;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
diff --git a/src/dh-gex.c b/src/dh-gex.c
index c6295f8f..4e59073f 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -37,7 +37,7 @@
#include "libssh/buffer.h"
#include "libssh/session.h"
-/* Minimum, recommanded and maximum size of DH group */
+/* Minimum, recommended and maximum size of DH group */
#define DH_PMIN 2048
#define DH_PREQ 2048
#define DH_PMAX 8192
@@ -108,11 +108,15 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
bignum pmin1 = NULL, one = NULL;
bignum_CTX ctx = bignum_ctx_new();
bignum modulus = NULL, generator = NULL;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pubkey;
+#else
+ bignum pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
(void) type;
(void) user;
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_GROUP received");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_DH_GEX_GROUP received");
if (bignum_ctx_invalid(ctx)) {
goto error;
@@ -212,6 +216,9 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
session->dh_handshake_state = DH_STATE_INIT_SENT;
@@ -229,6 +236,9 @@ error:
bignum_safe_free(generator);
bignum_safe_free(one);
bignum_safe_free(pmin1);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
if(!bignum_ctx_invalid(ctx)) {
bignum_ctx_free(ctx);
}
@@ -238,6 +248,11 @@ error:
return SSH_PACKET_USED;
}
+void ssh_client_dhgex_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+}
+
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
{
struct ssh_crypto_struct *crypto=session->next_crypto;
@@ -246,9 +261,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
bignum server_pubkey = NULL;
(void)type;
(void)user;
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_REPLY received");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_DH_GEX_REPLY received");
- ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+ ssh_client_dhgex_remove_callbacks(session);
rc = ssh_buffer_unpack(packet,
"SBS",
&pubkey_blob, &server_pubkey,
@@ -282,15 +297,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc = ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
@@ -367,9 +377,9 @@ static bool dhgroup_better_size(uint32_t pmin,
* @brief returns 1 with 1/n probability
* @returns 1 on with P(1/n), 0 with P(n-1/n).
*/
-static bool invn_chance(int n)
+static bool invn_chance(size_t n)
{
- uint32_t nounce = 0;
+ size_t nounce = 0;
int ok;
ok = ssh_get_random(&nounce, sizeof(nounce), 0);
@@ -425,7 +435,7 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
if (rc == EOF) {
break;
}
- SSH_LOG(SSH_LOG_INFO, "Invalid moduli entry line %zu", line);
+ SSH_LOG(SSH_LOG_DEBUG, "Invalid moduli entry line %zu", line);
do {
firstbyte = getc(moduli);
} while(firstbyte != '\n' && firstbyte != EOF);
@@ -463,14 +473,14 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
}
}
if (*best_size != 0) {
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Selected %zu bits modulus out of %zu candidates in %zu lines",
*best_size,
best_nlines - 1,
line);
} else {
- SSH_LOG(SSH_LOG_WARNING,
- "No moduli found for [%u:%u:%u]",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "No moduli found for [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
pmin,
pn,
pmax);
@@ -515,10 +525,11 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
moduli = fopen(MODULI_FILE, "r");
if (moduli == NULL) {
- SSH_LOG(SSH_LOG_WARNING,
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ SSH_LOG(SSH_LOG_DEBUG,
"Unable to open moduli file: %s",
- strerror(errno));
- return ssh_fallback_group(pmax, p, g);
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ return ssh_fallback_group(pmax, p, g);
}
*size = 0;
@@ -610,12 +621,12 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
ssh_set_error_invalid(session);
goto error;
}
- SSH_LOG(SSH_LOG_INFO, "dh-gex: DHGEX_REQUEST[%u:%u:%u]", pmin, pn, pmax);
+ SSH_LOG(SSH_LOG_DEBUG, "dh-gex: DHGEX_REQUEST[%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]", pmin, pn, pmax);
if (pmin > pn || pn > pmax || pn > DH_PMAX || pmax < DH_PMIN) {
ssh_set_error(session,
SSH_FATAL,
- "Invalid dh-gex arguments [%u:%u:%u]",
+ "Invalid dh-gex arguments [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
pmin,
pn,
pmax);
@@ -642,7 +653,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
if (rc == SSH_ERROR) {
ssh_set_error(session,
SSH_FATAL,
- "Couldn't find DH group for [%u:%u:%u]",
+ "Couldn't find DH group for [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
pmin,
pn,
pmax);
diff --git a/src/dh.c b/src/dh.c
index 18b71734..e19e43d1 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -25,6 +25,8 @@
#include "config.h"
+#include <stdio.h>
+
#include "libssh/priv.h"
#include "libssh/crypto.h"
#include "libssh/buffer.h"
@@ -309,7 +311,11 @@ static struct ssh_packet_callbacks_struct ssh_dh_client_callbacks = {
*/
int ssh_client_dh_init(ssh_session session){
struct ssh_crypto_struct *crypto = session->next_crypto;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pubkey;
+#else
+ bignum pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
int rc;
rc = ssh_dh_init_common(crypto);
@@ -330,6 +336,9 @@ int ssh_client_dh_init(ssh_session session){
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_dh_client_callbacks);
@@ -338,10 +347,18 @@ int ssh_client_dh_init(ssh_session session){
rc = ssh_packet_send(session);
return rc;
error:
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif
ssh_dh_cleanup(crypto);
return SSH_ERROR;
}
+void ssh_client_dh_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dh_client_callbacks);
+}
+
SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
struct ssh_crypto_struct *crypto=session->next_crypto;
ssh_string pubkey_blob = NULL;
@@ -351,7 +368,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_dh_client_callbacks);
+ ssh_client_dh_remove_callbacks(session);
rc = ssh_buffer_unpack(packet, "SBS", &pubkey_blob, &server_pubkey,
&crypto->dh_server_signature);
@@ -370,7 +387,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
if (rc != 0) {
goto error;
}
-
+
rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx,
DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR,
&session->next_crypto->shared_secret);
@@ -381,16 +398,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
@@ -436,7 +447,11 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
bignum client_pubkey;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum server_pubkey;
+#else
+ bignum server_pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
int packet_type;
int rc;
@@ -516,6 +531,9 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
sig_blob);
SSH_STRING_FREE(sig_blob);
SSH_STRING_FREE(pubkey_blob);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(server_pubkey);
+#endif
if(rc != SSH_OK) {
ssh_set_error_oom(session);
ssh_buffer_reinit(session->out_buffer);
@@ -527,20 +545,20 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
}
SSH_LOG(SSH_LOG_DEBUG, "Sent KEX_DH_[GEX]_REPLY");
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- ssh_buffer_reinit(session->out_buffer);
- goto error;
- }
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
- if (ssh_packet_send(session) == SSH_ERROR) {
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent");
return SSH_OK;
error:
SSH_STRING_FREE(sig_blob);
SSH_STRING_FREE(pubkey_blob);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(server_pubkey);
+#endif
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_dh_cleanup(session->next_crypto);
@@ -640,7 +658,7 @@ ssh_key ssh_dh_get_current_server_publickey(ssh_session session)
return session->current_crypto->server_pubkey;
}
-/* Caller need to free the blob */
+/* Caller needs to free the blob */
int ssh_dh_get_current_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob)
{
@@ -654,7 +672,7 @@ ssh_key ssh_dh_get_next_server_publickey(ssh_session session)
return session->next_crypto->server_pubkey;
}
-/* Caller need to free the blob */
+/* Caller needs to free the blob */
int ssh_dh_get_next_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob)
{
@@ -683,7 +701,7 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
char *b64_unpadded = NULL;
size_t k;
- b64_padded = (char *)bin_to_base64(hash, (int)len);
+ b64_padded = (char *)bin_to_base64(hash, len);
if (b64_padded == NULL) {
return NULL;
}
@@ -699,7 +717,7 @@ 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. If it is a SHA sum, it will
- * return an unpadded base64 strings. If it is a MD5 sum, it will return hex
+ * return an unpadded base64 string. If it is a MD5 sum, it will return a hex
* string. Either way, the output is prepended by the hash-type.
*
* @warning Do NOT use MD5 or SHA1! Those hash functions are being deprecated.
@@ -711,7 +729,8 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
*
* @param len Length of the buffer to convert.
*
- * @return Returns the allocated fingerprint hash or NULL on error.
+ * @return Returns the allocated fingerprint hash or NULL on error. The caller
+ * needs to free the memory using ssh_string_free_char().
*
* @see ssh_string_free_char()
*/
diff --git a/src/dh_crypto.c b/src/dh_crypto.c
index 3b3495c1..9ff7ad3c 100644
--- a/src/dh_crypto.c
+++ b/src/dh_crypto.c
@@ -30,6 +30,13 @@
#include "openssl/crypto.h"
#include "openssl/dh.h"
#include "libcrypto-compat.h"
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/evp.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <openssl/err.h>
+#endif /* OPENSSL_VERSION_NUMBER */
extern bignum ssh_dh_generator;
extern bignum ssh_dh_group1;
@@ -38,13 +45,21 @@ extern bignum ssh_dh_group16;
extern bignum ssh_dh_group18;
struct dh_ctx {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
DH *keypair[2];
+#else
+ EVP_PKEY *keypair[2];
+#endif /* OPENSSL_VERSION_NUMBER */
};
void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
{
#ifdef DEBUG_CRYPTO
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum x = NULL, y = NULL, e = NULL, f = NULL;
+#else
+ bignum x = NULL, y = NULL, e = NULL, f = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
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);
@@ -52,6 +67,12 @@ void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
ssh_print_bignum("y", y);
ssh_print_bignum("e", e);
ssh_print_bignum("f", f);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(x);
+ bignum_safe_free(y);
+ bignum_safe_free(e);
+ bignum_safe_free(f);
+#endif /* OPENSSL_VERSION_NUMBER */
ssh_log_hexdump("Session server cookie", c->server_kex.cookie, 16);
ssh_log_hexdump("Session client cookie", c->client_kex.cookie, 16);
@@ -59,9 +80,10 @@ void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
#else
(void)c; /* UNUSED_PARAM */
-#endif
+#endif /* DEBUG_CRYPTO */
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
const_bignum *priv, const_bignum *pub)
{
@@ -70,22 +92,76 @@ int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
(ctx->keypair[peer] == NULL)) {
return SSH_ERROR;
}
+
DH_get0_key(ctx->keypair[peer], pub, priv);
+
+ if (priv && (*priv == NULL || bignum_num_bits(*priv) == 0)) {
+ return SSH_ERROR;
+ }
+ if (pub && (*pub == NULL || bignum_num_bits(*pub) == 0)) {
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+#else
+/* If set *priv and *pub should be initialized
+ * to NULL before calling this function*/
+int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
+ bignum *priv, bignum *pub)
+{
+ int rc;
+ if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) ||
+ ((priv == NULL) && (pub == NULL)) || (ctx == NULL) ||
+ (ctx->keypair[peer] == NULL)) {
+ return SSH_ERROR;
+ }
+
+ if (priv) {
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[peer],
+ OSSL_PKEY_PARAM_PRIV_KEY,
+ priv);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ }
+ if (pub) {
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[peer],
+ OSSL_PKEY_PARAM_PUB_KEY,
+ pub);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ }
if (priv && (*priv == NULL || bignum_num_bits(*priv) == 0)) {
+ if (pub && (*pub != NULL && bignum_num_bits(*pub) != 0)) {
+ bignum_safe_free(*pub);
+ *pub = NULL;
+ }
return SSH_ERROR;
}
if (pub && (*pub == NULL || bignum_num_bits(*pub) == 0)) {
+ if (priv) {
+ bignum_safe_free(*priv);
+ *priv = NULL;
+ }
return SSH_ERROR;
}
return SSH_OK;
}
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
- const bignum priv, const bignum pub)
+ bignum priv, bignum pub)
{
- bignum priv_key = NULL;
- bignum pub_key = NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ int rc;
+ OSSL_PARAM *params = NULL, *out_params = NULL, *merged_params = NULL;
+ OSSL_PARAM_BLD *param_bld = NULL;
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) ||
((priv == NULL) && (pub == NULL)) || (ctx == NULL) ||
@@ -93,17 +169,85 @@ int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ (void)DH_set0_key(ctx->keypair[peer], pub, priv);
+
+ return SSH_OK;
+#else
+ rc = EVP_PKEY_todata(ctx->keypair[peer], EVP_PKEY_KEYPAIR, &out_params);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+
+ param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ evp_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ctx->keypair[peer], NULL);
+ if (evp_ctx == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = EVP_PKEY_fromdata_init(evp_ctx);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
if (priv) {
- priv_key = priv;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, priv);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
}
if (pub) {
- pub_key = pub;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, pub);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
}
- (void)DH_set0_key(ctx->keypair[peer], pub_key, priv_key);
- return SSH_OK;
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+ OSSL_PARAM_BLD_free(param_bld);
+
+ merged_params = OSSL_PARAM_merge(out_params, params);
+ if (merged_params == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = EVP_PKEY_fromdata(evp_ctx,
+ &(ctx->keypair[peer]),
+ EVP_PKEY_PUBLIC_KEY,
+ merged_params);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = SSH_OK;
+out:
+ bignum_safe_free(priv);
+ bignum_safe_free(pub);
+ EVP_PKEY_CTX_free(evp_ctx);
+ OSSL_PARAM_free(out_params);
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_free(merged_params);
+
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
int ssh_dh_get_parameters(struct dh_ctx *ctx,
const_bignum *modulus, const_bignum *generator)
{
@@ -113,18 +257,51 @@ int ssh_dh_get_parameters(struct dh_ctx *ctx,
DH_get0_pqg(ctx->keypair[0], modulus, NULL, generator);
return SSH_OK;
}
+#else
+int ssh_dh_get_parameters(struct dh_ctx *ctx,
+ bignum *modulus, bignum *generator)
+{
+ int rc;
+
+ if (ctx == NULL || ctx->keypair[0] == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[0], OSSL_PKEY_PARAM_FFC_P, (BIGNUM**)modulus);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[0], OSSL_PKEY_PARAM_FFC_G, (BIGNUM**)generator);
+ if (rc != 1) {
+ bignum_safe_free(*modulus);
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_set_parameters(struct dh_ctx *ctx,
const bignum modulus, const bignum generator)
{
size_t i;
int rc;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM *params = NULL;
+ OSSL_PARAM_BLD *param_bld = NULL;
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
if ((ctx == NULL) || (modulus == NULL) || (generator == NULL)) {
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ evp_ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL);
+#endif
+
for (i = 0; i < 2; i++) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
bignum p = NULL;
bignum g = NULL;
@@ -146,16 +323,78 @@ int ssh_dh_set_parameters(struct dh_ctx *ctx,
rc = SSH_ERROR;
goto done;
}
+#else
+ param_bld = OSSL_PARAM_BLD_new();
+
+ if (param_bld == NULL) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, modulus);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, generator);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ OSSL_PARAM_BLD_free(param_bld);
+ rc = SSH_ERROR;
+ goto done;
+ }
+ OSSL_PARAM_BLD_free(param_bld);
+
+ rc = EVP_PKEY_fromdata_init(evp_ctx);
+ if (rc != 1) {
+ OSSL_PARAM_free(params);
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ /* make sure to invalidate existing keys */
+ EVP_PKEY_free(ctx->keypair[i]);
+ ctx->keypair[i] = NULL;
+
+ rc = EVP_PKEY_fromdata(evp_ctx,
+ &(ctx->keypair[i]),
+ EVP_PKEY_KEY_PARAMETERS,
+ params);
+ if (rc != 1) {
+ OSSL_PARAM_free(params);
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
}
rc = SSH_OK;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
done:
if (rc != SSH_OK) {
DH_free(ctx->keypair[0]);
DH_free(ctx->keypair[1]);
+ }
+#else
+done:
+ EVP_PKEY_CTX_free(evp_ctx);
+
+ if (rc != SSH_OK) {
+ EVP_PKEY_free(ctx->keypair[0]);
+ EVP_PKEY_free(ctx->keypair[1]);
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (rc != SSH_OK) {
ctx->keypair[0] = NULL;
ctx->keypair[1] = NULL;
}
+
return rc;
}
@@ -202,8 +441,13 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
{
if (crypto->dh_ctx != NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
DH_free(crypto->dh_ctx->keypair[0]);
DH_free(crypto->dh_ctx->keypair[1]);
+#else
+ EVP_PKEY_free(crypto->dh_ctx->keypair[0]);
+ EVP_PKEY_free(crypto->dh_ctx->keypair[1]);
+#endif /* OPENSSL_VERSION_NUMBER */
free(crypto->dh_ctx);
crypto->dh_ctx = NULL;
}
@@ -212,7 +456,8 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
/** @internal
* @brief generates a secret DH parameter of at least DH_SECURITY_BITS
* security as well as the corresponding public key.
- * @param[out] parms a dh_ctx that will hold the new keys.
+ *
+ * @param[out] params a dh_ctx that will hold the new keys.
* @param peer Select either client or server key storage. Valid values are:
* DH_CLIENT_KEYPAIR or DH_SERVER_KEYPAIR
*
@@ -221,14 +466,43 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer)
{
int rc;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif
if ((dh_ctx == NULL) || (dh_ctx->keypair[peer] == NULL)) {
return SSH_ERROR;
}
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
rc = DH_generate_key(dh_ctx->keypair[peer]);
if (rc != 1) {
return SSH_ERROR;
}
+#else
+ evp_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, dh_ctx->keypair[peer], NULL);
+ if (evp_ctx == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_keygen_init(evp_ctx);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(evp_ctx);
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_generate(evp_ctx, &(dh_ctx->keypair[peer]));
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(evp_ctx);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to generate DH: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return SSH_ERROR;
+ }
+
+ EVP_PKEY_CTX_free(evp_ctx);
+#endif /* OPENSSL_VERSION_NUMBER */
+
return SSH_OK;
}
@@ -247,8 +521,14 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
bignum *dest)
{
unsigned char *kstring = NULL;
+ int rc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pub_key = NULL;
- int klen, rc;
+ int klen;
+#else
+ size_t klen;
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
if ((dh_ctx == NULL) ||
(dh_ctx->keypair[local] == NULL) ||
@@ -256,6 +536,7 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
kstring = malloc(DH_size(dh_ctx->keypair[local]));
if (kstring == NULL) {
rc = SSH_ERROR;
@@ -273,6 +554,43 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
rc = SSH_ERROR;
goto done;
}
+#else
+ evp_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, dh_ctx->keypair[local], NULL);
+
+ rc = EVP_PKEY_derive_init(evp_ctx);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ rc = EVP_PKEY_derive_set_peer(evp_ctx, dh_ctx->keypair[remote]);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to set peer key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ /* getting the size of the secret */
+ rc = EVP_PKEY_derive(evp_ctx, kstring, &klen);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ kstring = malloc(klen);
+ if (kstring == NULL) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ rc = EVP_PKEY_derive(evp_ctx, kstring, &klen);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
*dest = BN_bin2bn(kstring, klen, NULL);
if (*dest == NULL) {
@@ -282,6 +600,9 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
rc = SSH_OK;
done:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_PKEY_CTX_free(evp_ctx);
+#endif
free(kstring);
return rc;
}
diff --git a/src/dh_key.c b/src/dh_key.c
index bda54b17..20d24a31 100644
--- a/src/dh_key.c
+++ b/src/dh_key.c
@@ -289,8 +289,10 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
/** @internal
* @brief generates a secret DH parameter of at least DH_SECURITY_BITS
* security as well as the corresponding public key.
- * @param[out] parms a dh_kex paramters structure with preallocated bignum
+ *
+ * @param[out] params a dh_kex parameters structure with preallocated bignum
* where to store the parameters
+ *
* @return SSH_OK on success, SSH_ERROR on error
*/
int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer)
diff --git a/src/ecdh.c b/src/ecdh.c
index a4c07ccb..af80beec 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -43,6 +43,11 @@ struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks = {
.user = NULL
};
+void ssh_client_ecdh_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_ecdh_client_callbacks);
+}
+
/** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS
@@ -55,7 +60,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_ecdh_client_callbacks);
+ ssh_client_ecdh_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) {
ssh_set_error(session,SSH_FATAL, "No public key in packet");
@@ -88,16 +93,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c
index a1de27fd..817f066a 100644
--- a/src/ecdh_crypto.c
+++ b/src/ecdh_crypto.c
@@ -30,15 +30,27 @@
#ifdef HAVE_ECDH
#include <openssl/ecdh.h>
-
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#define NISTP256 NID_X9_62_prime256v1
#define NISTP384 NID_secp384r1
#define NISTP521 NID_secp521r1
+#else
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/param_build.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include "libcrypto-compat.h"
+#endif /* OPENSSL_VERSION_NUMBER */
/** @internal
* @brief Map the given key exchange enum value to its curve name.
*/
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
+#else
+static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
+#endif /* OPENSSL_VERSION_NUMBER */
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
return NISTP256;
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
@@ -46,88 +58,191 @@ static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
return NISTP521;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
return SSH_ERROR;
+#else
+ return NULL;
+#endif
}
-/** @internal
- * @brief Starts ecdh-sha2-nistp256 key exchange
+/* @internal
+ * @brief Generate ECDH key pair for ecdh key exchange and store it in the
+ * session->next_crypto structure
*/
-int ssh_client_ecdh_init(ssh_session session){
- EC_KEY *key;
- const EC_GROUP *group;
- const EC_POINT *pubkey;
- ssh_string client_pubkey;
- int curve;
- int len;
- int rc;
- bignum_CTX ctx = BN_CTX_new();
+static ssh_string ssh_ecdh_generate(ssh_session session)
+{
+ ssh_string pubkey_string = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const EC_POINT *point = NULL;
+ const EC_GROUP *group = NULL;
+ EC_KEY *key = NULL;
+ int curve;
+#else
+ EC_POINT *point = NULL;
+ EC_GROUP *group = NULL;
+ const char *curve = NULL;
+ EVP_PKEY *key = NULL;
+ OSSL_PARAM *out_params = NULL;
+ const OSSL_PARAM *pubkey_param = NULL;
+ const void *pubkey = NULL;
+ size_t pubkey_len;
+ int nid;
+ int rc;
+#endif /* OPENSSL_VERSION_NUMBER */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
- if (rc < 0) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
+ curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (curve == SSH_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to get curve name");
+ return NULL;
+ }
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == SSH_ERROR) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
+ key = EC_KEY_new_by_curve_name(curve);
+#else
+ if (curve == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to get curve name");
+ return NULL;
+ }
- key = EC_KEY_new_by_curve_name(curve);
- if (key == NULL) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
- group = EC_KEY_get0_group(key);
+ key = EVP_EC_gen(curve);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (key == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to generate key");
+ return NULL;
+ }
- EC_KEY_generate_key(key);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ group = EC_KEY_get0_group(key);
- pubkey=EC_KEY_get0_public_key(key);
- len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
- NULL,0,ctx);
+ EC_KEY_generate_key(key);
- client_pubkey = ssh_string_new(len);
- if (client_pubkey == NULL) {
- BN_CTX_free(ctx);
- EC_KEY_free(key);
- return SSH_ERROR;
- }
+ point = EC_KEY_get0_public_key(key);
- EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
- ssh_string_data(client_pubkey),len,ctx);
- BN_CTX_free(ctx);
+ pubkey_string = pki_key_make_ecpoint_string(group, point);
+#else
+ rc = EVP_PKEY_todata(key, EVP_PKEY_PUBLIC_KEY, &out_params);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to export public key");
+ EVP_PKEY_free(key);
+ return NULL;
+ }
- rc = ssh_buffer_add_ssh_string(session->out_buffer,client_pubkey);
- if (rc < 0) {
- EC_KEY_free(key);
- SSH_STRING_FREE(client_pubkey);
- return SSH_ERROR;
- }
+ pubkey_param = OSSL_PARAM_locate_const(out_params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (pubkey_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to find public key");
+ EVP_PKEY_free(key);
+ OSSL_PARAM_free(out_params);
+ return NULL;
+ }
- session->next_crypto->ecdh_privkey = key;
- session->next_crypto->ecdh_client_pubkey = client_pubkey;
+ rc = OSSL_PARAM_get_octet_string_ptr(pubkey_param,
+ (const void**)&pubkey,
+ &pubkey_len);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to read public key");
+ OSSL_PARAM_free(out_params);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
- /* register the packet callbacks */
- ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
- session->dh_handshake_state = DH_STATE_INIT_SENT;
+ /* Convert the data to low-level representation */
+ nid = pki_key_ecgroup_name_to_nid(curve);
+ group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, nid);
+ if (group == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not create group: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ OSSL_PARAM_free(out_params);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
+ point = EC_POINT_new(group);
+ if (point == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not create point: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EC_GROUP_free(group);
+ OSSL_PARAM_free(out_params);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
+ rc = EC_POINT_oct2point(group, point, pubkey, pubkey_len, NULL);
+ OSSL_PARAM_free(out_params);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to export public key");
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
- rc = ssh_packet_send(session);
+ pubkey_string = pki_key_make_ecpoint_string(group, point);
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (pubkey_string == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to convert public key");
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(key);
+#else
+ EVP_PKEY_free(key);
+#endif /* OPENSSL_VERSION_NUMBER */
+ return NULL;
+ }
+ session->next_crypto->ecdh_privkey = key;
+ return pubkey_string;
+}
- return rc;
+/** @internal
+ * @brief Starts ecdh-sha2-nistp256 key exchange
+ */
+int ssh_client_ecdh_init(ssh_session session)
+{
+ ssh_string client_pubkey = NULL;
+ int rc;
+
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+
+ client_pubkey = ssh_ecdh_generate(session);
+ if (client_pubkey == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
+ if (rc < 0) {
+ ssh_string_free(client_pubkey);
+ return SSH_ERROR;
+ }
+
+ session->next_crypto->ecdh_client_pubkey = client_pubkey;
+
+ /* register the packet callbacks */
+ ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
+ session->dh_handshake_state = DH_STATE_INIT_SENT;
+
+ rc = ssh_packet_send(session);
+
+ return rc;
}
-int ecdh_build_k(ssh_session session) {
- const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey);
- EC_POINT *pubkey;
- void *buffer;
+int ecdh_build_k(ssh_session session)
+{
+ struct ssh_crypto_struct *next_crypto = session->next_crypto;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const EC_GROUP *group = EC_KEY_get0_group(next_crypto->ecdh_privkey);
+ EC_POINT *pubkey = NULL;
+ void *buffer = NULL;
int rc;
int len = (EC_GROUP_get_degree(group) + 7) / 8;
bignum_CTX ctx = bignum_ctx_new();
if (ctx == NULL) {
return -1;
}
-
pubkey = EC_POINT_new(group);
if (pubkey == NULL) {
bignum_ctx_free(ctx);
@@ -137,14 +252,14 @@ int ecdh_build_k(ssh_session session) {
if (session->server) {
rc = EC_POINT_oct2point(group,
pubkey,
- ssh_string_data(session->next_crypto->ecdh_client_pubkey),
- ssh_string_len(session->next_crypto->ecdh_client_pubkey),
+ ssh_string_data(next_crypto->ecdh_client_pubkey),
+ ssh_string_len(next_crypto->ecdh_client_pubkey),
ctx);
} else {
rc = EC_POINT_oct2point(group,
pubkey,
- ssh_string_data(session->next_crypto->ecdh_server_pubkey),
- ssh_string_len(session->next_crypto->ecdh_server_pubkey),
+ ssh_string_data(next_crypto->ecdh_server_pubkey),
+ ssh_string_len(next_crypto->ecdh_server_pubkey),
ctx);
}
bignum_ctx_free(ctx);
@@ -162,7 +277,7 @@ int ecdh_build_k(ssh_session session) {
rc = ECDH_compute_key(buffer,
len,
pubkey,
- session->next_crypto->ecdh_privkey,
+ next_crypto->ecdh_privkey,
NULL);
EC_POINT_clear_free(pubkey);
if (rc <= 0) {
@@ -170,23 +285,149 @@ int ecdh_build_k(ssh_session session) {
return -1;
}
- bignum_bin2bn(buffer, len, &session->next_crypto->shared_secret);
+ bignum_bin2bn(buffer, len, &next_crypto->shared_secret);
free(buffer);
- if (session->next_crypto->shared_secret == NULL) {
- EC_KEY_free(session->next_crypto->ecdh_privkey);
- session->next_crypto->ecdh_privkey = NULL;
+#else
+ const char *curve = NULL;
+ EVP_PKEY *pubkey = NULL;
+ void *secret = NULL;
+ size_t secret_len;
+ int rc;
+ ssh_string peer_pubkey = NULL;
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ EVP_PKEY_CTX *dh_ctx = EVP_PKEY_CTX_new_from_pkey(NULL,
+ next_crypto->ecdh_privkey,
+ NULL);
+
+ if (dh_ctx == NULL || param_bld == NULL) {
+ ssh_set_error_oom(session);
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+ }
+
+ rc = EVP_PKEY_derive_init(dh_ctx);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not init PKEY derive: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
return -1;
}
- EC_KEY_free(session->next_crypto->ecdh_privkey);
- session->next_crypto->ecdh_privkey = NULL;
+
+ if (session->server) {
+ peer_pubkey = next_crypto->ecdh_client_pubkey;
+ } else {
+ peer_pubkey = next_crypto->ecdh_server_pubkey;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld,
+ OSSL_PKEY_PARAM_PUB_KEY,
+ ssh_string_data(peer_pubkey),
+ ssh_string_len(peer_pubkey));
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not push the pub key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+ }
+ curve = ecdh_kex_type_to_curve(next_crypto->kex_type);
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld,
+ OSSL_PKEY_PARAM_GROUP_NAME,
+ (char *)curve,
+ strlen(curve));
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not push the group name: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+ }
+
+ rc = evp_build_pkey("EC", param_bld, &pubkey, EVP_PKEY_PUBLIC_KEY);
+ OSSL_PARAM_BLD_free(param_bld);
+ if (rc != SSH_OK) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not build the pkey: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_derive_set_peer(dh_ctx, pubkey);
+ EVP_PKEY_free(pubkey);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not set peer pubkey: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ /* get the max length of the secret */
+ rc = EVP_PKEY_derive(dh_ctx, NULL, &secret_len);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not set peer pubkey: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ secret = malloc(secret_len);
+ if (secret == NULL) {
+ ssh_set_error_oom(session);
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_derive(dh_ctx, secret, &secret_len);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not derive shared key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ EVP_PKEY_CTX_free(dh_ctx);
+
+ bignum_bin2bn(secret, secret_len, &next_crypto->shared_secret);
+ free(secret);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (next_crypto->shared_secret == NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(next_crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(next_crypto->ecdh_privkey);
+#endif /* OPENSSL_VERSION_NUMBER */
+ next_crypto->ecdh_privkey = NULL;
+ return -1;
+ }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(next_crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(next_crypto->ecdh_privkey);
+#endif /* OPENSSL_VERSION_NUMBER */
+ next_crypto->ecdh_privkey = NULL;
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("Session server cookie",
- session->next_crypto->server_kex.cookie, 16);
+ next_crypto->server_kex.cookie, 16);
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
+ next_crypto->client_kex.cookie, 16);
+ ssh_print_bignum("Shared secret key", next_crypto->shared_secret);
+#endif /* DEBUG_CRYPTO */
return 0;
}
@@ -196,78 +437,36 @@ int ecdh_build_k(ssh_session session) {
/** @brief Handle a SSH_MSG_KEXDH_INIT packet (server) and send a
* SSH_MSG_KEXDH_REPLY
*/
-SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
+SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
+{
/* ECDH keys */
- ssh_string q_c_string;
- ssh_string q_s_string;
- EC_KEY *ecdh_key;
- const EC_GROUP *group;
- const EC_POINT *ecdh_pubkey;
- bignum_CTX ctx;
- /* SSH host keys (rsa,dsa,ecdsa) */
+ ssh_string q_c_string = NULL;
+ ssh_string q_s_string = NULL;
+ /* SSH host keys (rsa, ed25519 and ecdsa) */
ssh_key privkey;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
- int curve;
- int len;
int rc;
(void)type;
(void)user;
+ SSH_LOG(SSH_LOG_TRACE, "Processing SSH_MSG_KEXDH_INIT");
+
ssh_packet_remove_callbacks(session, &ssh_ecdh_server_callbacks);
/* Extract the client pubkey from the init packet */
q_c_string = ssh_buffer_get_ssh_string(packet);
if (q_c_string == NULL) {
- ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet");
+ ssh_set_error(session, SSH_FATAL, "No Q_C ECC point in packet");
goto error;
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
- /* Build server's keypair */
-
- ctx = BN_CTX_new();
-
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == SSH_ERROR) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
-
- ecdh_key = EC_KEY_new_by_curve_name(curve);
- if (ecdh_key == NULL) {
- ssh_set_error_oom(session);
- BN_CTX_free(ctx);
- goto error;
- }
-
- group = EC_KEY_get0_group(ecdh_key);
- EC_KEY_generate_key(ecdh_key);
-
- ecdh_pubkey = EC_KEY_get0_public_key(ecdh_key);
- len = EC_POINT_point2oct(group,
- ecdh_pubkey,
- POINT_CONVERSION_UNCOMPRESSED,
- NULL,
- 0,
- ctx);
-
- q_s_string = ssh_string_new(len);
+ q_s_string = ssh_ecdh_generate(session);
if (q_s_string == NULL) {
- EC_KEY_free(ecdh_key);
- BN_CTX_free(ctx);
goto error;
}
- EC_POINT_point2oct(group,
- ecdh_pubkey,
- POINT_CONVERSION_UNCOMPRESSED,
- ssh_string_data(q_s_string),
- len,
- ctx);
- BN_CTX_free(ctx);
-
- session->next_crypto->ecdh_privkey = ecdh_key;
session->next_crypto->ecdh_server_pubkey = q_s_string;
/* build k and session_id */
@@ -317,24 +516,18 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEXDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
goto error;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- if (rc == SSH_ERROR){
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c
index d9c41bf9..86d15f72 100644
--- a/src/ecdh_gcrypt.c
+++ b/src/ecdh_gcrypt.c
@@ -271,7 +271,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
ssh_string q_s_string;
gcry_sexp_t param = NULL;
gcry_sexp_t key = NULL;
- /* SSH host keys (rsa,dsa,ecdsa) */
+ /* SSH host keys (rsa, ed25519 and ecdsa) */
ssh_key privkey;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
@@ -295,7 +295,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
- /* Build server's keypair */
+ /* Build server's key pair */
err = gcry_sexp_build(&param, NULL, "(genkey(ecdh(curve %s) (flags transient-key)))",
curve);
if (err) {
@@ -366,23 +366,19 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEXDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc != SSH_OK) {
goto out;
}
-
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
/* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc != SSH_OK) {
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
-
out:
gcry_sexp_release(param);
gcry_sexp_release(key);
diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c
index 718f1522..1d9c8f36 100644
--- a/src/ecdh_mbedcrypto.c
+++ b/src/ecdh_mbedcrypto.c
@@ -34,6 +34,7 @@
#include <mbedtls/ecdh.h>
#include <mbedtls/ecp.h>
+#include "mbedcrypto-compat.h"
#ifdef HAVE_ECDH
@@ -54,6 +55,10 @@ int ssh_client_ecdh_init(ssh_session session)
mbedtls_ecp_group grp;
int rc;
mbedtls_ecp_group_id curve;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ mbedtls_ecp_keypair *ecdh_privkey = NULL;
+
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
if (curve == MBEDTLS_ECP_DP_NONE) {
@@ -70,7 +75,9 @@ int ssh_client_ecdh_init(ssh_session session)
return SSH_ERROR;
}
- mbedtls_ecp_keypair_init(session->next_crypto->ecdh_privkey);
+ ecdh_privkey = session->next_crypto->ecdh_privkey;
+
+ mbedtls_ecp_keypair_init(ecdh_privkey);
mbedtls_ecp_group_init(&grp);
rc = mbedtls_ecp_group_load(&grp, curve);
@@ -80,10 +87,10 @@ int ssh_client_ecdh_init(ssh_session session)
}
rc = mbedtls_ecp_gen_keypair(&grp,
- &session->next_crypto->ecdh_privkey->d,
- &session->next_crypto->ecdh_privkey->Q,
- mbedtls_ctr_drbg_random,
- ssh_get_mbedtls_ctr_drbg_context());
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
@@ -91,7 +98,7 @@ int ssh_client_ecdh_init(ssh_session session)
}
client_pubkey = make_ecpoint_string(&grp,
- &session->next_crypto->ecdh_privkey->Q);
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q));
if (client_pubkey == NULL) {
rc = SSH_ERROR;
goto out;
@@ -124,6 +131,10 @@ int ecdh_build_k(ssh_session session)
mbedtls_ecp_point pubkey;
int rc;
mbedtls_ecp_group_id curve;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ mbedtls_ecp_keypair *ecdh_privkey = NULL;
+
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
if (curve == MBEDTLS_ECP_DP_NONE) {
@@ -162,19 +173,20 @@ int ecdh_build_k(ssh_session session)
mbedtls_mpi_init(session->next_crypto->shared_secret);
+ ecdh_privkey = session->next_crypto->ecdh_privkey;
rc = mbedtls_ecdh_compute_shared(&grp,
- session->next_crypto->shared_secret,
- &pubkey,
- &session->next_crypto->ecdh_privkey->d,
- mbedtls_ctr_drbg_random,
- ssh_get_mbedtls_ctr_drbg_context());
+ session->next_crypto->shared_secret,
+ &pubkey,
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
goto out;
}
out:
- mbedtls_ecp_keypair_free(session->next_crypto->ecdh_privkey);
+ mbedtls_ecp_keypair_free(ecdh_privkey);
SAFE_FREE(session->next_crypto->ecdh_privkey);
mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&pubkey);
@@ -187,6 +199,8 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
ssh_string q_c_string = NULL;
ssh_string q_s_string = NULL;
mbedtls_ecp_group grp;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ mbedtls_ecp_keypair *ecdh_privkey = NULL;
ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
@@ -214,10 +228,14 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
return SSH_ERROR;
}
+ ecdh_privkey = session->next_crypto->ecdh_privkey;
+
session->next_crypto->ecdh_client_pubkey = q_c_string;
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
+
mbedtls_ecp_group_init(&grp);
- mbedtls_ecp_keypair_init(session->next_crypto->ecdh_privkey);
+ mbedtls_ecp_keypair_init(ecdh_privkey);
rc = mbedtls_ecp_group_load(&grp, curve);
if (rc != 0) {
@@ -226,16 +244,16 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
}
rc = mbedtls_ecp_gen_keypair(&grp,
- &session->next_crypto->ecdh_privkey->d,
- &session->next_crypto->ecdh_privkey->Q,
- mbedtls_ctr_drbg_random,
- ssh_get_mbedtls_ctr_drbg_context());
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
goto out;
}
- q_s_string = make_ecpoint_string(&grp, &session->next_crypto->ecdh_privkey->Q);
+ q_s_string = make_ecpoint_string(&grp, &ecdh_privkey->MBEDTLS_PRIVATE(Q));
if (q_s_string == NULL) {
rc = SSH_ERROR;
goto out;
@@ -293,23 +311,20 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEXDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc != SSH_OK) {
rc = SSH_ERROR;
goto out;
}
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- rc = SSH_ERROR;
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
-
out:
mbedtls_ecp_group_free(&grp);
if (rc == SSH_ERROR) {
diff --git a/src/error.c b/src/error.c
index 22180407..3f8d78cd 100644
--- a/src/error.c
+++ b/src/error.c
@@ -29,7 +29,7 @@
#include "libssh/session.h"
/**
- * @defgroup libssh_error The SSH error functions.
+ * @defgroup libssh_error The SSH error functions
* @ingroup libssh
*
* Functions for error handling.
@@ -63,8 +63,8 @@ void _ssh_set_error(void *error,
va_end(va);
err->error.error_code = code;
- if (ssh_get_log_level() >= SSH_LOG_WARN) {
- ssh_log_function(SSH_LOG_WARN,
+ if (ssh_get_log_level() == SSH_LOG_TRACE) {
+ ssh_log_function(SSH_LOG_TRACE,
function,
err->error.error_buffer);
}
@@ -142,7 +142,7 @@ const char *ssh_get_error(void *error) {
* SSH_FATAL A fatal error occurred. This could be an unexpected
* disconnection\n
*
- * Other error codes are internal but can be considered same than
+ * Other error codes are internal but can be considered the same as
* SSH_FATAL.
*/
int ssh_get_error_code(void *error) {
diff --git a/src/external/bcrypt_pbkdf.c b/src/external/bcrypt_pbkdf.c
index 8b0b8118..85f4be47 100644
--- a/src/external/bcrypt_pbkdf.c
+++ b/src/external/bcrypt_pbkdf.c
@@ -42,7 +42,7 @@
* function with the following modifications:
* 1. The input password and salt are preprocessed with SHA512.
* 2. The output length is expanded to 256 bits.
- * 3. Subsequently the magic string to be encrypted is lengthened and modifed
+ * 3. Subsequently the magic string to be encrypted is lengthened and modified
* to "OxychromaticBlowfishSwatDynamite"
* 4. The hash function is defined to perform 64 rounds of initial state
* expansion. (More rounds are performed by iterating the hash.)
@@ -166,7 +166,7 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
}
/*
- * pbkdf2 deviation: ouput the key material non-linearly.
+ * pbkdf2 deviation: output the key material non-linearly.
*/
amt = MIN(amt, keylen);
for (i = 0; i < amt; i++) {
diff --git a/src/gcrypt_missing.c b/src/gcrypt_missing.c
index e931ec5b..21a63a9b 100644
--- a/src/gcrypt_missing.c
+++ b/src/gcrypt_missing.c
@@ -55,7 +55,7 @@ char *ssh_gcry_bn2dec(bignum bn) {
size = gcry_mpi_get_nbits(bn) * 3;
rsize = size / 10 + size / 1000 + 2;
- ret = malloc(rsize + 1);
+ ret = gcry_malloc(rsize + 1);
if (ret == NULL) {
return NULL;
}
diff --git a/src/getpass.c b/src/getpass.c
index 99627665..6be33c77 100644
--- a/src/getpass.c
+++ b/src/getpass.c
@@ -44,7 +44,8 @@
*
* @return 1 on success, 0 on error.
*/
-static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) {
+static int ssh_gets(const char *prompt, char *buf, size_t len, int verify)
+{
char *tmp;
char *ptr = NULL;
int ok = 0;
@@ -121,7 +122,8 @@ int ssh_getpass(const char *prompt,
char *buf,
size_t len,
int echo,
- int verify) {
+ int verify)
+{
HANDLE h;
DWORD mode = 0;
int ok;
@@ -213,7 +215,8 @@ int ssh_getpass(const char *prompt,
char *buf,
size_t len,
int echo,
- int verify) {
+ int verify)
+{
struct termios attr;
struct termios old_attr;
int ok = 0;
@@ -255,7 +258,11 @@ int ssh_getpass(const char *prompt,
/* disable nonblocking I/O */
if (fd & O_NDELAY) {
- fcntl(0, F_SETFL, fd & ~O_NDELAY);
+ ok = fcntl(0, F_SETFL, fd & ~O_NDELAY);
+ if (ok < 0) {
+ perror("fcntl");
+ return -1;
+ }
}
ok = ssh_gets(prompt, buf, len, verify);
@@ -267,7 +274,11 @@ int ssh_getpass(const char *prompt,
/* close fd */
if (fd & O_NDELAY) {
- fcntl(0, F_SETFL, fd);
+ ok = fcntl(0, F_SETFL, fd);
+ if (ok < 0) {
+ perror("fcntl");
+ return -1;
+ }
}
if (!ok) {
diff --git a/src/getrandom_crypto.c b/src/getrandom_crypto.c
new file mode 100644
index 00000000..df8bd19f
--- /dev/null
+++ b/src/getrandom_crypto.c
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 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/crypto.h"
+#include <openssl/rand.h>
+
+/**
+ * @addtogroup libssh_misc
+ *
+ * @{
+ */
+
+/**
+ * @brief Get random bytes
+ *
+ * Make sure to always check the return code of this function!
+ *
+ * @param[in] where The buffer to fill with random bytes
+ *
+ * @param[in] len The size of the buffer to fill.
+ *
+ * @param[in] strong Use a strong or private RNG source.
+ *
+ * @return 1 on success, 0 on error.
+ */
+int
+ssh_get_random(void *where, int len, int strong)
+{
+#ifdef HAVE_OPENSSL_RAND_PRIV_BYTES
+ if (strong) {
+ /* Returns -1 when not supported, 0 on error, 1 on success */
+ return !!RAND_priv_bytes(where, len);
+ }
+#else
+ (void)strong;
+#endif /* HAVE_RAND_PRIV_BYTES */
+
+ /* Returns -1 when not supported, 0 on error, 1 on success */
+ return !!RAND_bytes(where, len);
+}
+
+/**
+ * @}
+ */
diff --git a/src/getrandom_gcrypt.c b/src/getrandom_gcrypt.c
new file mode 100644
index 00000000..da726405
--- /dev/null
+++ b/src/getrandom_gcrypt.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * 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/crypto.h"
+#include <gcrypt.h>
+
+int
+ssh_get_random(void *where, int len, int strong)
+{
+ /* variable not used in gcrypt */
+ (void)strong;
+
+ /* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */
+ gcry_randomize(where, len, GCRY_STRONG_RANDOM);
+
+ return 1;
+}
diff --git a/src/getrandom_mbedcrypto.c b/src/getrandom_mbedcrypto.c
new file mode 100644
index 00000000..7e87b6a6
--- /dev/null
+++ b/src/getrandom_mbedcrypto.c
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * 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/crypto.h"
+#include "mbedcrypto-compat.h"
+
+mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg;
+
+int
+ssh_mbedtls_random(void *where, int len, int strong)
+{
+ int rc = 0;
+ if (strong) {
+ mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
+ MBEDTLS_CTR_DRBG_PR_ON);
+ rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
+ mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
+ MBEDTLS_CTR_DRBG_PR_OFF);
+ } else {
+ rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
+ }
+
+ return !rc;
+}
+
+int
+ssh_get_random(void *where, int len, int strong)
+{
+ return ssh_mbedtls_random(where, len, strong);
+}
diff --git a/src/gssapi.c b/src/gssapi.c
index 488df582..5254d38d 100644
--- a/src/gssapi.c
+++ b/src/gssapi.c
@@ -68,15 +68,15 @@ struct ssh_gssapi_struct{
/** @internal
* @initializes a gssapi context for authentication
*/
-static int ssh_gssapi_init(ssh_session session){
+static int ssh_gssapi_init(ssh_session session)
+{
if (session->gssapi != NULL)
return SSH_OK;
- session->gssapi = malloc(sizeof(struct ssh_gssapi_struct));
- if(!session->gssapi){
+ session->gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
+ if (session->gssapi == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
- ZERO_STRUCTP(session->gssapi);
session->gssapi->server_creds = GSS_C_NO_CREDENTIAL;
session->gssapi->client_creds = GSS_C_NO_CREDENTIAL;
session->gssapi->ctx = GSS_C_NO_CONTEXT;
@@ -87,7 +87,8 @@ static int ssh_gssapi_init(ssh_session session){
/** @internal
* @frees a gssapi context
*/
-static void ssh_gssapi_free(ssh_session session){
+static void ssh_gssapi_free(ssh_session session)
+{
OM_uint32 min;
if (session->gssapi == NULL)
return;
@@ -114,7 +115,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token){
* @brief sends a SSH_MSG_USERAUTH_GSSAPI_RESPONSE packet
* @param[in] oid the OID that was selected for authentication
*/
-static int ssh_gssapi_send_response(ssh_session session, ssh_string oid){
+static int ssh_gssapi_send_response(ssh_session session, ssh_string oid)
+{
if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) < 0 ||
ssh_buffer_add_ssh_string(session->out_buffer,oid) < 0) {
ssh_set_error_oom(session);
@@ -184,8 +186,11 @@ out:
/** @internal
* @brief handles an user authentication using GSSAPI
*/
-int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids){
- char service_name[]="host";
+int
+ssh_gssapi_handle_userauth(ssh_session session, const char *user,
+ uint32_t n_oid, ssh_string *oids)
+{
+ char service_name[] = "host";
gss_buffer_desc name_buf;
gss_name_t server_name; /* local server fqdn */
OM_uint32 maj_stat, min_stat;
@@ -218,11 +223,12 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "indicate mecks %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG, "indicate mecks %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
+ gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
}
@@ -240,7 +246,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
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");
+ SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
continue;
}
oid.elements = &oid_s[2];
@@ -253,25 +259,28 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
}
gss_release_oid_set(&min_stat, &supported);
if (oid_count == 0){
- SSH_LOG(SSH_LOG_PROTOCOL,"GSSAPI: no OID match");
+ SSH_LOG(SSH_LOG_DEBUG,"GSSAPI: no OID match");
ssh_auth_reply_default(session, 0);
gss_release_oid_set(&min_stat, &both_supported);
return SSH_OK;
}
/* from now we have room for context */
- if (ssh_gssapi_init(session) == SSH_ERROR)
+ if (ssh_gssapi_init(session) == SSH_ERROR) {
+ gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
+ }
name_buf.value = service_name;
name_buf.length = strlen(name_buf.value) + 1;
maj_stat = gss_import_name(&min_stat, &name_buf,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
+ gss_release_oid_set(&min_stat, &both_supported);
return -1;
}
@@ -282,8 +291,8 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
gss_release_oid_set(&min_stat, &both_supported);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "error acquiring credentials %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE, "error acquiring credentials %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_TRACE,
"acquiring creds",
maj_stat,
min_stat);
@@ -291,7 +300,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "acquiring credentials %d, %d", maj_stat, min_stat);
+ SSH_LOG(SSH_LOG_DEBUG, "acquiring credentials %d, %d", maj_stat, min_stat);
/* finding which OID from client we selected */
for (i=0 ; i< n_oid ; ++i){
@@ -302,7 +311,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
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");
+ SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
continue;
}
oid.elements = &oid_s[2];
@@ -317,6 +326,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
session->gssapi->mech.elements = malloc(oid.length);
if (session->gssapi->mech.elements == NULL){
ssh_set_error_oom(session);
+ gss_release_oid_set(&min_stat, &selected);
return SSH_ERROR;
}
memcpy(session->gssapi->mech.elements, oid.elements, oid.length);
@@ -327,17 +337,19 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
return ssh_gssapi_send_response(session, oids[i]);
}
-static char *ssh_gssapi_name_to_char(gss_name_t name){
+static char *ssh_gssapi_name_to_char(gss_name_t name)
+{
gss_buffer_desc buffer;
OM_uint32 maj_stat, min_stat;
char *ptr;
maj_stat = gss_display_name(&min_stat, name, &buffer, NULL);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"converting name",
maj_stat,
min_stat);
ptr = malloc(buffer.length + 1);
if (ptr == NULL) {
+ gss_release_buffer(&min_stat, &buffer);
return NULL;
}
memcpy(ptr, buffer.value, buffer.length);
@@ -407,7 +419,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
maj_stat = gss_accept_sec_context(&min_stat, &session->gssapi->ctx, session->gssapi->server_creds,
&input_token, input_bindings, &client_name, NULL /*mech_oid*/, &output_token, &ret_flags,
NULL /*time*/, &session->gssapi->client_creds);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token",
maj_stat,
min_stat);
@@ -417,10 +429,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
session->gssapi->canonic_user = ssh_gssapi_name_to_char(client_name);
}
if (GSS_ERROR(maj_stat)){
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
maj_stat,
min_stat);
+ gss_release_buffer(&min_stat, &output_token);
ssh_auth_reply_default(session,0);
ssh_gssapi_free(session);
session->gssapi=NULL;
@@ -431,13 +444,23 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa);
SAFE_FREE(hexa);
- ssh_buffer_pack(session->out_buffer,
- "bdP",
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- output_token.length,
- (size_t)output_token.length, output_token.value);
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdP",
+ SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ output_token.length,
+ (size_t)output_token.length, output_token.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ ssh_auth_reply_default(session, 0);
+ ssh_gssapi_free(session);
+ session->gssapi = NULL;
+ return SSH_PACKET_USED;
+ }
ssh_packet_send(session);
}
+
+ gss_release_buffer(&min_stat, &output_token);
+
if(maj_stat == GSS_S_COMPLETE){
session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC;
}
@@ -465,8 +488,8 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session)
rc = ssh_buffer_pack(mic_buffer,
"dPbsss",
- crypto->digest_len,
- (size_t)crypto->digest_len, crypto->session_id,
+ crypto->session_id_len,
+ crypto->session_id_len, crypto->session_id,
SSH2_MSG_USERAUTH_REQUEST,
session->gssapi->user,
"ssh-connection",
@@ -524,7 +547,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic)
mic_token_buf.value = ssh_string_data(mic_token);
maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"verifying MIC",
maj_stat,
min_stat);
@@ -572,7 +595,8 @@ end:
* @returns gssapi credentials handle.
* @returns NULL if no forwardable token is available.
*/
-ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){
+ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session)
+{
if (!session || !session->gssapi || session->gssapi->client_creds == GSS_C_NO_CREDENTIAL)
return NULL;
return (ssh_gssapi_creds)session->gssapi->client_creds;
@@ -581,10 +605,11 @@ ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){
#endif /* SERVER */
/**
- * @brief Set the forwadable ticket to be given to the server for authentication.
+ * @brief Set the forwardable ticket to be given to the server for authentication.
* Unlike ssh_gssapi_get_creds() this is called on the client side of an ssh
* connection.
*
+ * @param[in] session The session
* @param[in] creds gssapi credentials handle.
*/
void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds)
@@ -602,7 +627,9 @@ void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds)
session->gssapi->client.client_deleg_creds = (gss_cred_id_t)creds;
}
-static int ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid){
+static int
+ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid)
+{
int rc;
int i;
@@ -638,7 +665,7 @@ fail:
static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
{
OM_uint32 maj_stat, min_stat, lifetime;
- gss_OID_set actual_mechs;
+ gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
gss_buffer_desc namebuf;
gss_name_t client_id = GSS_C_NO_NAME;
gss_OID oid;
@@ -700,6 +727,7 @@ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
ret = SSH_OK;
end:
+ gss_release_oid_set(&min_stat, &actual_mechs);
gss_release_name(&min_stat, &client_id);
return ret;
}
@@ -711,9 +739,10 @@ end:
* SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again
* later.
*/
-int ssh_gssapi_auth_mic(ssh_session session){
+int ssh_gssapi_auth_mic(ssh_session session)
+{
size_t i;
- gss_OID_set selected; /* oid selected for authentication */
+ gss_OID_set selected = GSS_C_NO_OID_SET; /* oid selected for authentication */
ssh_string *oids = NULL;
int rc;
size_t n_oids = 0;
@@ -739,8 +768,8 @@ int ssh_gssapi_auth_mic(ssh_session session){
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
&session->gssapi->client.server_name);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
@@ -754,7 +783,7 @@ int ssh_gssapi_auth_mic(ssh_session session){
return SSH_AUTH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi to host %s with user %s",
+ SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi to host %s with user %s",
session->opts.host, session->gssapi->user);
rc = ssh_gssapi_match(session, &selected);
if (rc == SSH_ERROR) {
@@ -762,7 +791,7 @@ int ssh_gssapi_auth_mic(ssh_session session){
}
n_oids = selected->count;
- SSH_LOG(SSH_LOG_PROTOCOL, "Sending %zu oids", n_oids);
+ SSH_LOG(SSH_LOG_DEBUG, "Sending %zu oids", n_oids);
oids = calloc(n_oids, sizeof(ssh_string));
if (oids == NULL) {
@@ -790,6 +819,8 @@ out:
SSH_STRING_FREE(oids[i]);
}
free(oids);
+ gss_release_oid_set(&min_stat, &selected);
+
if (rc != SSH_ERROR) {
return SSH_AUTH_AGAIN;
}
@@ -834,6 +865,7 @@ static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s)
}
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
+ int rc;
ssh_string oid_s;
gss_uint32 maj_stat, min_stat;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
@@ -875,7 +907,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
if(GSS_ERROR(maj_stat)){
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Initializing gssapi context",
maj_stat,
min_stat);
@@ -885,14 +917,20 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s", hexa);
SAFE_FREE(hexa);
- ssh_buffer_pack(session->out_buffer,
- "bdP",
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- output_token.length,
- (size_t)output_token.length, output_token.value);
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdP",
+ SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ output_token.length,
+ (size_t)output_token.length, output_token.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
ssh_packet_send(session);
session->auth.state = SSH_AUTH_STATE_GSSAPI_TOKEN;
}
+
+ gss_release_buffer(&min_stat, &output_token);
return SSH_PACKET_USED;
error:
@@ -902,7 +940,8 @@ error:
return SSH_PACKET_USED;
}
-static int ssh_gssapi_send_mic(ssh_session session){
+static int ssh_gssapi_send_mic(ssh_session session)
+{
OM_uint32 maj_stat, min_stat;
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER;
@@ -921,9 +960,11 @@ 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);
+
+ SSH_BUFFER_FREE(mic_buffer);
+
if (GSS_ERROR(maj_stat)){
- SSH_BUFFER_FREE(mic_buffer);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"generating MIC",
maj_stat,
min_stat);
@@ -935,8 +976,10 @@ static int ssh_gssapi_send_mic(ssh_session session){
SSH2_MSG_USERAUTH_GSSAPI_MIC,
mic_token_buf.length,
(size_t)mic_token_buf.length, mic_token_buf.value);
+
+ gss_release_buffer(&min_stat, &mic_token_buf);
+
if (rc != SSH_OK) {
- SSH_BUFFER_FREE(mic_buffer);
ssh_set_error_oom(session);
return SSH_ERROR;
}
@@ -945,6 +988,7 @@ static int ssh_gssapi_send_mic(ssh_session session){
}
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
+ int rc;
ssh_string token;
char *hexa;
OM_uint32 maj_stat, min_stat;
@@ -980,13 +1024,13 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token",
maj_stat,
min_stat);
SSH_STRING_FREE(token);
if (GSS_ERROR(maj_stat)){
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
maj_stat,
min_stat);
@@ -997,14 +1041,20 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa);
SAFE_FREE(hexa);
- ssh_buffer_pack(session->out_buffer,
- "bdP",
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- output_token.length,
- (size_t)output_token.length, output_token.value);
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdP",
+ SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ output_token.length,
+ (size_t)output_token.length, output_token.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
ssh_packet_send(session);
}
+ gss_release_buffer(&min_stat, &output_token);
+
if (maj_stat == GSS_S_COMPLETE) {
ssh_gssapi_send_mic(session);
session->auth.state = SSH_AUTH_STATE_GSSAPI_MIC_SENT;
diff --git a/src/gzip.c b/src/gzip.c
index 129571cd..cff15518 100644
--- a/src/gzip.c
+++ b/src/gzip.c
@@ -24,211 +24,237 @@
#include "config.h"
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <zlib.h>
-#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
+#include "libssh/priv.h"
#include "libssh/session.h"
#ifndef BLOCKSIZE
#define BLOCKSIZE 4092
#endif
-static z_stream *initcompress(ssh_session session, int level) {
- z_stream *stream = NULL;
- int status;
+static z_stream *
+initcompress(ssh_session session, int level)
+{
+ z_stream *stream = NULL;
+ int status;
- stream = calloc(1, sizeof(z_stream));
- if (stream == NULL) {
- return NULL;
- }
+ stream = calloc(1, sizeof(z_stream));
+ if (stream == NULL) {
+ return NULL;
+ }
- status = deflateInit(stream, level);
- if (status != Z_OK) {
- SAFE_FREE(stream);
- ssh_set_error(session, SSH_FATAL,
- "status %d inititalising zlib deflate", status);
- return NULL;
- }
+ status = deflateInit(stream, level);
+ if (status != Z_OK) {
+ SAFE_FREE(stream);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "status %d initialising zlib deflate",
+ status);
+ return NULL;
+ }
- return stream;
+ return stream;
}
-static ssh_buffer gzip_compress(ssh_session session, ssh_buffer source, int level)
+static ssh_buffer
+gzip_compress(ssh_session session, ssh_buffer source, int level)
{
- struct ssh_crypto_struct *crypto = NULL;
- z_stream *zout = NULL;
- void *in_ptr = ssh_buffer_get(source);
- unsigned long in_size = ssh_buffer_get_len(source);
- ssh_buffer dest = NULL;
- unsigned char out_buf[BLOCKSIZE] = {0};
- unsigned long len;
- int status;
-
- crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
- if (crypto == NULL) {
- return NULL;
- }
- zout = crypto->compress_out_ctx;
- if (zout == NULL) {
- zout = crypto->compress_out_ctx = initcompress(session, level);
+ struct ssh_crypto_struct *crypto = NULL;
+ z_stream *zout = NULL;
+ void *in_ptr = ssh_buffer_get(source);
+ uint32_t in_size = ssh_buffer_get_len(source);
+ ssh_buffer dest = NULL;
+ unsigned char out_buf[BLOCKSIZE] = {0};
+ uint32_t len;
+ int status;
+
+ crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
+ if (crypto == NULL) {
+ return NULL;
+ }
+ zout = crypto->compress_out_ctx;
if (zout == NULL) {
- return NULL;
+ zout = crypto->compress_out_ctx = initcompress(session, level);
+ if (zout == NULL) {
+ return NULL;
+ }
}
- }
-
- dest = ssh_buffer_new();
- if (dest == NULL) {
- return NULL;
- }
- zout->next_out = out_buf;
- zout->next_in = in_ptr;
- zout->avail_in = in_size;
- do {
- zout->avail_out = BLOCKSIZE;
- status = deflate(zout, Z_PARTIAL_FLUSH);
- if (status != Z_OK) {
- 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);
- return NULL;
+ dest = ssh_buffer_new();
+ if (dest == NULL) {
+ return NULL;
}
- zout->next_out = out_buf;
- } while (zout->avail_out == 0);
- return dest;
+ zout->next_out = out_buf;
+ zout->next_in = in_ptr;
+ zout->avail_in = in_size;
+ do {
+ zout->avail_out = BLOCKSIZE;
+ status = deflate(zout, Z_PARTIAL_FLUSH);
+ if (status != Z_OK) {
+ 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);
+ return NULL;
+ }
+ zout->next_out = out_buf;
+ } while (zout->avail_out == 0);
+
+ return dest;
}
-int compress_buffer(ssh_session session, ssh_buffer buf) {
- ssh_buffer dest = NULL;
+int
+compress_buffer(ssh_session session, ssh_buffer buf)
+{
+ ssh_buffer dest = NULL;
+ int rv;
- dest = gzip_compress(session, buf, session->opts.compressionlevel);
- if (dest == NULL) {
- return -1;
- }
+ dest = gzip_compress(session, buf, session->opts.compressionlevel);
+ if (dest == NULL) {
+ return -1;
+ }
- if (ssh_buffer_reinit(buf) < 0) {
- SSH_BUFFER_FREE(dest);
- return -1;
- }
+ if (ssh_buffer_reinit(buf) < 0) {
+ 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);
- return -1;
- }
+ rv = ssh_buffer_add_data(buf,
+ ssh_buffer_get(dest),
+ ssh_buffer_get_len(dest));
+ if (rv < 0) {
+ SSH_BUFFER_FREE(dest);
+ return -1;
+ }
- SSH_BUFFER_FREE(dest);
- return 0;
+ SSH_BUFFER_FREE(dest);
+ return 0;
}
/* decompression */
-static z_stream *initdecompress(ssh_session session) {
- z_stream *stream = NULL;
- int status;
+static z_stream *
+initdecompress(ssh_session session)
+{
+ z_stream *stream = NULL;
+ int status;
- stream = calloc(1, sizeof(z_stream));
- if (stream == NULL) {
- return NULL;
- }
+ stream = calloc(1, sizeof(z_stream));
+ if (stream == NULL) {
+ return NULL;
+ }
- status = inflateInit(stream);
- if (status != Z_OK) {
- SAFE_FREE(stream);
- ssh_set_error(session, SSH_FATAL,
- "Status = %d initiating inflate context!", status);
- return NULL;
- }
+ status = inflateInit(stream);
+ if (status != Z_OK) {
+ SAFE_FREE(stream);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Status = %d initiating inflate context!",
+ status);
+ return NULL;
+ }
- return stream;
+ return stream;
}
-static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen)
+static ssh_buffer
+gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen)
{
- struct ssh_crypto_struct *crypto = NULL;
- z_stream *zin = NULL;
- void *in_ptr = ssh_buffer_get(source);
- unsigned long in_size = ssh_buffer_get_len(source);
- unsigned char out_buf[BLOCKSIZE] = {0};
- ssh_buffer dest = NULL;
- unsigned long len;
- 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);
- if (zin == NULL) {
- return NULL;
+ struct ssh_crypto_struct *crypto = NULL;
+ z_stream *zin = NULL;
+ void *in_ptr = ssh_buffer_get(source);
+ uint32_t in_size = ssh_buffer_get_len(source);
+ unsigned char out_buf[BLOCKSIZE] = {0};
+ ssh_buffer dest = NULL;
+ uint32_t len;
+ int status;
+
+ crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
+ if (crypto == NULL) {
+ return NULL;
}
- }
-
- dest = ssh_buffer_new();
- if (dest == NULL) {
- return NULL;
- }
-
- zin->next_out = out_buf;
- zin->next_in = in_ptr;
- zin->avail_in = in_size;
- do {
- zin->avail_out = BLOCKSIZE;
- status = inflate(zin, Z_PARTIAL_FLUSH);
- if (status != Z_OK && status != Z_BUF_ERROR) {
- ssh_set_error(session, SSH_FATAL,
- "status %d inflating zlib packet", status);
- SSH_BUFFER_FREE(dest);
- return NULL;
+ zin = crypto->compress_in_ctx;
+ if (zin == NULL) {
+ zin = crypto->compress_in_ctx = initdecompress(session);
+ if (zin == NULL) {
+ return NULL;
+ }
}
- len = BLOCKSIZE - zin->avail_out;
- if (ssh_buffer_add_data(dest,out_buf,len) < 0) {
- 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);
- return NULL;
+ dest = ssh_buffer_new();
+ if (dest == NULL) {
+ return NULL;
}
- zin->next_out = out_buf;
- } while (zin->avail_out == 0);
- return dest;
+ zin->next_out = out_buf;
+ zin->next_in = in_ptr;
+ zin->avail_in = in_size;
+
+ do {
+ zin->avail_out = BLOCKSIZE;
+ status = inflate(zin, Z_PARTIAL_FLUSH);
+ if (status != Z_OK && status != Z_BUF_ERROR) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "status %d inflating zlib packet",
+ status);
+ 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);
+ return NULL;
+ }
+ if (ssh_buffer_get_len(dest) > maxlen) {
+ /* Size of packet exceeded, avoid a denial of service attack */
+ SSH_BUFFER_FREE(dest);
+ return NULL;
+ }
+ zin->next_out = out_buf;
+ } while (zin->avail_out == 0);
+
+ return dest;
}
-int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){
- ssh_buffer dest = NULL;
+int
+decompress_buffer(ssh_session session, ssh_buffer buf, size_t maxlen)
+{
+ ssh_buffer dest = NULL;
+ int rv;
- dest = gzip_decompress(session,buf, maxlen);
- if (dest == NULL) {
- return -1;
- }
+ dest = gzip_decompress(session, buf, maxlen);
+ if (dest == NULL) {
+ return -1;
+ }
- if (ssh_buffer_reinit(buf) < 0) {
- SSH_BUFFER_FREE(dest);
- return -1;
- }
+ if (ssh_buffer_reinit(buf) < 0) {
+ 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);
- return -1;
- }
+ rv = ssh_buffer_add_data(buf,
+ ssh_buffer_get(dest),
+ ssh_buffer_get_len(dest));
+ if (rv < 0) {
+ SSH_BUFFER_FREE(dest);
+ return -1;
+ }
- SSH_BUFFER_FREE(dest);
- return 0;
+ SSH_BUFFER_FREE(dest);
+ return 0;
}
diff --git a/src/init.c b/src/init.c
index 2ebcedf6..e516c331 100644
--- a/src/init.c
+++ b/src/init.c
@@ -22,6 +22,9 @@
*/
#include "config.h"
+
+#include <stdio.h>
+
#include "libssh/priv.h"
#include "libssh/socket.h"
#include "libssh/dh.h"
@@ -104,7 +107,7 @@ _ret:
/**
* @brief Initialize global cryptographic data structures.
*
- * This functions is automatically called when the library is loaded.
+ * This function is automatically called when the library is loaded.
*
*/
void libssh_constructor(void)
@@ -127,7 +130,7 @@ void libssh_constructor(void)
* The libssh library is implementing the SSH protocols and some of its
* extensions. This group of functions is mostly used to implement an SSH
* client.
- * Some function are needed to implement an SSH server too.
+ * Some functions are needed to implement an SSH server too.
*
* @{
*/
@@ -136,8 +139,8 @@ void libssh_constructor(void)
* @brief Initialize global cryptographic data structures.
*
* Since version 0.8.0, when libssh is dynamically linked, it is not necessary
- * to call this function on systems which are fully supported with regards to
- * threading (that is, system with pthreads available).
+ * to call this function on systems that fully support threading (that is,
+ * systems with pthreads available).
*
* If libssh is statically linked, it is necessary to explicitly call ssh_init()
* before calling any other provided API, and it is necessary to explicitly call
@@ -161,12 +164,14 @@ static int _ssh_finalize(unsigned destructor) {
if (_ssh_initialized > 1) {
_ssh_initialized--;
- goto _ret;
+ ssh_mutex_unlock(&ssh_init_mutex);
+ return 0;
}
if (_ssh_initialized == 1) {
if (_ssh_init_ret < 0) {
- goto _ret;
+ ssh_mutex_unlock(&ssh_init_mutex);
+ return 0;
}
}
}
@@ -181,15 +186,22 @@ static int _ssh_finalize(unsigned destructor) {
_ssh_initialized = 0;
-_ret:
if (!destructor) {
ssh_mutex_unlock(&ssh_init_mutex);
}
+
+#if (defined(_WIN32) && !defined(HAVE_PTHREAD))
+ if (ssh_init_mutex != NULL) {
+ DeleteCriticalSection(ssh_init_mutex);
+ SAFE_FREE(ssh_init_mutex);
+ }
+#endif
+
return 0;
}
/**
- * @brief Finalize and cleanup all libssh and cryptographic data structures.
+ * @brief Finalize and clean up all libssh and cryptographic data structures.
*
* This function is automatically called when the library is unloaded.
*
@@ -206,7 +218,7 @@ void libssh_destructor(void)
}
/**
- * @brief Finalize and cleanup all libssh and cryptographic data structures.
+ * @brief Finalize and clean up all libssh and cryptographic data structures.
*
* Since version 0.8.0, when libssh is dynamically linked, it is not necessary
* to call this function, since it is automatically called when the library is
@@ -269,7 +281,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,
*
* @see ssh_init()
*/
-bool is_ssh_initialized() {
+bool is_ssh_initialized(void) {
bool is_initialized = false;
diff --git a/src/kdf.c b/src/kdf.c
index 0e90e188..6bc477ce 100644
--- a/src/kdf.c
+++ b/src/kdf.c
@@ -39,7 +39,7 @@
/* The following implements the SSHKDF for crypto backend that
- * do not have a native implementations */
+ * do not have a native implementation */
struct ssh_mac_ctx_struct {
enum ssh_kdf_digest digest_type;
union {
@@ -58,73 +58,110 @@ static ssh_mac_ctx ssh_mac_ctx_init(enum ssh_kdf_digest type)
}
ctx->digest_type = type;
- switch(type){
+ switch (type) {
case SSH_KDF_SHA1:
ctx->ctx.sha1_ctx = sha1_init();
+ if (ctx->ctx.sha1_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA256:
ctx->ctx.sha256_ctx = sha256_init();
+ if (ctx->ctx.sha256_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA384:
ctx->ctx.sha384_ctx = sha384_init();
+ if (ctx->ctx.sha384_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA512:
ctx->ctx.sha512_ctx = sha512_init();
+ if (ctx->ctx.sha512_ctx == NULL) {
+ goto err;
+ }
return ctx;
- default:
- SAFE_FREE(ctx);
- return NULL;
}
+err:
+ SAFE_FREE(ctx);
+ return NULL;
}
-static void ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len)
+static void ssh_mac_ctx_free(ssh_mac_ctx ctx)
{
- switch(ctx->digest_type){
+ if (ctx == NULL) {
+ return;
+ }
+
+ switch (ctx->digest_type) {
case SSH_KDF_SHA1:
- sha1_update(ctx->ctx.sha1_ctx, data, len);
+ sha1_ctx_free(ctx->ctx.sha1_ctx);
break;
case SSH_KDF_SHA256:
- sha256_update(ctx->ctx.sha256_ctx, data, len);
+ sha256_ctx_free(ctx->ctx.sha256_ctx);
break;
case SSH_KDF_SHA384:
- sha384_update(ctx->ctx.sha384_ctx, data, len);
+ sha384_ctx_free(ctx->ctx.sha384_ctx);
break;
case SSH_KDF_SHA512:
- sha512_update(ctx->ctx.sha512_ctx, data, len);
+ sha512_ctx_free(ctx->ctx.sha512_ctx);
break;
}
+ SAFE_FREE(ctx);
}
-static void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+static int ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len)
{
- switch(ctx->digest_type){
+ switch (ctx->digest_type) {
+ case SSH_KDF_SHA1:
+ return sha1_update(ctx->ctx.sha1_ctx, data, len);
+ case SSH_KDF_SHA256:
+ return sha256_update(ctx->ctx.sha256_ctx, data, len);
+ case SSH_KDF_SHA384:
+ return sha384_update(ctx->ctx.sha384_ctx, data, len);
+ case SSH_KDF_SHA512:
+ return sha512_update(ctx->ctx.sha512_ctx, data, len);
+ }
+ return SSH_ERROR;
+}
+
+static int ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+{
+ int rc = SSH_ERROR;
+
+ switch (ctx->digest_type) {
case SSH_KDF_SHA1:
- sha1_final(md,ctx->ctx.sha1_ctx);
+ rc = sha1_final(md, ctx->ctx.sha1_ctx);
break;
case SSH_KDF_SHA256:
- sha256_final(md,ctx->ctx.sha256_ctx);
+ rc = sha256_final(md, ctx->ctx.sha256_ctx);
break;
case SSH_KDF_SHA384:
- sha384_final(md,ctx->ctx.sha384_ctx);
+ rc = sha384_final(md, ctx->ctx.sha384_ctx);
break;
case SSH_KDF_SHA512:
- sha512_final(md,ctx->ctx.sha512_ctx);
+ rc = sha512_final(md, ctx->ctx.sha512_ctx);
break;
}
SAFE_FREE(ctx);
+ return rc;
}
int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
- unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ unsigned char *key,
+ size_t key_len,
+ uint8_t key_type,
+ unsigned char *output,
size_t requested_len)
{
/* Can't use VLAs with Visual Studio, so allocate the biggest
* digest buffer we can possibly need */
unsigned char digest[DIGEST_MAX_LEN];
size_t output_len = crypto->digest_len;
- char letter = key_type;
ssh_mac_ctx ctx;
+ int rc;
if (DIGEST_MAX_LEN < crypto->digest_len) {
return -1;
@@ -135,11 +172,30 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
return -1;
}
- ssh_mac_update(ctx, key, key_len);
- ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
- ssh_mac_update(ctx, &letter, 1);
- ssh_mac_update(ctx, crypto->session_id, crypto->digest_len);
- ssh_mac_final(digest, ctx);
+ rc = ssh_mac_update(ctx, key, key_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, &key_type, 1);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->session_id, crypto->session_id_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_final(digest, ctx);
+ if (rc != SSH_OK) {
+ return -1;
+ }
if (requested_len < output_len) {
output_len = requested_len;
@@ -151,10 +207,25 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
if (ctx == NULL) {
return -1;
}
- ssh_mac_update(ctx, key, key_len);
- ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
- ssh_mac_update(ctx, output, output_len);
- ssh_mac_final(digest, ctx);
+ rc = ssh_mac_update(ctx, key, key_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, output, output_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_final(digest, ctx);
+ if (rc != SSH_OK) {
+ return -1;
+ }
if (requested_len < output_len + crypto->digest_len) {
memcpy(output + output_len, digest, requested_len - output_len);
} else {
diff --git a/src/kex.c b/src/kex.c
index d2ee93ba..0df4d3eb 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdbool.h>
+#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/dh.h"
@@ -88,60 +89,64 @@
#endif /* HAVE_LIBCRYPTO */
#ifdef WITH_ZLIB
-#define ZLIB "none,zlib,zlib@openssh.com"
+#define ZLIB "none,zlib@openssh.com,zlib"
+#define ZLIB_DEFAULT "none,zlib@openssh.com"
#else
#define ZLIB "none"
-#endif
+#define ZLIB_DEFAULT "none"
+#endif /* WITH_ZLIB */
#ifdef HAVE_CURVE25519
#define CURVE25519 "curve25519-sha256,curve25519-sha256@libssh.org,"
#else
#define CURVE25519 ""
-#endif
+#endif /* HAVE_CURVE25519 */
-#ifdef HAVE_ECDH
+#ifdef HAVE_ECC
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
-#define EC_HOSTKEYS "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,"
-#define EC_PUBLIC_KEY_ALGORITHMS "ecdsa-sha2-nistp521-cert-v01@openssh.com," \
+#define EC_HOSTKEYS "ecdsa-sha2-nistp521," \
+ "ecdsa-sha2-nistp384," \
+ "ecdsa-sha2-nistp256,"
+#define EC_SK_HOSTKEYS "sk-ecdsa-sha2-nistp256@openssh.com,"
+#define EC_FIPS_PUBLIC_KEY_ALGOS "ecdsa-sha2-nistp521-cert-v01@openssh.com," \
"ecdsa-sha2-nistp384-cert-v01@openssh.com," \
"ecdsa-sha2-nistp256-cert-v01@openssh.com,"
+#define EC_PUBLIC_KEY_ALGORITHMS EC_FIPS_PUBLIC_KEY_ALGOS \
+ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,"
#else
+#define ECDH ""
#define EC_HOSTKEYS ""
+#define EC_SK_HOSTKEYS ""
+#define EC_FIPS_PUBLIC_KEY_ALGOS ""
#define EC_PUBLIC_KEY_ALGORITHMS ""
-#define ECDH ""
-#endif
-
-#ifdef HAVE_DSA
-#define DSA_HOSTKEYS ",ssh-dss"
-#define DSA_PUBLIC_KEY_ALGORITHMS ",ssh-dss-cert-v01@openssh.com"
-#else
-#define DSA_HOSTKEYS ""
-#define DSA_PUBLIC_KEY_ALGORITHMS ""
-#endif
+#endif /* HAVE_ECC */
#ifdef WITH_INSECURE_NONE
#define NONE ",none"
#else
#define NONE
-#endif
+#endif /* WITH_INSECURE_NONE */
#define HOSTKEYS "ssh-ed25519," \
EC_HOSTKEYS \
+ "sk-ssh-ed25519@openssh.com," \
+ EC_SK_HOSTKEYS \
"rsa-sha2-512," \
"rsa-sha2-256," \
- "ssh-rsa" \
- DSA_HOSTKEYS
+ "ssh-rsa"
#define DEFAULT_HOSTKEYS "ssh-ed25519," \
EC_HOSTKEYS \
+ "sk-ssh-ed25519@openssh.com," \
+ EC_SK_HOSTKEYS \
"rsa-sha2-512," \
"rsa-sha2-256"
#define PUBLIC_KEY_ALGORITHMS "ssh-ed25519-cert-v01@openssh.com," \
+ "sk-ssh-ed25519-cert-v01@openssh.com," \
EC_PUBLIC_KEY_ALGORITHMS \
"rsa-sha2-512-cert-v01@openssh.com," \
"rsa-sha2-256-cert-v01@openssh.com," \
- "ssh-rsa-cert-v01@openssh.com" \
- DSA_PUBLIC_KEY_ALGORITHMS "," \
+ "ssh-rsa-cert-v01@openssh.com," \
HOSTKEYS
#define DEFAULT_PUBLIC_KEY_ALGORITHMS "ssh-ed25519-cert-v01@openssh.com," \
EC_PUBLIC_KEY_ALGORITHMS \
@@ -173,6 +178,9 @@
/* RFC 8308 */
#define KEX_EXTENSION_CLIENT "ext-info-c"
+/* Strict kex mitigation against CVE-2023-48795 */
+#define KEX_STRICT_CLIENT "kex-strict-c-v00@openssh.com"
+#define KEX_STRICT_SERVER "kex-strict-s-v00@openssh.com"
/* Allowed algorithms in FIPS mode */
#define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\
@@ -186,7 +194,7 @@
"rsa-sha2-512," \
"rsa-sha2-256"
-#define FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS EC_PUBLIC_KEY_ALGORITHMS \
+#define FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS EC_FIPS_PUBLIC_KEY_ALGOS \
"rsa-sha2-512-cert-v01@openssh.com," \
"rsa-sha2-256-cert-v01@openssh.com," \
FIPS_ALLOWED_HOSTKEYS
@@ -214,8 +222,8 @@ static const char *fips_methods[] = {
FIPS_ALLOWED_CIPHERS,
FIPS_ALLOWED_MACS,
FIPS_ALLOWED_MACS,
- ZLIB,
- ZLIB,
+ ZLIB_DEFAULT,
+ ZLIB_DEFAULT,
"",
"",
NULL
@@ -229,8 +237,8 @@ static const char *default_methods[] = {
CHACHA20 AES,
"hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512",
"hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512",
- "none",
- "none",
+ ZLIB_DEFAULT,
+ ZLIB_DEFAULT,
"",
"",
NULL
@@ -316,6 +324,10 @@ static int cmp_first_kex_algo(const char *client_str,
int is_wrong = 1;
+ if (client_str == NULL || server_str == NULL) {
+ return is_wrong;
+ }
+
colon = strchr(client_str, ',');
if (colon == NULL) {
client_kex_len = strlen(client_str);
@@ -342,6 +354,7 @@ static int cmp_first_kex_algo(const char *client_str,
SSH_PACKET_CALLBACK(ssh_packet_kexinit)
{
int i, ok;
+ struct ssh_crypto_struct *crypto = session->next_crypto;
int server_kex = session->server;
ssh_string str = NULL;
char *strings[SSH_KEX_METHODS] = {0};
@@ -355,35 +368,67 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
(void)type;
(void)user;
+ SSH_LOG(SSH_LOG_TRACE, "KEXINIT received");
+
if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) {
- SSH_LOG(SSH_LOG_INFO, "Initiating key re-exchange");
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
+ SSH_LOG(SSH_LOG_DEBUG, "Peer initiated key re-exchange");
+ /* Reset the sent flag if the re-kex was initiated by the peer */
+ session->flags &= ~SSH_SESSION_FLAG_KEXINIT_SENT;
+ } else if (session->flags & SSH_SESSION_FLAG_KEXINIT_SENT &&
+ session->dh_handshake_state == DH_STATE_INIT_SENT) {
+ /* This happens only when we are sending our-guessed first kex
+ * packet right after our KEXINIT packet. */
+ SSH_LOG(SSH_LOG_DEBUG, "Received peer kexinit answer.");
+ } else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) {
+ ssh_set_error(session, SSH_FATAL,
+ "SSH_KEXINIT received in wrong state");
+ goto error;
+ }
} else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) {
- ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
+ ssh_set_error(session, SSH_FATAL,
+ "SSH_KEXINIT received in wrong state");
goto error;
}
if (server_kex) {
- len = ssh_buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16);
+#ifdef WITH_SERVER
+ len = ssh_buffer_get_data(packet, crypto->client_kex.cookie, 16);
if (len != 16) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: no cookie in packet");
goto error;
}
- ok = ssh_hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie);
+ ok = ssh_hashbufin_add_cookie(session, crypto->client_kex.cookie);
if (ok < 0) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: adding cookie failed");
goto error;
}
+
+ ok = server_set_kex(session);
+ if (ok == SSH_ERROR) {
+ goto error;
+ }
+#endif /* WITH_SERVER */
} else {
- len = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16);
+ len = ssh_buffer_get_data(packet, crypto->server_kex.cookie, 16);
if (len != 16) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: no cookie in packet");
goto error;
}
- ok = ssh_hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie);
+ ok = ssh_hashbufin_add_cookie(session, crypto->server_kex.cookie);
if (ok < 0) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: adding cookie failed");
+ goto error;
+ }
+
+ ok = ssh_set_client_kex(session);
+ if (ok == SSH_ERROR) {
goto error;
}
}
@@ -396,7 +441,8 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
rc = ssh_buffer_add_ssh_string(session->in_hashbuf, str);
if (rc < 0) {
- ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer");
+ ssh_set_error(session, SSH_FATAL,
+ "Error adding string in hash buffer");
goto error;
}
@@ -409,14 +455,16 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
str = NULL;
}
- /* copy the server kex info into an array of strings */
+ /* copy the peer kex info into an array of strings */
if (server_kex) {
+#ifdef WITH_SERVER
for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->client_kex.methods[i] = strings[i];
+ crypto->client_kex.methods[i] = strings[i];
}
+#endif /* WITH_SERVER */
} else { /* client */
for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->server_kex.methods[i] = strings[i];
+ crypto->server_kex.methods[i] = strings[i];
}
}
@@ -430,30 +478,70 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
* that its value is included when computing the session ID (see
* 'make_sessionid').
*/
- if (server_kex) {
- rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
- if (rc != 1) {
- goto error;
- }
- rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
+ if (rc != 1) {
+ goto error;
+ }
- rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
+ if (rc < 0) {
+ goto error;
+ }
+
+ /*
+ * Remember whether 'first_kex_packet_follows' was set and the client
+ * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
+ * must be ignored on the server side.
+ * Client needs to start the Key exchange over with the correct method
+ */
+ if (first_kex_packet_follows || session->send_first_kex_follows) {
+ char **client_methods = crypto->client_kex.methods;
+ char **server_methods = crypto->server_kex.methods;
+ session->first_kex_follows_guess_wrong =
+ cmp_first_kex_algo(client_methods[SSH_KEX],
+ server_methods[SSH_KEX]) ||
+ cmp_first_kex_algo(client_methods[SSH_HOSTKEYS],
+ server_methods[SSH_HOSTKEYS]);
+ SSH_LOG(SSH_LOG_DEBUG, "The initial guess was %s.",
+ session->first_kex_follows_guess_wrong ? "wrong" : "right");
+ }
+
+ /*
+ * handle the "strict KEX" feature. If supported by peer, then set up the
+ * flag and verify packet sequence numbers.
+ */
+ if (server_kex) {
+ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX],
+ KEX_STRICT_CLIENT);
+ if (ok) {
+ SSH_LOG(SSH_LOG_DEBUG, "Client supports strict kex, enabling.");
+ session->flags |= SSH_SESSION_FLAG_KEX_STRICT;
+ }
+ } else {
+ /* client kex */
+ ok = ssh_match_group(crypto->server_kex.methods[SSH_KEX],
+ KEX_STRICT_SERVER);
+ if (ok) {
+ SSH_LOG(SSH_LOG_DEBUG, "Server supports strict kex, enabling.");
+ session->flags |= SSH_SESSION_FLAG_KEX_STRICT;
+ }
+ }
+#ifdef WITH_SERVER
+ if (server_kex) {
/*
* If client sent a ext-info-c message in the kex list, it supports
* RFC 8308 extension negotiation.
*/
- ok = ssh_match_group(session->next_crypto->client_kex.methods[SSH_KEX],
+ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX],
KEX_EXTENSION_CLIENT);
if (ok) {
- const char *hostkeys = NULL;
+ const char *hostkeys = NULL, *wanted_hostkeys = NULL;
/* The client supports extension negotiation */
session->extensions |= SSH_EXT_NEGOTIATION;
@@ -463,14 +551,14 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
* by the client and enable the respective extensions to provide
* correct signature in the next packet if RSA is negotiated
*/
- hostkeys = session->next_crypto->client_kex.methods[SSH_HOSTKEYS];
+ hostkeys = crypto->client_kex.methods[SSH_HOSTKEYS];
+ wanted_hostkeys = session->opts.wanted_methods[SSH_HOSTKEYS];
ok = ssh_match_group(hostkeys, "rsa-sha2-512");
if (ok) {
/* Check if rsa-sha2-512 is allowed by config */
- if (session->opts.wanted_methods[SSH_HOSTKEYS] != NULL) {
- char *is_allowed =
- ssh_find_matching(session->opts.wanted_methods[SSH_HOSTKEYS],
- "rsa-sha2-512");
+ if (wanted_hostkeys != NULL) {
+ char *is_allowed = ssh_find_matching(wanted_hostkeys,
+ "rsa-sha2-512");
if (is_allowed != NULL) {
session->extensions |= SSH_EXT_SIG_RSA_SHA512;
}
@@ -480,10 +568,9 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
ok = ssh_match_group(hostkeys, "rsa-sha2-256");
if (ok) {
/* Check if rsa-sha2-256 is allowed by config */
- if (session->opts.wanted_methods[SSH_HOSTKEYS] != NULL) {
- char *is_allowed =
- ssh_find_matching(session->opts.wanted_methods[SSH_HOSTKEYS],
- "rsa-sha2-256");
+ if (wanted_hostkeys != NULL) {
+ char *is_allowed = ssh_find_matching(wanted_hostkeys,
+ "rsa-sha2-256");
if (is_allowed != NULL) {
session->extensions |= SSH_EXT_SIG_RSA_SHA256;
}
@@ -499,7 +586,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
(session->extensions & SSH_EXT_SIG_RSA_SHA512)) {
session->extensions &= ~(SSH_EXT_SIG_RSA_SHA256 | SSH_EXT_SIG_RSA_SHA512);
rsa_sig_ext = ssh_find_matching("rsa-sha2-512,rsa-sha2-256",
- session->next_crypto->client_kex.methods[SSH_HOSTKEYS]);
+ hostkeys);
if (rsa_sig_ext == NULL) {
goto error; /* should never happen */
} else if (strcmp(rsa_sig_ext, "rsa-sha2-512") == 0) {
@@ -518,24 +605,17 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
session->extensions & SSH_EXT_SIG_RSA_SHA256 ? "SHA256" : "",
session->extensions & SSH_EXT_SIG_RSA_SHA512 ? " SHA512" : "");
}
-
- /*
- * Remember whether 'first_kex_packet_follows' was set and the client
- * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
- * must be ignored.
- */
- if (first_kex_packet_follows) {
- session->first_kex_follows_guess_wrong =
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_KEX],
- session->next_crypto->server_kex.methods[SSH_KEX]) ||
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_HOSTKEYS],
- session->next_crypto->server_kex.methods[SSH_HOSTKEYS]);
- }
}
+#endif /* WITH_SERVER */
/* Note, that his overwrites authenticated state in case of rekeying */
session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED;
- session->dh_handshake_state = DH_STATE_INIT;
+ /* if we already sent our initial key exchange packet, do not reset the
+ * DH state. We will know if we were right with our guess only in
+ * dh_handshake_state() */
+ if (session->send_first_kex_follows == false) {
+ session->dh_handshake_state = DH_STATE_INIT;
+ }
session->ssh_connection_callback(session);
return SSH_PACKET_USED;
@@ -543,7 +623,9 @@ error:
SSH_STRING_FREE(str);
for (i = 0; i < SSH_KEX_METHODS; i++) {
if (server_kex) {
+#ifdef WITH_SERVER
session->next_crypto->client_kex.methods[i] = NULL;
+#endif /* WITH_SERVER */
} else { /* client */
session->next_crypto->server_kex.methods[i] = NULL;
}
@@ -601,7 +683,7 @@ char *ssh_client_select_hostkeys(ssh_session session)
/* 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,
+ SSH_LOG(SSH_LOG_TRACE,
"List of allowed host key algorithms is empty or contains only "
"unsupported algorithms");
return NULL;
@@ -654,7 +736,7 @@ char *ssh_client_select_hostkeys(ssh_session session)
fips_hostkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, new_hostkeys);
SAFE_FREE(new_hostkeys);
if (fips_hostkeys == NULL) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"None of the wanted host keys or keys in known_hosts files "
"is allowed in FIPS mode.");
return NULL;
@@ -677,11 +759,14 @@ int ssh_set_client_kex(ssh_session session)
{
struct ssh_kex_struct *client = &session->next_crypto->client_kex;
const char *wanted;
- char *kex = NULL;
- char *kex_tmp = NULL;
int ok;
int i;
- size_t kex_len, len;
+
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (client->methods[0] != NULL) {
+ return SSH_OK;
+ }
ok = ssh_get_random(client->cookie, 16, 0);
if (!ok) {
@@ -689,8 +774,6 @@ int ssh_set_client_kex(ssh_session session)
return SSH_ERROR;
}
- 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++) {
@@ -726,23 +809,52 @@ int ssh_set_client_kex(ssh_session session)
return SSH_OK;
}
- /* Here we append ext-info-c to the list of kex algorithms */
- kex = client->methods[SSH_KEX];
+ ok = ssh_kex_append_extensions(session, client);
+ if (ok != SSH_OK){
+ return ok;
+ }
+
+ return SSH_OK;
+}
+
+int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex)
+{
+ char *kex = NULL;
+ char *kex_tmp = NULL;
+ size_t kex_len, len;
+
+ /* Here we append ext-info-c and kex-strict-c-v00@openssh.com for client
+ * and kex-strict-s-v00@openssh.com for server to the list of kex algorithms
+ */
+ kex = pkex->methods[SSH_KEX];
len = strlen(kex);
- if (len + strlen(KEX_EXTENSION_CLIENT) + 2 < len) {
+ if (session->server) {
+ /* Comma, nul byte */
+ kex_len = len + 1 + strlen(KEX_STRICT_SERVER) + 1;
+ } else {
+ /* Comma, comma, nul byte */
+ kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 +
+ strlen(KEX_STRICT_CLIENT) + 1;
+ }
+ if (kex_len >= MAX_PACKET_LEN) {
/* Overflow */
return SSH_ERROR;
}
- kex_len = len + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */
kex_tmp = realloc(kex, kex_len);
if (kex_tmp == NULL) {
- free(kex);
ssh_set_error_oom(session);
return SSH_ERROR;
}
- snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_EXTENSION_CLIENT);
- client->methods[SSH_KEX] = kex_tmp;
-
+ if (session->server){
+ snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_STRICT_SERVER);
+ } else {
+ snprintf(kex_tmp + len,
+ kex_len - len,
+ ",%s,%s",
+ KEX_EXTENSION_CLIENT,
+ KEX_STRICT_CLIENT);
+ }
+ pkex->methods[SSH_KEX] = kex_tmp;
return SSH_OK;
}
@@ -760,71 +872,142 @@ static const char *ssh_find_aead_hmac(const char *cipher)
return NULL;
}
+static enum ssh_key_exchange_e
+kex_select_kex_type(const char *kex)
+{
+ if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) {
+ return SSH_KEX_DH_GROUP1_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) {
+ return SSH_KEX_DH_GROUP14_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) {
+ return SSH_KEX_DH_GROUP14_SHA256;
+ } else if (strcmp(kex, "diffie-hellman-group16-sha512") == 0) {
+ return SSH_KEX_DH_GROUP16_SHA512;
+ } else if (strcmp(kex, "diffie-hellman-group18-sha512") == 0) {
+ return SSH_KEX_DH_GROUP18_SHA512;
+#ifdef WITH_GEX
+ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha1") == 0) {
+ return SSH_KEX_DH_GEX_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha256") == 0) {
+ return SSH_KEX_DH_GEX_SHA256;
+#endif /* WITH_GEX */
+ } else if (strcmp(kex, "ecdh-sha2-nistp256") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP256;
+ } else if (strcmp(kex, "ecdh-sha2-nistp384") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP384;
+ } else if (strcmp(kex, "ecdh-sha2-nistp521") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP521;
+ } else if (strcmp(kex, "curve25519-sha256@libssh.org") == 0) {
+ return SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
+ } else if (strcmp(kex, "curve25519-sha256") == 0) {
+ return SSH_KEX_CURVE25519_SHA256;
+ }
+ /* should not happen. We should be getting only valid names at this stage */
+ return 0;
+}
+
+
+/** @internal
+ * @brief Reverts guessed callbacks set during the dh_handshake()
+ * @param session session handle
+ * @returns void
+ */
+static void revert_kex_callbacks(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:
+ ssh_client_dh_remove_callbacks(session);
+ break;
+#ifdef WITH_GEX
+ case SSH_KEX_DH_GEX_SHA1:
+ case SSH_KEX_DH_GEX_SHA256:
+ ssh_client_dhgex_remove_callbacks(session);
+ break;
+#endif /* WITH_GEX */
+#ifdef HAVE_ECDH
+ case SSH_KEX_ECDH_SHA2_NISTP256:
+ case SSH_KEX_ECDH_SHA2_NISTP384:
+ case SSH_KEX_ECDH_SHA2_NISTP521:
+ ssh_client_ecdh_remove_callbacks(session);
+ break;
+#endif
+#ifdef HAVE_CURVE25519
+ case SSH_KEX_CURVE25519_SHA256:
+ case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
+ ssh_client_curve25519_remove_callbacks(session);
+ break;
+#endif
+ }
+}
+
/** @brief Select the different methods on basis of client's and
* server's kex messages, and watches out if a match is possible.
*/
int ssh_kex_select_methods (ssh_session session)
{
- struct ssh_kex_struct *server = &session->next_crypto->server_kex;
- struct ssh_kex_struct *client = &session->next_crypto->client_kex;
+ struct ssh_crypto_struct *crypto = session->next_crypto;
+ struct ssh_kex_struct *server = &crypto->server_kex;
+ struct ssh_kex_struct *client = &crypto->client_kex;
char *ext_start = NULL;
const char *aead_hmac = NULL;
+ enum ssh_key_exchange_e kex_type;
int i;
- /* Here we should drop the ext-info-c from the list so we avoid matching.
+ /* Here we should drop the extensions from the list so we avoid matching.
* it. We added it to the end, so we can just truncate the string here */
- ext_start = strstr(client->methods[SSH_KEX], ","KEX_EXTENSION_CLIENT);
- if (ext_start != NULL) {
- ext_start[0] = '\0';
+ if (session->client) {
+ ext_start = strstr(client->methods[SSH_KEX], "," KEX_EXTENSION_CLIENT);
+ if (ext_start != NULL) {
+ ext_start[0] = '\0';
+ }
+ }
+ if (session->server) {
+ ext_start = strstr(server->methods[SSH_KEX], "," KEX_STRICT_SERVER);
+ if (ext_start != NULL) {
+ ext_start[0] = '\0';
+ }
}
for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]);
+ crypto->kex_methods[i] = ssh_find_matching(server->methods[i],
+ client->methods[i]);
if (i == SSH_MAC_C_S || i == SSH_MAC_S_C) {
- aead_hmac = ssh_find_aead_hmac(session->next_crypto->kex_methods[i-2]);
+ aead_hmac = ssh_find_aead_hmac(crypto->kex_methods[i - 2]);
if (aead_hmac) {
- free(session->next_crypto->kex_methods[i]);
- session->next_crypto->kex_methods[i] = strdup(aead_hmac);
+ free(crypto->kex_methods[i]);
+ crypto->kex_methods[i] = strdup(aead_hmac);
}
}
- 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]",
- ssh_kex_descriptions[i],server->methods[i],client->methods[i]);
+ if (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]", ssh_kex_descriptions[i],
+ server->methods[i], client->methods[i]);
return SSH_ERROR;
- } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) {
+ } else if ((i >= SSH_LANG_C_S) && (crypto->kex_methods[i] == NULL)) {
/* we can safely do that for languages */
- session->next_crypto->kex_methods[i] = strdup("");
+ crypto->kex_methods[i] = strdup("");
}
}
- if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
- 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){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP18_SHA512;
-#ifdef WITH_GEX
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA1;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA256;
-#endif /* WITH_GEX */
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp384") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP384;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp521") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP521;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256;
- }
- SSH_LOG(SSH_LOG_INFO, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
+
+ /* We can not set this value directly as the old value is needed to revert
+ * callbacks if we are client */
+ kex_type = kex_select_kex_type(crypto->kex_methods[SSH_KEX]);
+ if (session->client && session->first_kex_follows_guess_wrong) {
+ SSH_LOG(SSH_LOG_DEBUG, "Our guess was wrong. Restarting the KEX");
+ /* We need to remove the wrong callbacks and start kex again */
+ revert_kex_callbacks(session);
+ session->dh_handshake_state = DH_STATE_INIT;
+ session->first_kex_follows_guess_wrong = false;
+ }
+ crypto->kex_type = kex_type;
+
+ SSH_LOG(SSH_LOG_DEBUG, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
session->next_crypto->kex_methods[SSH_KEX],
session->next_crypto->kex_methods[SSH_HOSTKEYS],
session->next_crypto->kex_methods[SSH_CRYPT_C_S],
@@ -841,63 +1024,116 @@ int ssh_kex_select_methods (ssh_session session)
/* this function only sends the predefined set of kex methods */
-int ssh_send_kex(ssh_session session, int server_kex)
+int ssh_send_kex(ssh_session session)
{
- struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
- &session->next_crypto->client_kex);
- ssh_string str = NULL;
- int i;
- int rc;
-
- rc = ssh_buffer_pack(session->out_buffer,
- "bP",
- SSH2_MSG_KEXINIT,
- 16,
- kex->cookie); /* cookie */
- if (rc != SSH_OK)
- goto error;
- if (ssh_hashbufout_add_cookie(session) < 0) {
- goto error;
- }
+ struct ssh_kex_struct *kex = (session->server ?
+ &session->next_crypto->server_kex :
+ &session->next_crypto->client_kex);
+ ssh_string str = NULL;
+ int i;
+ int rc;
+ int first_kex_packet_follows = 0;
+
+ /* Only client can initiate the handshake methods we implement. If we
+ * already received the peer mechanisms, there is no point in guessing */
+ if (session->client &&
+ session->session_state != SSH_SESSION_STATE_KEXINIT_RECEIVED &&
+ session->send_first_kex_follows) {
+ first_kex_packet_follows = 1;
+ }
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Sending KEXINIT packet, first_kex_packet_follows = %d",
+ first_kex_packet_follows);
+
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bP",
+ SSH2_MSG_KEXINIT,
+ 16,
+ kex->cookie); /* cookie */
+ if (rc != SSH_OK)
+ goto error;
+ if (ssh_hashbufout_add_cookie(session) < 0) {
+ goto error;
+ }
- ssh_list_kex(kex);
+ ssh_list_kex(kex);
- for (i = 0; i < SSH_KEX_METHODS; i++) {
- str = ssh_string_from_char(kex->methods[i]);
- if (str == NULL) {
- goto error;
+ for (i = 0; i < SSH_KEX_METHODS; i++) {
+ str = ssh_string_from_char(kex->methods[i]);
+ if (str == NULL) {
+ goto error;
+ }
+
+ rc = ssh_buffer_add_ssh_string(session->out_hashbuf, str);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, str);
+ if (rc < 0) {
+ goto error;
+ }
+ SSH_STRING_FREE(str);
+ str = NULL;
}
- if (ssh_buffer_add_ssh_string(session->out_hashbuf, str) < 0) {
- goto error;
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bd",
+ first_kex_packet_follows,
+ 0);
+ if (rc != SSH_OK) {
+ goto error;
}
- if (ssh_buffer_add_ssh_string(session->out_buffer, str) < 0) {
- goto error;
+
+ /* Prepare also the first_kex_packet_follows and reserved to 0 */
+ rc = ssh_buffer_add_u8(session->out_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_u32(session->out_hashbuf, 0);
+ if (rc < 0) {
+ goto error;
}
- SSH_STRING_FREE(str);
- str = NULL;
- }
- rc = ssh_buffer_pack(session->out_buffer,
- "bd",
- 0,
- 0);
- if (rc != SSH_OK) {
- goto error;
- }
+ rc = ssh_packet_send(session);
+ if (rc == SSH_ERROR) {
+ return -1;
+ }
- if (ssh_packet_send(session) == SSH_ERROR) {
- return -1;
- }
+ session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT;
+ SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
+
+ /* If we indicated that we are sending the guessed key exchange packet,
+ * do it now. The packet is simple, but we need to do some preparations */
+ if (first_kex_packet_follows == 1) {
+ char *list = kex->methods[SSH_KEX];
+ char *colon = strchr(list, ',');
+ size_t kex_name_len = colon ? (size_t)(colon - list) : strlen(list);
+ char *kex_name = calloc(kex_name_len + 1, 1);
+ if (kex_name == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ snprintf(kex_name, kex_name_len + 1, "%.*s", (int)kex_name_len, list);
+ SSH_LOG(SSH_LOG_TRACE, "Sending the first kex packet for %s", kex_name);
+
+ session->next_crypto->kex_type = kex_select_kex_type(kex_name);
+ free(kex_name);
+
+ /* run the first step of the DH handshake */
+ session->dh_handshake_state = DH_STATE_INIT;
+ if (dh_handshake(session) == SSH_ERROR) {
+ goto error;
+ }
+ }
+ return 0;
- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
- return 0;
error:
- ssh_buffer_reinit(session->out_buffer);
- ssh_buffer_reinit(session->out_hashbuf);
- SSH_STRING_FREE(str);
+ ssh_buffer_reinit(session->out_buffer);
+ ssh_buffer_reinit(session->out_hashbuf);
+ SSH_STRING_FREE(str);
- return -1;
+ return -1;
}
/*
@@ -940,7 +1176,7 @@ int ssh_send_rekex(ssh_session session)
}
session->dh_handshake_state = DH_STATE_INIT;
- rc = ssh_send_kex(session, session->server);
+ rc = ssh_send_kex(session);
if (rc < 0) {
SSH_LOG(SSH_LOG_PACKET, "Failed to send kex");
return rc;
@@ -965,13 +1201,13 @@ char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list)
/**
* @internal
*
- * @brief Return a new allocated string containing only the FIPS allowed
+ * @brief Return a newly allocated string containing only the FIPS allowed
* algorithms from the list.
*
* @param[in] algo The type of the methods to filter
* @param[in] list The list to be filtered
*
- * @return A new allocated list containing only the FIPS allowed algorithms from
+ * @return A newly allocated list containing only the FIPS allowed algorithms from
* the list; NULL in case of error.
*/
char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list)
@@ -983,6 +1219,111 @@ char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list)
return ssh_find_all_matching(fips_methods[algo], list);
}
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms plus the algorithms specified in list. If the system
+ * runs in fips mode, this will add only fips approved algorithms.
+ * Empty list will cause error.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be appended
+ *
+ * @return A newly allocated list containing the default algorithms and the
+ * algorithms in list at the end; NULL in case of error.
+ */
+char *ssh_add_to_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ char *tmp = NULL, *ret = NULL;
+
+ if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') {
+ return NULL;
+ }
+
+ if (ssh_fips_mode()) {
+ tmp = ssh_append_without_duplicates(fips_methods[algo], list);
+ ret = ssh_find_all_matching(fips_methods[algo], tmp);
+ } else {
+ tmp = ssh_append_without_duplicates(default_methods[algo], list);
+ ret = ssh_find_all_matching(supported_methods[algo], tmp);
+ }
+
+ free(tmp);
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms excluding the algorithms specified in list. If the system
+ * runs in fips mode, this will remove from the fips_methods list.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be exclude
+ *
+ * @return A newly allocated list containing the default algorithms without the
+ * algorithms in list; NULL in case of error.
+ */
+char *ssh_remove_from_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ if (algo > SSH_LANG_S_C) {
+ return NULL;
+ }
+
+ if (list == NULL || list[0] == '\0') {
+ if (ssh_fips_mode()) {
+ return strdup(fips_methods[algo]);
+ } else {
+ return strdup(default_methods[algo]);
+ }
+ }
+
+ if (ssh_fips_mode()) {
+ return ssh_remove_all_matching(fips_methods[algo], list);
+ } else {
+ return ssh_remove_all_matching(default_methods[algo], list);
+ }
+}
+
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms with prioritized algorithms specified in list. If the
+ * algorithms are present in the default list they get prioritized, if not
+ * they are added to the front of the default list. If the system
+ * runs in fips mode, this will work with the fips_methods list.
+ * Empty list will cause error.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be pushed to priority
+ *
+ * @return A newly allocated list containing the default algorithms prioritized
+ * with the algorithms in list at the beginning of the list; NULL in case
+ * of error.
+ */
+char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ char *ret = NULL, *tmp = NULL;
+
+ if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') {
+ return NULL;
+ }
+
+ if (ssh_fips_mode()) {
+ tmp = ssh_prefix_without_duplicates(fips_methods[algo], list);
+ ret = ssh_find_all_matching(fips_methods[algo], tmp);
+ } else {
+ tmp = ssh_prefix_without_duplicates(default_methods[algo], list);
+ ret = ssh_find_all_matching(supported_methods[algo], tmp);
+ }
+
+ free(tmp);
+ return ret;
+}
+
int ssh_make_sessionid(ssh_session session)
{
ssh_string num = NULL;
@@ -990,10 +1331,18 @@ int ssh_make_sessionid(ssh_session session)
ssh_buffer client_hash = NULL;
ssh_buffer buf = NULL;
ssh_string server_pubkey_blob = NULL;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum client_pubkey, server_pubkey;
+#else
+ bignum client_pubkey = NULL, server_pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
#ifdef WITH_GEX
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum modulus, generator;
-#endif
+#else
+ bignum modulus = NULL, generator = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* WITH_GEX */
int rc = SSH_ERROR;
buf = ssh_buffer_new();
@@ -1017,33 +1366,6 @@ int ssh_make_sessionid(ssh_session session)
client_hash = session->in_hashbuf;
}
- /*
- * Handle the two final fields for the KEXINIT message (RFC 4253 7.1):
- *
- * boolean first_kex_packet_follows
- * uint32 0 (reserved for future extension)
- */
- rc = ssh_buffer_add_u8(server_hash, 0);
- if (rc < 0) {
- goto error;
- }
- rc = ssh_buffer_add_u32(server_hash, 0);
- if (rc < 0) {
- goto error;
- }
-
- /* These fields are handled for the server case in ssh_packet_kexinit. */
- if (session->client) {
- rc = ssh_buffer_add_u8(client_hash, 0);
- if (rc < 0) {
- goto error;
- }
- rc = ssh_buffer_add_u32(client_hash, 0);
- if (rc < 0) {
- goto error;
- }
- }
-
rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
if (rc != SSH_OK) {
goto error;
@@ -1086,6 +1408,10 @@ int ssh_make_sessionid(ssh_session session)
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(client_pubkey);
+ bignum_safe_free(server_pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
break;
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
@@ -1117,6 +1443,10 @@ int ssh_make_sessionid(ssh_session session)
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(modulus);
+ bignum_safe_free(generator);
+#endif /* OPENSSL_VERSION_NUMBER */
break;
#endif /* WITH_GEX */
#ifdef HAVE_ECDH
@@ -1125,7 +1455,7 @@ int ssh_make_sessionid(ssh_session session)
case SSH_KEX_ECDH_SHA2_NISTP521:
if (session->next_crypto->ecdh_client_pubkey == NULL ||
session->next_crypto->ecdh_server_pubkey == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing");
+ SSH_LOG(SSH_LOG_TRACE, "ECDH parameter missing");
goto error;
}
rc = ssh_buffer_pack(buf,
@@ -1136,7 +1466,7 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
break;
-#endif
+#endif /* HAVE_ECDH */
#ifdef HAVE_CURVE25519
case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
@@ -1151,7 +1481,7 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
break;
-#endif
+#endif /* HAVE_CURVE25519 */
}
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
if (rc != SSH_OK) {
@@ -1233,12 +1563,14 @@ int ssh_make_sessionid(ssh_session session)
}
memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash,
session->next_crypto->digest_len);
+ /* Initial length is the same as secret hash */
+ session->next_crypto->session_id_len = session->next_crypto->digest_len;
}
#ifdef DEBUG_CRYPTO
- printf("Session hash: \n");
+ SSH_LOG(SSH_LOG_DEBUG, "Session hash: \n");
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
+ ssh_log_hexdump("session id", session->next_crypto->session_id, session->next_crypto->session_id_len);
+#endif /* DEBUG_CRYPTO */
rc = SSH_OK;
error:
@@ -1250,6 +1582,10 @@ error:
session->out_hashbuf = NULL;
SSH_STRING_FREE(num);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(client_pubkey);
+ bignum_safe_free(server_pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
return rc;
}
@@ -1434,7 +1770,7 @@ int ssh_generate_session_keys(ssh_session session)
intkey_cli_to_srv_len);
ssh_log_hexdump("Server to Client Integrity Key", intkey_srv_to_cli,
intkey_srv_to_cli_len);
-#endif
+#endif /* DEBUG_CRYPTO */
rc = 0;
error:
diff --git a/src/known_hosts.c b/src/known_hosts.c
index 3feab159..a1d9a432 100644
--- a/src/known_hosts.c
+++ b/src/known_hosts.c
@@ -65,12 +65,12 @@
* @param[out] file A pointer to the known host file. Could be pointing to
* NULL at start.
*
- * @param[in] filename The file name of the known host file.
+ * @param[in] filename The filename of the known host file.
*
* @param[out] found_type A pointer to a string to be set with the found key
* type.
*
- * @returns The found_type type of key (ie "dsa","ssh-rsa"). Don't
+ * @returns The found_type type of key (ie "ssh-rsa"). Don't
* free that value. NULL if no match was found or the file
* was not found.
*/
@@ -152,7 +152,7 @@ static int check_public_key(ssh_session session, char **tokens) {
char *pubkey_64;
int rc;
- /* ssh-dss or ssh-rsa */
+ /* ssh-rsa, ssh-ed25519, .. */
pubkey_64 = tokens[2];
pubkey_buffer = base64_to_bin(pubkey_64);
@@ -210,8 +210,8 @@ static int match_hashed_host(const char *host, const char *sourcehash)
HMACCTX mac;
char *source;
char *b64hash;
- int match;
- unsigned int size;
+ int match, rc;
+ size_t size;
if (strncmp(sourcehash, "|1|", 3) != 0) {
return 0;
@@ -256,8 +256,20 @@ static int match_hashed_host(const char *host, const char *sourcehash)
return 0;
}
size = sizeof(buffer);
- hmac_update(mac, host, strlen(host));
- hmac_final(mac, buffer, &size);
+ rc = hmac_update(mac, host, strlen(host));
+ if (rc != 1) {
+ ssh_buffer_free(salt);
+ ssh_buffer_free(hash);
+
+ return 0;
+ }
+ rc = hmac_final(mac, buffer, &size);
+ if (rc != 1) {
+ ssh_buffer_free(salt);
+ ssh_buffer_free(hash);
+
+ return 0;
+ }
if (size == ssh_buffer_get_len(hash) &&
memcmp(buffer, ssh_buffer_get(hash), size) == 0) {
@@ -516,10 +528,12 @@ int ssh_write_knownhost(ssh_session session)
errno = 0;
file = fopen(session->opts.knownhosts, "a");
if (file == NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (errno == ENOENT) {
dir = ssh_dirname(session->opts.knownhosts);
if (dir == NULL) {
- ssh_set_error(session, SSH_FATAL, "%s", strerror(errno));
+ ssh_set_error(session, SSH_FATAL,
+ "%s", ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -527,7 +541,7 @@ int ssh_write_knownhost(ssh_session session)
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Cannot create %s directory: %s",
- dir, strerror(errno));
+ dir, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(dir);
return SSH_ERROR;
}
@@ -539,13 +553,15 @@ int ssh_write_knownhost(ssh_session session)
ssh_set_error(session, SSH_FATAL,
"Couldn't open known_hosts file %s"
" for appending: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
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));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
}
diff --git a/src/knownhosts.c b/src/knownhosts.c
index ad36363e..c073b266 100644
--- a/src/knownhosts.c
+++ b/src/knownhosts.c
@@ -58,8 +58,9 @@ static int hash_hostname(const char *name,
unsigned char *salt,
unsigned int salt_size,
unsigned char **hash,
- unsigned int *hash_size)
+ size_t *hash_size)
{
+ int rc;
HMACCTX mac_ctx;
mac_ctx = hmac_init(salt, salt_size, SSH_HMAC_SHA1);
@@ -67,8 +68,13 @@ static int hash_hostname(const char *name,
return SSH_ERROR;
}
- hmac_update(mac_ctx, name, strlen(name));
- hmac_final(mac_ctx, *hash, hash_size);
+ rc = hmac_update(mac_ctx, name, strlen(name));
+ if (rc != 1)
+ return SSH_ERROR;
+
+ rc = hmac_final(mac_ctx, *hash, hash_size);
+ if (rc != 1)
+ return SSH_ERROR;
return SSH_OK;
}
@@ -81,7 +87,7 @@ static int match_hashed_hostname(const char *host, const char *hashed_host)
ssh_buffer hash = NULL;
unsigned char hashed_buf[256] = {0};
unsigned char *hashed_buf_ptr = hashed_buf;
- unsigned int hashed_buf_size = sizeof(hashed_buf);
+ size_t hashed_buf_size = sizeof(hashed_buf);
int cmp;
int rc;
int match = 0;
@@ -228,8 +234,9 @@ static int ssh_known_hosts_read_entries(const char *match,
fp = fopen(filename, "r");
if (fp == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Failed to open the known_hosts file '%s': %s",
- filename, strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
/* The missing file is not an error here */
return SSH_OK;
}
@@ -473,24 +480,33 @@ static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e typ
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_SK_ED25519:
+ return "sk-ssh-ed25519@openssh.com";
+#ifdef HAVE_ECC
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";
+ case SSH_KEYTYPE_SK_ECDSA:
+ return "sk-ecdsa-sha2-nistp256@openssh.com";
+#else
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P521:
+ SSH_LOG(SSH_LOG_WARN, "ECDSA keys are not supported by this build");
+ break;
#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;
+ SSH_LOG(SSH_LOG_TRACE,
+ "The given type %d is not a base private key type "
+ "or is unsupported",
+ type);
}
+
+ return NULL;
}
/**
@@ -572,6 +588,8 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
algo = ssh_known_host_sigs_from_hostkey_type(entry->publickey->type);
if (algo == NULL) {
+ ssh_knownhosts_entry_free(entry);
+ ssh_list_remove(entry_list, it);
continue;
}
@@ -600,7 +618,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
/**
* @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
+ * This parses a known_hosts entry into a structure with the key in a libssh
* consumeable form. You can use the PKI key function to further work with it.
*
* @param[in] hostname The hostname to match the line to
@@ -608,7 +626,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
* @param[in] line The line to compare and parse if we have a hostname
* match.
*
- * @param[in] entry A pointer to store the the allocated known_hosts
+ * @param[in] entry A pointer to store the allocated known_hosts
* entry structure. The user needs to free the memory
* using SSH_KNOWNHOSTS_ENTRY_FREE().
*
@@ -621,6 +639,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
struct ssh_knownhosts_entry *e = NULL;
char *known_host = NULL;
char *p;
+ char *save_tok = NULL;
enum ssh_keytypes_e key_type;
int match = 0;
int rc = SSH_OK;
@@ -631,7 +650,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
}
/* match pattern for hostname or hashed hostname */
- p = strtok(known_host, " ");
+ p = strtok_r(known_host, " ", &save_tok);
if (p == NULL ) {
free(known_host);
return SSH_ERROR;
@@ -652,14 +671,16 @@ int ssh_known_hosts_parse_line(const char *hostname,
match = match_hashed_hostname(hostname, p);
}
- for (q = strtok(p, ",");
+ save_tok = NULL;
+
+ for (q = strtok_r(p, ",", &save_tok);
q != NULL;
- q = strtok(NULL, ",")) {
+ q = strtok_r(NULL, ",", &save_tok)) {
int cmp;
if (q[0] == '[' && hostname[0] != '[') {
/* Corner case: We have standard port so we do not have
- * hostname in square braces. But the patern is enclosed
+ * hostname in square braces. But the pattern is enclosed
* in braces with, possibly standard or wildcard, port.
* We need to test against [host]:port pair here.
*/
@@ -702,7 +723,9 @@ int ssh_known_hosts_parse_line(const char *hostname,
goto out;
}
- p = strtok(known_host, " ");
+ save_tok = NULL;
+
+ p = strtok_r(known_host, " ", &save_tok);
if (p == NULL ) {
rc = SSH_ERROR;
goto out;
@@ -715,7 +738,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
}
/* pubkey type */
- p = strtok(NULL, " ");
+ p = strtok_r(NULL, " ", &save_tok);
if (p == NULL) {
rc = SSH_ERROR;
goto out;
@@ -723,13 +746,13 @@ int ssh_known_hosts_parse_line(const char *hostname,
key_type = ssh_key_type_from_name(p);
if (key_type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "key type '%s' unknown!", p);
+ SSH_LOG(SSH_LOG_TRACE, "key type '%s' unknown!", p);
rc = SSH_ERROR;
goto out;
}
/* public key */
- p = strtok(NULL, " ");
+ p = strtok_r(NULL, " ", &save_tok);
if (p == NULL) {
rc = SSH_ERROR;
goto out;
@@ -739,7 +762,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
key_type,
&e->publickey);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to parse %s key for entry: %s!",
ssh_key_type_to_char(key_type),
e->unparsed);
@@ -747,7 +770,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
}
/* comment */
- p = strtok(NULL, " ");
+ p = strtok_r(NULL, " ", &save_tok);
if (p != NULL) {
p = strstr(line, p);
if (p != NULL) {
@@ -770,12 +793,12 @@ out:
}
/**
- * @brief Check if the set hostname and port matches an entry in known_hosts.
+ * @brief Check if the set hostname and port match an entry in known_hosts.
*
- * This check if the set hostname and port has an entry in the known_hosts file.
+ * This check if the set hostname and port have an entry in the known_hosts file.
* You need to set at least the hostname using ssh_options_set().
*
- * @param[in] session The session with with the values set to check.
+ * @param[in] session The session with the values set to check.
*
* @return A ssh_known_hosts_e return value.
*/
@@ -810,7 +833,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
if (session->opts.knownhosts != NULL) {
known_hosts_found = ssh_file_readaccess_ok(session->opts.knownhosts);
if (!known_hosts_found) {
- SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
+ SSH_LOG(SSH_LOG_TRACE, "Cannot access file %s",
session->opts.knownhosts);
}
}
@@ -819,7 +842,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
global_known_hosts_found =
ssh_file_readaccess_ok(session->opts.global_knownhosts);
if (!global_known_hosts_found) {
- SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
+ SSH_LOG(SSH_LOG_TRACE, "Cannot access file %s",
session->opts.global_knownhosts);
}
}
@@ -888,11 +911,11 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
*
* @param[in] session The session with information to export.
*
- * @param[in] pentry_string A pointer to a string to store the alloocated
+ * @param[in] pentry_string A pointer to a string to store the allocated
* line of the entry. The user must free it using
* ssh_string_free_char().
*
- * @return SSH_OK on succcess, SSH_ERROR otherwise.
+ * @return SSH_OK on success, SSH_ERROR otherwise.
*/
int ssh_session_export_known_hosts_entry(ssh_session session,
char **pentry_string)
@@ -957,7 +980,7 @@ int ssh_session_export_known_hosts_entry(ssh_session session,
}
/**
- * @brief Add the current connected server to the user known_hosts file.
+ * @brief Adds the currently connected server to the user known_hosts file.
*
* This adds the currently connected server to the known_hosts file by
* appending a new line at the end. The global known_hosts file is considered
@@ -975,6 +998,7 @@ int ssh_session_update_known_hosts(ssh_session session)
size_t nwritten;
size_t len;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (session->opts.knownhosts == NULL) {
rc = ssh_options_apply(session);
@@ -990,7 +1014,8 @@ int ssh_session_update_known_hosts(ssh_session session)
if (errno == ENOENT) {
dir = ssh_dirname(session->opts.knownhosts);
if (dir == NULL) {
- ssh_set_error(session, SSH_FATAL, "%s", strerror(errno));
+ ssh_set_error(session, SSH_FATAL, "%s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -998,7 +1023,8 @@ int ssh_session_update_known_hosts(ssh_session session)
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Cannot create %s directory: %s",
- dir, strerror(errno));
+ dir,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(dir);
return SSH_ERROR;
}
@@ -1010,7 +1036,8 @@ int ssh_session_update_known_hosts(ssh_session session)
ssh_set_error(session, SSH_FATAL,
"Couldn't open known_hosts file %s"
" for appending: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
} else {
@@ -1033,7 +1060,8 @@ int ssh_session_update_known_hosts(ssh_session session)
if (nwritten != len || ferror(fp)) {
ssh_set_error(session, SSH_FATAL,
"Couldn't append to known_hosts file %s: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
fclose(fp);
return SSH_ERROR;
}
@@ -1108,7 +1136,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
}
/**
- * @brief Get the known_hosts entry for the current connected session.
+ * @brief Get the known_hosts entry for the currently connected session.
*
* @param[in] session The session to validate.
*
@@ -1127,7 +1155,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
* SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
* host is thus unknown. File will be
* created if host key is accepted.\n
- * SSH_KNOWN_HOSTS_ERROR: There had been an eror checking the host.
+ * SSH_KNOWN_HOSTS_ERROR: There had been an error checking the host.
*
* @see ssh_knownhosts_entry_free()
*/
@@ -1196,7 +1224,7 @@ ssh_session_get_known_hosts_entry(ssh_session session,
* SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
* host is thus unknown. File will be
* created if host key is accepted.\n
- * SSH_KNOWN_HOSTS_ERROR: There had been an eror checking the host.
+ * SSH_KNOWN_HOSTS_ERROR: There had been an error checking the host.
*
* @see ssh_knownhosts_entry_free()
*/
diff --git a/src/legacy.c b/src/legacy.c
index 8ae3d920..72ff7f36 100644
--- a/src/legacy.c
+++ b/src/legacy.c
@@ -20,7 +20,7 @@
*/
/** functions in that file are wrappers to the newly named functions. All
- * of them are depreciated, but these wrapper will avoid breaking backward
+ * of them are depreciated, but these wrappers will avoid breaking backward
* compatibility
*/
@@ -83,12 +83,18 @@ int ssh_userauth_pubkey(ssh_session session,
key->type = privatekey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PRIVATE|SSH_KEY_FLAG_PUBLIC;
- key->dsa = privatekey->dsa_priv;
+#ifndef HAVE_LIBCRYPTO
key->rsa = privatekey->rsa_priv;
+#else
+ key->key = privatekey->key_priv;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_userauth_publickey(session, username, key);
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return rc;
@@ -164,7 +170,7 @@ int channel_change_pty_size(ssh_channel channel,int cols,int rows){
}
ssh_channel channel_forward_accept(ssh_session session, int timeout_ms){
- return ssh_channel_accept_forward(session, timeout_ms, NULL);
+ return ssh_channel_open_forward_port(session, timeout_ms, NULL, NULL, NULL);
}
int channel_close(ssh_channel channel){
@@ -350,22 +356,15 @@ void publickey_free(ssh_public_key key) {
}
switch(key->type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_LIBGCRYPT
- gcry_sexp_release(key->dsa_pub);
-#elif defined HAVE_LIBCRYPTO
- DSA_free(key->dsa_pub);
-#endif
- break;
case SSH_KEYTYPE_RSA:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(key->rsa_pub);
#elif defined HAVE_LIBCRYPTO
- RSA_free(key->rsa_pub);
+ EVP_PKEY_free(key->key_pub);
#elif defined HAVE_LIBMBEDCRYPTO
mbedtls_pk_free(key->rsa_pub);
SAFE_FREE(key->rsa_pub);
-#endif
+#endif /* HAVE_LIBGCRYPT */
break;
default:
break;
@@ -387,12 +386,18 @@ ssh_public_key publickey_from_privatekey(ssh_private_key prv) {
privkey->type = prv->type;
privkey->type_c = ssh_key_type_to_char(privkey->type);
privkey->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- privkey->dsa = prv->dsa_priv;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa = prv->rsa_priv;
+#else
+ privkey->key = prv->key_priv;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
- privkey->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa = NULL;
+#else
+ privkey->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(privkey);
if (rc < 0) {
return NULL;
@@ -438,11 +443,15 @@ ssh_private_key privatekey_from_file(ssh_session session,
}
privkey->type = key->type;
- privkey->dsa_priv = key->dsa;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa_priv = key->rsa;
- key->dsa = NULL;
key->rsa = NULL;
+#else
+ privkey->key_priv = key->key;
+
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
@@ -461,15 +470,13 @@ void privatekey_free(ssh_private_key prv) {
}
#ifdef HAVE_LIBGCRYPT
- gcry_sexp_release(prv->dsa_priv);
gcry_sexp_release(prv->rsa_priv);
#elif defined HAVE_LIBCRYPTO
- DSA_free(prv->dsa_priv);
- RSA_free(prv->rsa_priv);
+ EVP_PKEY_free(prv->key_priv);
#elif defined HAVE_LIBMBEDCRYPTO
mbedtls_pk_free(prv->rsa_priv);
SAFE_FREE(prv->rsa_priv);
-#endif
+#endif /* HAVE_LIBGCRYPT */
memset(prv, 0, sizeof(struct ssh_private_key_struct));
SAFE_FREE(prv);
}
@@ -530,10 +537,13 @@ ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s) {
pubkey->type = key->type;
pubkey->type_c = key->type_c;
- pubkey->dsa_pub = key->dsa;
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
pubkey->rsa_pub = key->rsa;
key->rsa = NULL;
+#else
+ pubkey->key_pub = key->key;
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
@@ -557,16 +567,22 @@ ssh_string publickey_to_string(ssh_public_key pubkey) {
key->type = pubkey->type;
key->type_c = pubkey->type_c;
- key->dsa = pubkey->dsa_pub;
+#ifndef HAVE_LIBCRYPTO
key->rsa = pubkey->rsa_pub;
+#else
+ key->key = pubkey->key_pub;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_pki_export_pubkey_blob(key, &key_blob);
if (rc < 0) {
key_blob = NULL;
}
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return key_blob;
@@ -622,8 +638,12 @@ int ssh_publickey_to_file(ssh_session session,
fp = fopen(file, "w+");
if (fp == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Error opening %s: %s", file, strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Error opening %s: %s",
+ file,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -735,7 +755,7 @@ int ssh_accept(ssh_session session) {
}
int channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
- return ssh_channel_write(channel, data, len);
+ return ssh_channel_write_stderr(channel, data, len);
}
/** @deprecated
diff --git a/src/libcrypto-compat.c b/src/libcrypto-compat.c
deleted file mode 100644
index 33b8dffd..00000000
--- a/src/libcrypto-compat.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the OpenSSL license (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-#include "config.h"
-
-#include <string.h>
-#include "libcrypto-compat.h"
-
-int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
-{
- /* If the fields n and e in r are NULL, the corresponding input
- * parameters MUST be non-NULL for n and e. d may be
- * left NULL (in case only the public key is used).
- */
- if ((r->n == NULL && n == NULL)
- || (r->e == NULL && e == NULL))
- return 0;
-
- if (n != NULL) {
- BN_free(r->n);
- r->n = n;
- }
- if (e != NULL) {
- BN_free(r->e);
- r->e = e;
- }
- if (d != NULL) {
- BN_free(r->d);
- r->d = d;
- }
-
- return 1;
-}
-
-int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
-{
- /* If the fields p and q in r are NULL, the corresponding input
- * parameters MUST be non-NULL.
- */
- if ((r->p == NULL && p == NULL)
- || (r->q == NULL && q == NULL))
- return 0;
-
- if (p != NULL) {
- BN_free(r->p);
- r->p = p;
- }
- if (q != NULL) {
- BN_free(r->q);
- r->q = q;
- }
-
- return 1;
-}
-
-int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
-{
- /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
- * parameters MUST be non-NULL.
- */
- if ((r->dmp1 == NULL && dmp1 == NULL)
- || (r->dmq1 == NULL && dmq1 == NULL)
- || (r->iqmp == NULL && iqmp == NULL))
- return 0;
-
- if (dmp1 != NULL) {
- BN_free(r->dmp1);
- r->dmp1 = dmp1;
- }
- if (dmq1 != NULL) {
- BN_free(r->dmq1);
- r->dmq1 = dmq1;
- }
- if (iqmp != NULL) {
- BN_free(r->iqmp);
- r->iqmp = iqmp;
- }
-
- return 1;
-}
-
-void RSA_get0_key(const RSA *r,
- const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
-{
- if (n != NULL)
- *n = r->n;
- if (e != NULL)
- *e = r->e;
- if (d != NULL)
- *d = r->d;
-}
-
-void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
-{
- if (p != NULL)
- *p = r->p;
- if (q != NULL)
- *q = r->q;
-}
-
-void RSA_get0_crt_params(const RSA *r,
- const BIGNUM **dmp1, const BIGNUM **dmq1,
- const BIGNUM **iqmp)
-{
- if (dmp1 != NULL)
- *dmp1 = r->dmp1;
- if (dmq1 != NULL)
- *dmq1 = r->dmq1;
- if (iqmp != NULL)
- *iqmp = r->iqmp;
-}
-
-void DSA_get0_pqg(const DSA *d,
- const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
-{
- if (p != NULL)
- *p = d->p;
- if (q != NULL)
- *q = d->q;
- if (g != NULL)
- *g = d->g;
-}
-
-int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
-{
- /* If the fields p, q and g in d are NULL, the corresponding input
- * parameters MUST be non-NULL.
- */
- if ((d->p == NULL && p == NULL)
- || (d->q == NULL && q == NULL)
- || (d->g == NULL && g == NULL))
- return 0;
-
- if (p != NULL) {
- BN_free(d->p);
- d->p = p;
- }
- if (q != NULL) {
- BN_free(d->q);
- d->q = q;
- }
- if (g != NULL) {
- BN_free(d->g);
- d->g = g;
- }
-
- return 1;
-}
-
-void DSA_get0_key(const DSA *d,
- const BIGNUM **pub_key, const BIGNUM **priv_key)
-{
- if (pub_key != NULL)
- *pub_key = d->pub_key;
- if (priv_key != NULL)
- *priv_key = d->priv_key;
-}
-
-int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
-{
- /* If the field pub_key in d is NULL, the corresponding input
- * parameters MUST be non-NULL. The priv_key field may
- * be left NULL.
- */
- if (d->pub_key == NULL && pub_key == NULL)
- return 0;
-
- if (pub_key != NULL) {
- BN_free(d->pub_key);
- d->pub_key = pub_key;
- }
- if (priv_key != NULL) {
- BN_free(d->priv_key);
- d->priv_key = priv_key;
- }
-
- return 1;
-}
-
-void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
-{
- if (pr != NULL)
- *pr = sig->r;
- if (ps != NULL)
- *ps = sig->s;
-}
-
-int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- if (r == NULL || s == NULL)
- return 0;
- BN_clear_free(sig->r);
- BN_clear_free(sig->s);
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
-{
- if (pr != NULL)
- *pr = sig->r;
- if (ps != NULL)
- *ps = sig->s;
-}
-
-int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- if (r == NULL || s == NULL)
- return 0;
- BN_clear_free(sig->r);
- BN_clear_free(sig->s);
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-EVP_MD_CTX *EVP_MD_CTX_new(void)
-{
- EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof(EVP_MD_CTX));
- if (ctx != NULL) {
- EVP_MD_CTX_init(ctx);
- }
- return ctx;
-}
-
-void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
-{
- EVP_MD_CTX_cleanup(ctx);
- OPENSSL_free(ctx);
-}
-
-void DH_get0_pqg(const DH *dh,
- const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
-{
- if (p) {
- *p = dh->p;
- }
- if (q) {
- *q = NULL;
- }
- if (g) {
- *g = dh->g;
- }
-}
-
-int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
-{
- if (p) {
- if (dh->p) {
- BN_free(dh->p);
- }
- dh->p = p;
- }
- if (g) {
- if (dh->g) {
- BN_free(dh->g);
- }
- dh->g = g;
- }
- return 1;
-}
-
-void DH_get0_key(const DH *dh,
- const BIGNUM **pub_key, const BIGNUM **priv_key)
-{
- if (pub_key) {
- *pub_key = dh->pub_key;
- }
- if (priv_key) {
- *priv_key = dh->priv_key;
- }
-}
-
-int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
-{
- if (pub_key) {
- if (dh->pub_key) {
- BN_free(dh->pub_key);
- }
- dh->pub_key = pub_key;
- }
- if (priv_key) {
- if (dh->priv_key) {
- BN_free(dh->priv_key);
- }
- dh->priv_key = priv_key;
- }
- return 1;
-}
-
-const char *OpenSSL_version(int type)
-{
- return SSLeay_version(type);
-}
-unsigned long OpenSSL_version_num(void)
-{
- return SSLeay();
-}
diff --git a/src/libcrypto-compat.h b/src/libcrypto-compat.h
index 437b0534..0f2dc184 100644
--- a/src/libcrypto-compat.h
+++ b/src/libcrypto-compat.h
@@ -2,47 +2,13 @@
#define LIBCRYPTO_COMPAT_H
#include <openssl/opensslv.h>
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#include <openssl/ecdsa.h>
-#include <openssl/dh.h>
-#include <openssl/evp.h>
-#include <openssl/hmac.h>
-#include <openssl/bn.h>
-
-int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
-int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q);
-int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp);
-void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d);
-void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q);
-void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp);
-
-void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
-int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g);
-void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key);
-int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key);
-
-void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
-int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s);
-
-void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
-int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
-
-EVP_MD_CTX *EVP_MD_CTX_new(void);
-void EVP_MD_CTX_free(EVP_MD_CTX *ctx);
-
-void DH_get0_pqg(const DH *dh,
- const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
-int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
-void DH_get0_key(const DH *dh,
- const BIGNUM **pub_key, const BIGNUM **priv_key);
-int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
-
-const char *OpenSSL_version(int type);
-unsigned long OpenSSL_version_num(void);
+#define NISTP256 "P-256"
+#define NISTP384 "P-384"
+#define NISTP521 "P-521"
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#define EVP_PKEY_eq EVP_PKEY_cmp
#endif /* OPENSSL_VERSION_NUMBER */
#endif /* LIBCRYPTO_COMPAT_H */
diff --git a/src/libcrypto.c b/src/libcrypto.c
index c14eeeea..f45ffa96 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -26,26 +26,31 @@
#include <string.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
-#endif
+#endif /* HAVE_SYS_TIME_H */
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/wrapper.h"
#include "libssh/libcrypto.h"
-#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
+#include "libssh/pki.h"
+#ifdef HAVE_OPENSSL_EVP_CHACHA20
#include "libssh/bytearray.h"
#include "libssh/chacha20-poly1305-common.h"
#endif
#ifdef HAVE_LIBCRYPTO
+#include <openssl/opensslv.h>
#include <openssl/sha.h>
#include <openssl/md5.h>
-#include <openssl/dsa.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#include <openssl/rsa.h>
#include <openssl/hmac.h>
-#include <openssl/opensslv.h>
+#else
+#include <openssl/param_build.h>
+#include <openssl/core_names.h>
+#endif /* OPENSSL_VERSION_NUMBER */
#include <openssl/rand.h>
#include <openssl/engine.h>
@@ -54,11 +59,11 @@
#ifdef HAVE_OPENSSL_AES_H
#define HAS_AES
#include <openssl/aes.h>
-#endif
+#endif /* HAVE_OPENSSL_AES_H */
#ifdef HAVE_OPENSSL_DES_H
#define HAS_DES
#include <openssl/des.h>
-#endif
+#endif /* HAVE_OPENSSL_DES_H */
#if (defined(HAVE_VALGRIND_VALGRIND_H) && defined(HAVE_OPENSSL_IA32CAP_LOC))
#include <valgrind/valgrind.h>
@@ -67,14 +72,19 @@
#include "libssh/crypto.h"
-#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID
+#ifdef HAVE_OPENSSL_EVP_KDF_CTX
#include <openssl/kdf.h>
-#endif
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/param_build.h>
+#include <openssl/core_names.h>
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* HAVE_OPENSSL_EVP_KDF_CTX */
#include "libssh/crypto.h"
static int libcrypto_initialized = 0;
+
void ssh_reseed(void){
#ifndef _WIN32
struct timeval tv;
@@ -83,265 +93,42 @@ void ssh_reseed(void){
#endif
}
-/**
- * @brief Get random bytes
- *
- * Make sure to always check the return code of this function!
- *
- * @param[in] where The buffer to fill with random bytes
- *
- * @param[in] len The size of the buffer to fill.
- *
- * @param[in] strong Use a strong or private RNG source.
- *
- * @return 1 on success, 0 on error.
- */
-int ssh_get_random(void *where, int len, int strong)
-{
-#ifdef HAVE_OPENSSL_RAND_PRIV_BYTES
- if (strong) {
- /* Returns -1 when not supported, 0 on error, 1 on success */
- return !!RAND_priv_bytes(where, len);
- }
-#else
- (void)strong;
-#endif /* HAVE_RAND_PRIV_BYTES */
-
- /* Returns -1 when not supported, 0 on error, 1 on success */
- return !!RAND_bytes(where, len);
-}
-
-SHACTX sha1_init(void)
-{
- int rc;
- SHACTX c = EVP_MD_CTX_new();
- if (c == NULL) {
- return NULL;
- }
- rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_free(c);
- c = NULL;
- }
- return c;
-}
-
-void sha1_update(SHACTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
+#ifndef WITH_PKCS11_PROVIDER
+static ENGINE *engine = NULL;
-void sha1_final(unsigned char *md, SHACTX c)
+ENGINE *pki_get_engine(void)
{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_free(c);
-}
+ int ok;
-void sha1(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHACTX c = sha1_init();
- if (c != NULL) {
- sha1_update(c, digest, len);
- sha1_final(hash, c);
- }
-}
+ if (engine == NULL) {
+ ENGINE_load_builtin_engines();
-#ifdef HAVE_OPENSSL_ECC
-static const EVP_MD *nid_to_evpmd(int nid)
-{
- switch (nid) {
- case NID_X9_62_prime256v1:
- return EVP_sha256();
- case NID_secp384r1:
- return EVP_sha384();
- case NID_secp521r1:
- return EVP_sha512();
- default:
+ engine = ENGINE_by_id("pkcs11");
+ if (engine == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Could not load the engine: %s",
+ ERR_error_string(ERR_get_error(), NULL));
return NULL;
- }
-
- return NULL;
-}
-
-void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen)
-{
- const EVP_MD *evp_md = nid_to_evpmd(nid);
- EVP_MD_CTX *md = EVP_MD_CTX_new();
-
- EVP_DigestInit(md, evp_md);
- EVP_DigestUpdate(md, digest, len);
- EVP_DigestFinal(md, hash, hlen);
- EVP_MD_CTX_free(md);
-}
-
-EVPCTX evp_init(int nid)
-{
- const EVP_MD *evp_md = nid_to_evpmd(nid);
-
- EVPCTX ctx = EVP_MD_CTX_new();
- if (ctx == NULL) {
- return NULL;
- }
-
- EVP_DigestInit(ctx, evp_md);
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- EVP_DigestFinal(ctx, md, mdlen);
- EVP_MD_CTX_free(ctx);
-}
-#endif
-
-SHA256CTX sha256_init(void)
-{
- int rc;
- SHA256CTX c = EVP_MD_CTX_new();
- if (c == NULL) {
- return NULL;
- }
- rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_free(c);
- c = NULL;
- }
- return c;
-}
-
-void sha256_update(SHA256CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha256_final(unsigned char *md, SHA256CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_free(c);
-}
-
-void sha256(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHA256CTX c = sha256_init();
- if (c != NULL) {
- sha256_update(c, digest, len);
- sha256_final(hash, c);
- }
-}
-
-SHA384CTX sha384_init(void)
-{
- int rc;
- SHA384CTX c = EVP_MD_CTX_new();
- if (c == NULL) {
- return NULL;
- }
- rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_free(c);
- c = NULL;
- }
- return c;
-}
-
-void sha384_update(SHA384CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha384_final(unsigned char *md, SHA384CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_free(c);
-}
-
-void sha384(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHA384CTX c = sha384_init();
- if (c != NULL) {
- sha384_update(c, digest, len);
- sha384_final(hash, c);
- }
-}
-
-SHA512CTX sha512_init(void)
-{
- int rc = 0;
- SHA512CTX c = EVP_MD_CTX_new();
- if (c == NULL) {
- return NULL;
- }
- rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_free(c);
- c = NULL;
- }
- return c;
-}
-
-void sha512_update(SHA512CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha512_final(unsigned char *md, SHA512CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_free(c);
-}
-
-void sha512(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHA512CTX c = sha512_init();
- if (c != NULL) {
- sha512_update(c, digest, len);
- sha512_final(hash, c);
- }
-}
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Engine loaded successfully");
+
+ ok = ENGINE_init(engine);
+ if (!ok) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Could not initialize the engine: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ ENGINE_free(engine);
+ return NULL;
+ }
-MD5CTX md5_init(void)
-{
- int rc;
- MD5CTX c = EVP_MD_CTX_new();
- if (c == NULL) {
- return NULL;
- }
- rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
- if(rc == 0) {
- EVP_MD_CTX_free(c);
- c = NULL;
+ SSH_LOG(SSH_LOG_DEBUG, "Engine init success");
}
- return c;
-}
-
-void md5_update(MD5CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
+ return engine;
}
+#endif /* WITH_PKCS11_PROVIDER */
-void md5_final(unsigned char *md, MD5CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_free(c);
-}
-
-#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID
+#ifdef HAVE_OPENSSL_EVP_KDF_CTX
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
{
switch (digest_type) {
@@ -356,19 +143,50 @@ static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
}
return NULL;
}
+#else
+static const char *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
+{
+ switch (digest_type) {
+ case SSH_KDF_SHA1:
+ return SN_sha1;
+ case SSH_KDF_SHA256:
+ return SN_sha256;
+ case SSH_KDF_SHA384:
+ return SN_sha384;
+ case SSH_KDF_SHA512:
+ return SN_sha512;
+ }
+ return NULL;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
+ int rc = -1;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EVP_KDF_CTX *ctx = EVP_KDF_CTX_new_id(EVP_KDF_SSHKDF);
- int rc;
+#else
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSHKDF", NULL);
+ EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ OSSL_PARAM *params = NULL;
+ const char *md = sshkdf_digest_to_md(crypto->digest_type);
+
+ EVP_KDF_free(kdf);
+ if (param_bld == NULL) {
+ EVP_KDF_CTX_free(ctx);
+ return -1;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
if (ctx == NULL) {
- return -1;
+ goto out;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD,
sshkdf_digest_to_md(crypto->digest_type));
if (rc != 1) {
@@ -388,7 +206,7 @@ int ssh_kdf(struct ssh_crypto_struct *crypto,
goto out;
}
rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID,
- crypto->session_id, crypto->digest_len);
+ crypto->session_id, crypto->session_id_len);
if (rc != 1) {
goto out;
}
@@ -396,8 +214,60 @@ int ssh_kdf(struct ssh_crypto_struct *crypto,
if (rc != 1) {
goto out;
}
+#else
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_KDF_PARAM_DIGEST,
+ md, strlen(md));
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_KDF_PARAM_KEY,
+ key, key_len);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld,
+ OSSL_KDF_PARAM_SSHKDF_XCGHASH,
+ crypto->secret_hash,
+ crypto->digest_len);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld,
+ OSSL_KDF_PARAM_SSHKDF_SESSION_ID,
+ crypto->session_id,
+ crypto->session_id_len);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_KDF_PARAM_SSHKDF_TYPE,
+ (const char*)&key_type, 1);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ rc = -1;
+ goto out;
+ }
+
+ rc = EVP_KDF_derive(ctx, output, requested_len, params);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
out:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD_free(param_bld);
+ OSSL_PARAM_free(params);
+#endif
EVP_KDF_CTX_free(ctx);
if (rc < 0) {
return rc;
@@ -408,15 +278,15 @@ out:
#else
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
return sshkdf_derive_key(crypto, key, key_len,
key_type, output, requested_len);
}
-#endif
+#endif /* HAVE_OPENSSL_EVP_KDF_CTX */
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type)
+HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type)
{
HMACCTX ctx = NULL;
EVP_PKEY *pkey = NULL;
@@ -461,17 +331,22 @@ error:
return NULL;
}
-void hmac_update(HMACCTX ctx, const void *data, unsigned long len)
+int hmac_update(HMACCTX ctx, const void *data, size_t len)
{
- EVP_DigestSignUpdate(ctx, data, len);
+ return EVP_DigestSignUpdate(ctx, data, len);
}
-void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len)
+int hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, size_t *len)
{
- size_t res;
- EVP_DigestSignFinal(ctx, hashmacbuf, &res);
+ size_t res = *len;
+ int rc;
+ rc = EVP_DigestSignFinal(ctx, hashmacbuf, &res);
EVP_MD_CTX_free(ctx);
- *len = res;
+ if (rc == 1) {
+ *len = res;
+ }
+
+ return rc;
}
static void evp_cipher_init(struct ssh_cipher_struct *cipher)
@@ -515,12 +390,12 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher)
cipher->cipher = EVP_bf_cbc();
break;
/* ciphers not using EVP */
-#endif
+#endif /* WITH_BLOWFISH_CIPHER */
case SSH_AEAD_CHACHA20_POLY1305:
- SSH_LOG(SSH_LOG_WARNING, "The ChaCha cipher cannot be handled here");
+ SSH_LOG(SSH_LOG_TRACE, "The ChaCha cipher cannot be handled here");
break;
case SSH_NO_CIPHER:
- SSH_LOG(SSH_LOG_WARNING, "No valid ciphertype found");
+ SSH_LOG(SSH_LOG_TRACE, "No valid ciphertype found");
break;
}
}
@@ -534,7 +409,7 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
rc = EVP_EncryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptInit_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptInit_ex failed");
return SSH_ERROR;
}
@@ -546,7 +421,7 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
-1,
(uint8_t *)IV);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_IV_FIXED failed");
return SSH_ERROR;
}
}
@@ -564,7 +439,7 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
rc = EVP_DecryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptInit_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptInit_ex failed");
return SSH_ERROR;
}
@@ -576,7 +451,7 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
-1,
(uint8_t *)IV);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_IV_FIXED failed");
return SSH_ERROR;
}
}
@@ -601,11 +476,11 @@ static void evp_cipher_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)in,
(int)len);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
return;
}
if (outlen != (int)len){
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG,
"EVP_EncryptUpdate: output size %d for %zu in",
outlen,
len);
@@ -627,11 +502,11 @@ static void evp_cipher_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)in,
(int)len);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed");
return;
}
if (outlen != (int)len){
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG,
"EVP_DecryptUpdate: output size %d for %zu in",
outlen,
len);
@@ -686,7 +561,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
1,
lastiv);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed");
return;
}
@@ -698,7 +573,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
(int)aadlen);
outlen = tmplen;
if (rc == 0 || outlen != aadlen) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to pass authenticated data");
return;
}
memcpy(out, in, aadlen);
@@ -711,7 +586,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
(int)len - aadlen);
outlen = tmplen;
if (rc != 1 || outlen != (int)len - aadlen) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
return;
}
@@ -720,7 +595,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
NULL,
&tmplen);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptFinal failed: Failed to create a tag");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal failed: Failed to create a tag");
return;
}
@@ -729,7 +604,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
authlen,
(unsigned char *)tag);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_GET_TAG failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_GET_TAG failed");
return;
}
}
@@ -757,7 +632,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
1,
lastiv);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed");
return SSH_ERROR;
}
@@ -767,7 +642,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
authlen,
(unsigned char *)complete_packet + aadlen + encrypted_size);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_TAG failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_TAG failed");
return SSH_ERROR;
}
@@ -778,7 +653,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)complete_packet,
(int)aadlen);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to pass authenticated data");
return SSH_ERROR;
}
/* Do not copy the length to the target buffer, because it is already processed */
@@ -789,14 +664,14 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)out,
&outlen,
(unsigned char *)complete_packet + aadlen,
- encrypted_size /* already substracted aadlen*/);
+ encrypted_size /* already subtracted aadlen */);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed");
return SSH_ERROR;
}
if (outlen != (int)encrypted_size) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"EVP_DecryptUpdate: output size %d for %zd in",
outlen,
encrypted_size);
@@ -808,26 +683,31 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
NULL,
&outlen);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptFinal failed: Failed authentication");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptFinal failed: Failed authentication");
return SSH_ERROR;
}
return SSH_OK;
}
-#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
+#ifdef HAVE_OPENSSL_EVP_CHACHA20
struct chacha20_poly1305_keysched {
/* cipher handle used for encrypting the packets */
EVP_CIPHER_CTX *main_evp;
/* cipher handle used for encrypting the length field */
EVP_CIPHER_CTX *header_evp;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* mac handle used for authenticating the packets */
EVP_PKEY_CTX *pctx;
/* Poly1305 key */
EVP_PKEY *key;
/* MD context for digesting data in poly1305 */
EVP_MD_CTX *mctx;
+#else
+ /* MAC context used to do poly1305 */
+ EVP_MAC_CTX *mctx;
+#endif /* OPENSSL_VERSION_NUMBER */
};
static void
@@ -845,11 +725,16 @@ chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher)
ctx->main_evp = NULL;
EVP_CIPHER_CTX_free(ctx->header_evp);
ctx->header_evp = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* ctx->pctx is freed as part of MD context */
EVP_PKEY_free(ctx->key);
ctx->key = NULL;
EVP_MD_CTX_free(ctx->mctx);
ctx->mctx = NULL;
+#else
+ EVP_MAC_CTX_free(ctx->mctx);
+ ctx->mctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
SAFE_FREE(cipher->chacha20_schedule);
}
@@ -862,6 +747,9 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
struct chacha20_poly1305_keysched *ctx = NULL;
uint8_t *u8key = key;
int ret = SSH_ERROR, rv;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *mac = NULL;
+#endif
if (cipher->chacha20_schedule == NULL) {
ctx = calloc(1, sizeof(*ctx));
@@ -877,38 +765,54 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
/* K2 uses the first half of the key */
ctx->main_evp = EVP_CIPHER_CTX_new();
if (ctx->main_evp == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CIPHER_CTX_new failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CIPHER_CTX_new failed");
goto out;
}
rv = EVP_EncryptInit_ex(ctx->main_evp, EVP_chacha20(), NULL, u8key, NULL);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit failed");
goto out;
}
/* K1 uses the second half of the key */
ctx->header_evp = EVP_CIPHER_CTX_new();
if (ctx->header_evp == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CIPHER_CTX_new failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CIPHER_CTX_new failed");
goto out;
}
ret = EVP_EncryptInit_ex(ctx->header_evp, EVP_chacha20(), NULL,
u8key + CHACHA20_KEYLEN, NULL);
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit failed");
goto out;
}
/* The Poly1305 key initialization is delayed to the time we know
* the actual key for packet so we do not need to create a bogus keys
*/
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
ctx->mctx = EVP_MD_CTX_new();
if (ctx->mctx == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_MD_CTX_new failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MD_CTX_new failed");
return SSH_ERROR;
}
+#else
+ mac = EVP_MAC_fetch(NULL, "poly1305", NULL);
+ if (mac == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_fetch failed");
+ goto out;
+ }
+ ctx->mctx = EVP_MAC_CTX_new(mac);
+ if (ctx->mctx == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_CTX_new failed");
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
ret = SSH_OK;
out:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_free(mac);
+#endif
if (ret != SSH_OK) {
chacha20_poly1305_cleanup(cipher);
}
@@ -937,13 +841,13 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
ret = EVP_CipherInit_ex(ctx->header_evp, NULL, NULL, NULL, seqbuf, do_encrypt);
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit_ex(header_evp) failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit_ex(header_evp) failed");
return SSH_ERROR;
}
ret = EVP_CipherInit_ex(ctx->main_evp, NULL, NULL, NULL, seqbuf, do_encrypt);
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit_ex(main_evp) failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit_ex(main_evp) failed");
return SSH_ERROR;
}
@@ -972,7 +876,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
rv = EVP_CipherUpdate(ctx->main_evp, poly_key, &len,
(unsigned char *)zero_block, sizeof(zero_block));
if (rv != 1 || len != CHACHA20_BLOCKSIZE) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
goto out;
}
#ifdef DEBUG_CRYPTO
@@ -980,17 +884,18 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Set the Poly1305 key */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (ctx->key == NULL) {
/* Poly1305 Initialization needs to know the actual key */
ctx->key = EVP_PKEY_new_mac_key(EVP_PKEY_POLY1305, NULL,
poly_key, POLY1305_KEYLEN);
if (ctx->key == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_PKEY_new_mac_key failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_new_mac_key failed");
goto out;
}
rv = EVP_DigestSignInit(ctx->mctx, &ctx->pctx, NULL, NULL, ctx->key);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignInit failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignInit failed");
goto out;
}
} else {
@@ -999,10 +904,17 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
EVP_PKEY_CTRL_SET_MAC_KEY,
POLY1305_KEYLEN, (void *)poly_key);
if (rv <= 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_PKEY_CTX_ctrl failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_CTX_ctrl failed");
goto out;
}
}
+#else
+ rv = EVP_MAC_init(ctx->mctx, poly_key, POLY1305_KEYLEN, NULL);
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_init failed");
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
ret = SSH_OK;
out:
@@ -1036,7 +948,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
rv = EVP_CipherUpdate(ctx->header_evp, out, &outlen, in, len);
if (rv != 1 || outlen != sizeof(uint32_t)) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
return SSH_ERROR;
}
@@ -1046,7 +958,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
rv = EVP_CipherFinal_ex(ctx->header_evp, out + outlen, &outlen);
if (rv != 1 || outlen != 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherFinal_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherFinal_ex failed");
return SSH_ERROR;
}
@@ -1071,7 +983,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
rv = chacha20_poly1305_packet_setup(cipher, seq, 0);
if (rv != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
goto out;
}
@@ -1080,19 +992,34 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Calculate MAC of received data */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
rv = EVP_DigestSignUpdate(ctx->mctx, complete_packet,
encrypted_size + sizeof(uint32_t));
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed");
goto out;
}
rv = EVP_DigestSignFinal(ctx->mctx, tag, &taglen);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "poly1305 verify error");
+ SSH_LOG(SSH_LOG_TRACE, "poly1305 verify error");
+ goto out;
+ }
+#else
+ rv = EVP_MAC_update(ctx->mctx, complete_packet,
+ encrypted_size + sizeof(uint32_t));
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed");
goto out;
}
+ rv = EVP_MAC_final(ctx->mctx, tag, &taglen, POLY1305_TAGLEN);
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_final failed");
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("calculated mac", tag, POLY1305_TAGLEN);
#endif /* DEBUG_CRYPTO */
@@ -1110,13 +1037,13 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)complete_packet + sizeof(uint32_t),
encrypted_size);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
goto out;
}
rv = EVP_CipherFinal_ex(ctx->main_evp, out + len, &len);
if (rv != 1 || len != 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherFinal_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherFinal_ex failed");
goto out;
}
@@ -1141,7 +1068,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
ret = chacha20_poly1305_packet_setup(cipher, seq, 1);
if (ret != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
return;
}
@@ -1156,7 +1083,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)&in_packet->length,
sizeof(uint32_t));
if (ret != 1 || outlen != sizeof(uint32_t)) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
return;
}
#ifdef DEBUG_CRYPTO
@@ -1165,7 +1092,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
ret = EVP_CipherFinal_ex(ctx->header_evp, (uint8_t *)out + outlen, &outlen);
if (ret != 1 || outlen != 0) {
- SSH_LOG(SSH_LOG_PACKET, "EVP_EncryptFinal_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal_ex failed");
return;
}
@@ -1177,23 +1104,37 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
in_packet->payload,
len - sizeof(uint32_t));
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
return;
}
/* step 4, compute the MAC */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
ret = EVP_DigestSignUpdate(ctx->mctx, out_packet, len);
if (ret <= 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed");
return;
}
ret = EVP_DigestSignFinal(ctx->mctx, tag, &taglen);
if (ret <= 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignFinal failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignFinal failed");
+ return;
+ }
+#else
+ ret = EVP_MAC_update(ctx->mctx, (void*)out_packet, len);
+ if (ret != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed");
return;
}
+
+ ret = EVP_MAC_final(ctx->mctx, tag, &taglen, POLY1305_TAGLEN);
+ if (ret != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_final failed");
+ return;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
}
-#endif /* defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) */
+#endif /* HAVE_OPENSSL_EVP_CHACHA20 */
#ifdef WITH_INSECURE_NONE
static void
@@ -1222,7 +1163,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
-#endif
+#endif /* WITH_BLOWFISH_CIPHER */
#ifdef HAS_AES
{
.name = "aes128-ctr",
@@ -1333,7 +1274,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
},
#endif /* HAS_DES */
{
-#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
+#ifdef HAVE_OPENSSL_EVP_CHACHA20
.ciphertype = SSH_AEAD_CHACHA20_POLY1305,
.name = "chacha20-poly1305@openssh.com",
.blocksize = CHACHA20_BLOCKSIZE/8,
@@ -1349,7 +1290,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.cleanup = chacha20_poly1305_cleanup
#else
.name = "chacha20-poly1305@openssh.com"
-#endif /* defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) */
+#endif /* HAVE_OPENSSL_EVP_CHACHA20 */
},
#ifdef WITH_INSECURE_NONE
{
@@ -1376,13 +1317,15 @@ struct ssh_cipher_struct *ssh_get_ciphertab(void)
*/
int ssh_crypto_init(void)
{
- UNUSED_VAR(size_t i);
+#ifndef HAVE_OPENSSL_EVP_CHACHA20
+ size_t i;
+#endif
if (libcrypto_initialized) {
return SSH_OK;
}
if (OpenSSL_version_num() != OPENSSL_VERSION_NUMBER){
- SSH_LOG(SSH_LOG_WARNING, "libssh compiled with %s "
+ SSH_LOG(SSH_LOG_DEBUG, "libssh compiled with %s "
"headers, currently running with %s.",
OPENSSL_VERSION_TEXT,
OpenSSL_version(OpenSSL_version_num())
@@ -1398,12 +1341,9 @@ int ssh_crypto_init(void)
/* Bit #57 denotes AES-NI instruction set extension */
OPENSSL_ia32cap &= ~(1LL << 57);
}
-#endif
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- OpenSSL_add_all_algorithms();
-#endif
+#endif /* CAN_DISABLE_AESNI */
-#if !defined(HAVE_OPENSSL_EVP_CHACHA20) || !defined(HAVE_OPENSSL_EVP_POLY1305)
+#ifndef HAVE_OPENSSL_EVP_CHACHA20
for (i = 0; ssh_ciphertab[i].name != NULL; i++) {
int cmp;
@@ -1415,7 +1355,7 @@ int ssh_crypto_init(void)
break;
}
}
-#endif /* !defined(HAVE_OPENSSL_EVP_CHACHA20) || !defined(HAVE_OPENSSL_EVP_POLY1305) */
+#endif /* HAVE_OPENSSL_EVP_CHACHA20 */
libcrypto_initialized = 1;
@@ -1432,13 +1372,210 @@ void ssh_crypto_finalize(void)
return;
}
- ENGINE_cleanup();
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
+/* TODO this should finalize engine if it was started, but during atexit calls,
+ * we are crashing. AFAIK this is related to the dlopened pkcs11 modules calling
+ * the crypto cleanups earlier. */
+#if 0
+ if (engine != NULL) {
+ ENGINE_finish(engine);
+ ENGINE_free(engine);
+ engine = NULL;
+ }
#endif
libcrypto_initialized = 0;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+/**
+ * @internal
+ * @brief Create EVP_PKEY from parameters
+ *
+ * @param[in] name Algorithm to use. For more info see manpage of EVP_PKEY_CTX_new_from_name
+ *
+ * @param[in] param_bld Constructed param builder for the pkey
+ *
+ * @param[out] pkey Created EVP_PKEY variable
+ *
+ * @param[in] selection Reference selections at man EVP_PKEY_FROMDATA
+ *
+ * @return 0 on success, -1 on error
+ */
+int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld,
+ EVP_PKEY **pkey, int selection)
+{
+ int rc;
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL);
+ OSSL_PARAM *params = NULL;
+
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_fromdata_init(ctx);
+ if (rc != 1) {
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_fromdata(ctx, pkey, selection, params);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Failed to import private key: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+
+ return SSH_OK;
+}
+
+/**
+ * @brief creates a copy of EVP_PKEY
+ *
+ * @param[in] name Algorithm to use. For more info see manpage of
+ * EVP_PKEY_CTX_new_from_name
+ *
+ * @param[in] key Key being duplicated from
+ *
+ * @param[in] demote Same as at pki_key_dup, only the public
+ * part of the key gets duplicated if true
+ *
+ * @param[out] new_key The key where the duplicate is saved
+ *
+ * @return 0 on success, -1 on error
+ */
+static int
+evp_dup_pkey(const char *name, const ssh_key key, int demote, ssh_key new_key)
+{
+ int rc;
+ EVP_PKEY_CTX *ctx = NULL;
+ OSSL_PARAM *params = NULL;
+
+ /* The simple case -- just reference the existing key */
+ if (!demote || (key->flags & SSH_KEY_FLAG_PRIVATE) == 0) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ return -1;
+ }
+ new_key->key = key->key;
+ return SSH_OK;
+ }
+
+ /* demote == 1 */
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL);
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, &params);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ if (strcmp(name, "EC") == 0) {
+ OSSL_PARAM *locate_param = NULL;
+ /* For ECC keys provided by engine or provider, we need to have the
+ * explicit public part available, otherwise the key will not be
+ * usable */
+ locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (locate_param == NULL) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return -1;
+ }
+ }
+ rc = EVP_PKEY_fromdata_init(ctx);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return -1;
+ }
+
+ rc = EVP_PKEY_fromdata(ctx, &(new_key->key), EVP_PKEY_PUBLIC_KEY, params);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return -1;
+ }
+
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+
+ return SSH_OK;
+}
+
+int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote)
+{
+ return evp_dup_pkey("RSA", key, demote, new_key);
+}
+
+int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote)
+{
+ return evp_dup_pkey("EC", key, demote, new_key);
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+
+ssh_string
+pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p)
+{
+ ssh_string s = NULL;
+ size_t len;
+
+ len = EC_POINT_point2oct(g,
+ p,
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL,
+ 0,
+ NULL);
+ if (len == 0) {
+ return NULL;
+ }
+
+ s = ssh_string_new(len);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = EC_POINT_point2oct(g,
+ p,
+ POINT_CONVERSION_UNCOMPRESSED,
+ ssh_string_data(s),
+ ssh_string_len(s),
+ NULL);
+ if (len != ssh_string_len(s)) {
+ SSH_STRING_FREE(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+int pki_key_ecgroup_name_to_nid(const char *group)
+{
+ if (strcmp(group, NISTP256) == 0 ||
+ strcmp(group, "secp256r1") == 0 ||
+ strcmp(group, "prime256v1") == 0) {
+ return NID_X9_62_prime256v1;
+ } else if (strcmp(group, NISTP384) == 0 ||
+ strcmp(group, "secp384r1") == 0) {
+ return NID_secp384r1;
+ } else if (strcmp(group, NISTP521) == 0 ||
+ strcmp(group, "secp521r1") == 0) {
+ return NID_secp521r1;
+ }
+ return -1;
+}
#endif /* LIBCRYPTO */
diff --git a/src/libgcrypt.c b/src/libgcrypt.c
index 2383ffa0..4feda00c 100644
--- a/src/libgcrypt.c
+++ b/src/libgcrypt.c
@@ -69,181 +69,16 @@ static int alloc_key(struct ssh_cipher_struct *cipher) {
void ssh_reseed(void){
}
-int ssh_get_random(void *where, int len, int strong)
-{
- /* variable not used in gcrypt */
- (void) strong;
-
- /* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */
- gcry_randomize(where,len,GCRY_STRONG_RANDOM);
-
- return 1;
-}
-
-SHACTX sha1_init(void) {
- SHACTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA1, 0);
-
- return ctx;
-}
-
-void sha1_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha1(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
-}
-
-#ifdef HAVE_GCRYPT_ECC
-static int nid_to_md_algo(int nid)
-{
- switch (nid) {
- case NID_gcrypt_nistp256:
- return GCRY_MD_SHA256;
- case NID_gcrypt_nistp384:
- return GCRY_MD_SHA384;
- case NID_gcrypt_nistp521:
- return GCRY_MD_SHA512;
- }
- return GCRY_MD_NONE;
-}
-
-void evp(int nid, unsigned char *digest, int len,
- unsigned char *hash, unsigned int *hlen)
-{
- int algo = nid_to_md_algo(nid);
-
- /* Note: What gcrypt calls 'hash' is called 'digest' here and
- vice-versa. */
- gcry_md_hash_buffer(algo, hash, digest, len);
- *hlen = gcry_md_get_algo_dlen(algo);
-}
-
-EVPCTX evp_init(int nid)
-{
- gcry_error_t err;
- int algo = nid_to_md_algo(nid);
- EVPCTX ctx;
-
- err = gcry_md_open(&ctx, algo, 0);
- if (err) {
- return NULL;
- }
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- gcry_md_write(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- int algo = gcry_md_get_algo(ctx);
- *mdlen = gcry_md_get_algo_dlen(algo);
- memcpy(md, gcry_md_read(ctx, algo), *mdlen);
- gcry_md_close(ctx);
-}
-#endif
-
-SHA256CTX sha256_init(void) {
- SHA256CTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA256, 0);
-
- return ctx;
-}
-
-void sha256_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha256_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA256_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha256(const unsigned char *digest, int len, unsigned char *hash){
- gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
-}
-
-SHA384CTX sha384_init(void) {
- SHA384CTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA384, 0);
-
- return ctx;
-}
-
-void sha384_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha384_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA384_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha384(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len);
-}
-
-SHA512CTX sha512_init(void) {
- SHA512CTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA512, 0);
-
- return ctx;
-}
-
-void sha512_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha512_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA512_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha512(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len);
-}
-
-MD5CTX md5_init(void) {
- MD5CTX c = NULL;
- gcry_md_open(&c, GCRY_MD_MD5, 0);
-
- return c;
-}
-
-void md5_update(MD5CTX c, const void *data, unsigned long len) {
- gcry_md_write(c,data,len);
-}
-
-void md5_final(unsigned char *md, MD5CTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN);
- gcry_md_close(c);
-}
-
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
return sshkdf_derive_key(crypto, key, key_len,
key_type, output, requested_len);
}
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
+HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type) {
HMACCTX c = NULL;
switch(type) {
@@ -268,14 +103,17 @@ HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
return c;
}
-void hmac_update(HMACCTX c, const void *data, unsigned long len) {
+int hmac_update(HMACCTX c, const void *data, size_t len) {
gcry_md_write(c, data, len);
+ return 1;
}
-void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len) {
- *len = gcry_md_get_algo_dlen(gcry_md_get_algo(c));
+int hmac_final(HMACCTX c, unsigned char *hashmacbuf, size_t *len) {
+ unsigned int tmp = gcry_md_get_algo_dlen(gcry_md_get_algo(c));
+ *len = (size_t)tmp;
memcpy(hashmacbuf, gcry_md_read(c, 0), *len);
gcry_md_close(c);
+ return 1;
}
#ifdef WITH_BLOWFISH_CIPHER
@@ -307,12 +145,12 @@ static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *I
}
static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
#endif /* WITH_BLOWFISH_CIPHER */
@@ -350,7 +188,7 @@ static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
}
break;
default:
- SSH_LOG(SSH_LOG_WARNING, "Unksupported key length %u.", cipher->keysize);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported key length %u.", cipher->keysize);
SAFE_FREE(cipher->key);
return -1;
}
@@ -434,7 +272,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setiv(cipher->key[0],
cipher->last_iv,
AES_GCM_IVLEN);
- /* This actualy does not increment the packet counter for the
+ /* This actually does not increment the packet counter for the
* current encryption operation, but for the next one. The first
* operation needs to be completed with the derived IV.
*
@@ -443,7 +281,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
*/
uint64_inc(cipher->last_iv + 4);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
return;
}
@@ -451,7 +289,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
/* Pass the authenticated data (packet_length) */
err = gcry_cipher_authenticate(cipher->key[0], in, aadlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_authenticate failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_authenticate failed: %s",
gpg_strerror(err));
return;
}
@@ -464,7 +302,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)in + aadlen,
len - aadlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
return;
}
@@ -474,7 +312,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
(void *)tag,
authlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_gettag failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_gettag failed: %s",
gpg_strerror(err));
return;
}
@@ -499,7 +337,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setiv(cipher->key[0],
cipher->last_iv,
AES_GCM_IVLEN);
- /* This actualy does not increment the packet counter for the
+ /* This actually does not increment the packet counter for the
* current encryption operation, but for the next one. The first
* operation needs to be completed with the derived IV.
*
@@ -508,7 +346,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
*/
uint64_inc(cipher->last_iv + 4);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -518,7 +356,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
complete_packet,
aadlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_authenticate failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_authenticate failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -532,7 +370,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)complete_packet + aadlen,
encrypted_size);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_decrypt failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -542,10 +380,10 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)complete_packet + aadlen + encrypted_size,
authlen);
if (gpg_err_code(err) == GPG_ERR_CHECKSUM) {
- SSH_LOG(SSH_LOG_WARNING, "The authentication tag does not match");
+ SSH_LOG(SSH_LOG_DEBUG, "The authentication tag does not match");
return SSH_ERROR;
} else if (err != GPG_ERR_NO_ERROR) {
- SSH_LOG(SSH_LOG_WARNING, "General error while decryption: %s",
+ SSH_LOG(SSH_LOG_TRACE, "General error while decryption: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -578,12 +416,12 @@ static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
}
static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
@@ -631,7 +469,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_open(&ctx->main_hd, GCRY_CIPHER_CHACHA20,
GCRY_CIPHER_MODE_STREAM, 0);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_open failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_open failed: %s",
gpg_strerror(err));
SAFE_FREE(cipher->chacha20_schedule);
return -1;
@@ -639,7 +477,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_open(&ctx->header_hd, GCRY_CIPHER_CHACHA20,
GCRY_CIPHER_MODE_STREAM, 0);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_open failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_open failed: %s",
gpg_strerror(err));
gcry_cipher_close(ctx->main_hd);
SAFE_FREE(cipher->chacha20_schedule);
@@ -647,7 +485,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
}
err = gcry_mac_open(&ctx->mac_hd, GCRY_MAC_POLY1305, 0, NULL);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_open failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_open failed: %s",
gpg_strerror(err));
gcry_cipher_close(ctx->main_hd);
gcry_cipher_close(ctx->header_hd);
@@ -660,7 +498,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setkey(ctx->main_hd, u8key, CHACHA20_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setkey failed: %s",
gpg_strerror(err));
chacha20_cleanup(cipher);
return -1;
@@ -669,7 +507,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setkey(ctx->header_hd, u8key + CHACHA20_KEYLEN,
CHACHA20_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setkey failed: %s",
gpg_strerror(err));
chacha20_cleanup(cipher);
return -1;
@@ -696,7 +534,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* step 1, prepare the poly1305 key */
err = gcry_cipher_setiv(ctx->main_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
goto out;
}
@@ -708,13 +546,13 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
zero_block,
sizeof(zero_block));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
err = gcry_mac_setkey(ctx->mac_hd, poly_key, POLY1305_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_setkey failed: %s",
gpg_strerror(err));
goto out;
}
@@ -722,7 +560,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* step 2, encrypt length field */
err = gcry_cipher_setiv(ctx->header_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
goto out;
}
@@ -732,7 +570,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)&in_packet->length,
sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
@@ -744,7 +582,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
in_packet->payload,
len - sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
@@ -752,13 +590,13 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* step 4, compute the MAC */
err = gcry_mac_write(ctx->mac_hd, (uint8_t *)out_packet, len);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_write failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_write failed: %s",
gpg_strerror(err));
goto out;
}
err = gcry_mac_read(ctx->mac_hd, tag, &taglen);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_read failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_read failed: %s",
gpg_strerror(err));
goto out;
}
@@ -784,7 +622,7 @@ static int chacha20_poly1305_aead_decrypt_length(
err = gcry_cipher_setiv(ctx->header_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -794,7 +632,7 @@ static int chacha20_poly1305_aead_decrypt_length(
in,
sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_decrypt failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -820,7 +658,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
/* step 1, prepare the poly1305 key */
err = gcry_cipher_setiv(ctx->main_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
goto out;
}
@@ -832,13 +670,13 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
zero_block,
sizeof(zero_block));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
err = gcry_mac_setkey(ctx->mac_hd, poly_key, POLY1305_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_setkey failed: %s",
gpg_strerror(err));
goto out;
}
@@ -847,7 +685,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
err = gcry_mac_write(ctx->mac_hd, (uint8_t *)complete_packet,
encrypted_size + sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_write failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_write failed: %s",
gpg_strerror(err));
goto out;
}
@@ -856,7 +694,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
SSH_LOG(SSH_LOG_PACKET, "poly1305 verify error");
goto out;
} else if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_verify failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_verify failed: %s",
gpg_strerror(err));
goto out;
}
@@ -868,7 +706,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)complete_packet + sizeof(uint32_t),
encrypted_size);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_decrypt failed: %s",
gpg_strerror(err));
goto out;
}
diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c
index 35e750e4..55951764 100644
--- a/src/libmbedcrypto.c
+++ b/src/libmbedcrypto.c
@@ -27,6 +27,7 @@
#include "libssh/crypto.h"
#include "libssh/priv.h"
#include "libssh/misc.h"
+#include "mbedcrypto-compat.h"
#if defined(MBEDTLS_CHACHA20_C) && defined(MBEDTLS_POLY1305_C)
#include "libssh/bytearray.h"
#include "libssh/chacha20-poly1305-common.h"
@@ -41,7 +42,7 @@
#endif /* MBEDTLS_GCM_C */
static mbedtls_entropy_context ssh_mbedtls_entropy;
-static mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg;
+extern mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg;
static int libmbedcrypto_initialized = 0;
@@ -50,354 +51,16 @@ void ssh_reseed(void)
mbedtls_ctr_drbg_reseed(&ssh_mbedtls_ctr_drbg, NULL, 0);
}
-int ssh_get_random(void *where, int len, int strong)
-{
- return ssh_mbedtls_random(where, len, strong);
-}
-
-SHACTX sha1_init(void)
-{
- SHACTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha1_update(SHACTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha1(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-static mbedtls_md_type_t nid_to_md_algo(int nid)
-{
- switch (nid) {
- case NID_mbedtls_nistp256:
- return MBEDTLS_MD_SHA256;
- case NID_mbedtls_nistp384:
- return MBEDTLS_MD_SHA384;
- case NID_mbedtls_nistp521:
- return MBEDTLS_MD_SHA512;
- }
- return MBEDTLS_MD_NONE;
-}
-
-void evp(int nid, unsigned char *digest, int len,
- unsigned char *hash, unsigned int *hlen)
-{
- mbedtls_md_type_t algo = nid_to_md_algo(nid);
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(algo);
-
-
- if (md_info != NULL) {
- *hlen = mbedtls_md_get_size(md_info);
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-EVPCTX evp_init(int nid)
-{
- EVPCTX ctx = NULL;
- int rc;
- mbedtls_md_type_t algo = nid_to_md_algo(nid);
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(algo);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- mbedtls_md_update(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- *mdlen = mbedtls_md_get_size(ctx->md_info);
- mbedtls_md_finish(ctx, md);
- mbedtls_md_free(ctx);
- SAFE_FREE(ctx);
-}
-
-SHA256CTX sha256_init(void)
-{
- SHA256CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if(ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha256_update(SHA256CTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha256_final(unsigned char *md, SHA256CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha256(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-SHA384CTX sha384_init(void)
-{
- SHA384CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha384_update(SHA384CTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha384_final(unsigned char *md, SHA384CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha384(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-SHA512CTX sha512_init(void)
-{
- SHA512CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha512_update(SHA512CTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha512_final(unsigned char *md, SHA512CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha512(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-MD5CTX md5_init(void)
-{
- MD5CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-
-void md5_update(MD5CTX c, const void *data, unsigned long len) {
- mbedtls_md_update(c, data, len);
-}
-
-void md5_final(unsigned char *md, MD5CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
return sshkdf_derive_key(crypto, key, key_len,
key_type, output, requested_len);
}
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type)
+HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type)
{
HMACCTX ctx = NULL;
const mbedtls_md_info_t *md_info = NULL;
@@ -446,17 +109,21 @@ error:
return NULL;
}
-void hmac_update(HMACCTX c, const void *data, unsigned long len)
+/* mbedtls returns 0 on success, but in this context
+ * success is 1 */
+int hmac_update(HMACCTX c, const void *data, size_t len)
{
- mbedtls_md_hmac_update(c, data, len);
+ return !mbedtls_md_hmac_update(c, data, len);
}
-void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len)
+int hmac_final(HMACCTX c, unsigned char *hashmacbuf, size_t *len)
{
- *len = mbedtls_md_get_size(c->md_info);
- mbedtls_md_hmac_finish(c, hashmacbuf);
+ int rc;
+ *len = (unsigned int)mbedtls_md_get_size(c->md_info);
+ rc = !mbedtls_md_hmac_finish(c, hashmacbuf);
mbedtls_md_free(c);
SAFE_FREE(c);
+ return rc;
}
static int
@@ -467,6 +134,8 @@ cipher_init(struct ssh_cipher_struct *cipher,
{
const mbedtls_cipher_info_t *cipher_info = NULL;
mbedtls_cipher_context_t *ctx;
+ size_t key_bitlen = 0;
+ size_t iv_size = 0;
int rc;
if (operation == MBEDTLS_ENCRYPT) {
@@ -474,7 +143,7 @@ cipher_init(struct ssh_cipher_struct *cipher,
} else if (operation == MBEDTLS_DECRYPT) {
ctx = &cipher->decrypt_ctx;
} else {
- SSH_LOG(SSH_LOG_WARNING, "unknown operation");
+ SSH_LOG(SSH_LOG_TRACE, "unknown operation");
return 1;
}
@@ -483,21 +152,21 @@ cipher_init(struct ssh_cipher_struct *cipher,
rc = mbedtls_cipher_setup(ctx, cipher_info);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setup failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_setup failed");
goto error;
}
- rc = mbedtls_cipher_setkey(ctx, key,
- cipher_info->key_bitlen,
- operation);
+ key_bitlen = mbedtls_cipher_info_get_key_bitlen(cipher_info);
+ rc = mbedtls_cipher_setkey(ctx, key, key_bitlen, operation);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_setkey failed");
goto error;
}
- rc = mbedtls_cipher_set_iv(ctx, IV, cipher_info->iv_size);
+ iv_size = mbedtls_cipher_info_get_iv_size(cipher_info);
+ rc = mbedtls_cipher_set_iv(ctx, IV, iv_size);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_set_iv failed");
goto error;
}
@@ -516,13 +185,13 @@ cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_ENCRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -540,23 +209,23 @@ cipher_set_encrypt_key_cbc(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_ENCRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
- /* libssh only encypts and decrypts packets that are multiples of a block
+ /* libssh only encrypts and decrypts packets that are multiples of a block
* size, and no padding is used */
rc = mbedtls_cipher_set_padding_mode(&cipher->encrypt_ctx,
MBEDTLS_PADDING_NONE);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_padding_mode failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_set_padding_mode failed");
goto error;
}
rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -573,17 +242,18 @@ cipher_set_key_gcm(struct ssh_cipher_struct *cipher,
void *IV)
{
const mbedtls_cipher_info_t *cipher_info = NULL;
+ size_t key_bitlen = 0;
int rc;
mbedtls_gcm_init(&cipher->gcm_ctx);
cipher_info = mbedtls_cipher_info_from_type(cipher->type);
- rc = mbedtls_gcm_setkey(&cipher->gcm_ctx,
- MBEDTLS_CIPHER_ID_AES,
- key,
- cipher_info->key_bitlen);
+ key_bitlen = mbedtls_cipher_info_get_key_bitlen(cipher_info);
+ rc = mbedtls_gcm_setkey(&cipher->gcm_ctx, MBEDTLS_CIPHER_ID_AES,
+ key, key_bitlen);
+
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_setkey failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_gcm_setkey failed");
goto error;
}
@@ -606,13 +276,13 @@ cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_DECRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
mbedtls_cipher_reset(&cipher->decrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -631,20 +301,20 @@ cipher_set_decrypt_key_cbc(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_DECRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
rc = mbedtls_cipher_set_padding_mode(&cipher->decrypt_ctx,
MBEDTLS_PADDING_NONE);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_padding_mode failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_set_padding_mode failed");
goto error;
}
mbedtls_cipher_reset(&cipher->decrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -664,7 +334,7 @@ static void cipher_encrypt(struct ssh_cipher_struct *cipher,
int rc = 0;
rc = mbedtls_cipher_update(&cipher->encrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during encryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during encryption");
return;
}
@@ -680,12 +350,12 @@ static void cipher_encrypt(struct ssh_cipher_struct *cipher,
total_len += outlen;
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_finish failed during encryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_finish failed during encryption");
return;
}
if (total_len != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -699,12 +369,12 @@ static void cipher_encrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void
int rc = 0;
rc = mbedtls_cipher_update(&cipher->encrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during encryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during encryption");
return;
}
if (outlen != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -722,7 +392,7 @@ static void cipher_decrypt(struct ssh_cipher_struct *cipher,
rc = mbedtls_cipher_update(&cipher->decrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during decryption");
return;
}
@@ -736,14 +406,14 @@ static void cipher_decrypt(struct ssh_cipher_struct *cipher,
outlen, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed during decryption");
return;
}
total_len += outlen;
if (total_len != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -757,7 +427,7 @@ static void cipher_decrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void
int rc = 0;
rc = mbedtls_cipher_update(&cipher->decrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during decryption");
return;
}
@@ -776,19 +446,19 @@ static void cipher_decrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void
}
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_finish failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_finish failed during decryption");
return;
}
rc = mbedtls_cipher_reset(&cipher->decrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed during decryption");
return;
}
if (outlen != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -842,7 +512,7 @@ cipher_encrypt_gcm(struct ssh_cipher_struct *cipher,
authlen,
tag); /* tag */
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_crypt_and_tag failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_gcm_crypt_and_tag failed");
return;
}
@@ -876,7 +546,7 @@ cipher_decrypt_gcm(struct ssh_cipher_struct *cipher,
(const uint8_t *)complete_packet + aadlen, /* input */
(unsigned char *)out); /* output */
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_auth_decrypt failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_gcm_auth_decrypt failed");
return SSH_ERROR;
}
@@ -950,14 +620,14 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
/* K2 uses the first half of the key */
rv = mbedtls_chacha20_setkey(&ctx->main_ctx, u8key);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_setkey(main_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_setkey(main_ctx) failed");
goto out;
}
/* K1 uses the second half of the key */
rv = mbedtls_chacha20_setkey(&ctx->header_ctx, u8key + CHACHA20_KEYLEN);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_setkey(header_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_setkey(header_ctx) failed");
goto out;
}
@@ -981,7 +651,7 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
/* The nonce in mbedTLS is 96 b long. The counter is passed through separate
* parameter of 32 b size.
- * Encode the seqence number into the last 8 bytes.
+ * Encode the sequence number into the last 8 bytes.
*/
PUSH_BE_U64(seqbuf, 4, seq);
#ifdef DEBUG_CRYPTO
@@ -990,13 +660,13 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
ret = mbedtls_chacha20_starts(&ctx->header_ctx, seqbuf, 0);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_starts(header_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_starts(header_ctx) failed");
return SSH_ERROR;
}
ret = mbedtls_chacha20_starts(&ctx->main_ctx, seqbuf, 0);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_starts(main_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_starts(main_ctx) failed");
return SSH_ERROR;
}
@@ -1025,7 +695,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
rv = mbedtls_chacha20_update(&ctx->main_ctx, sizeof(zero_block),
zero_block, poly_key);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
goto out;
}
#ifdef DEBUG_CRYPTO
@@ -1035,7 +705,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
/* Set the Poly1305 key */
rv = mbedtls_poly1305_starts(&ctx->poly_ctx, poly_key);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_starts failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_starts failed");
goto out;
}
@@ -1071,7 +741,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
rv = mbedtls_chacha20_update(&ctx->header_ctx, sizeof(uint32_t), in, out);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
return SSH_ERROR;
}
@@ -1099,7 +769,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
rv = chacha20_poly1305_packet_setup(cipher, seq, 0);
if (rv != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
goto out;
}
@@ -1111,13 +781,13 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
rv = mbedtls_poly1305_update(&ctx->poly_ctx, complete_packet,
encrypted_size + sizeof(uint32_t));
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_update failed");
goto out;
}
rv = mbedtls_poly1305_finish(&ctx->poly_ctx, tag);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_finish failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_finish failed");
goto out;
}
@@ -1138,7 +808,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)complete_packet + sizeof(uint32_t),
out);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
goto out;
}
@@ -1162,7 +832,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
ret = chacha20_poly1305_packet_setup(cipher, seq, 1);
if (ret != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
return;
}
@@ -1175,7 +845,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)&in_packet->length,
(unsigned char *)&out_packet->length);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
return;
}
#ifdef DEBUG_CRYPTO
@@ -1188,19 +858,19 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
ret = mbedtls_chacha20_update(&ctx->main_ctx, len - sizeof(uint32_t),
in_packet->payload, out_packet->payload);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
return;
}
/* step 4, compute the MAC */
ret = mbedtls_poly1305_update(&ctx->poly_ctx, (const unsigned char *)out_packet, len);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_update failed");
return;
}
ret = mbedtls_poly1305_finish(&ctx->poly_ctx, tag);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_finish failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_finish failed");
return;
}
}
@@ -1430,22 +1100,6 @@ int ssh_crypto_init(void)
return SSH_OK;
}
-int ssh_mbedtls_random(void *where, int len, int strong)
-{
- int rc = 0;
- if (strong) {
- mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
- MBEDTLS_CTR_DRBG_PR_ON);
- rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
- mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
- MBEDTLS_CTR_DRBG_PR_OFF);
- } else {
- rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
- }
-
- return !rc;
-}
-
mbedtls_ctr_drbg_context *ssh_get_mbedtls_ctr_drbg_context(void)
{
return &ssh_mbedtls_ctr_drbg;
diff --git a/src/libssh.map b/src/libssh.map
index 82cdfa21..558f921d 100644
--- a/src/libssh.map
+++ b/src/libssh.map
@@ -1,4 +1,4 @@
-# This map file was updated with abimap-0.3.1
+# This map file was updated with abimap-0.3.2
LIBSSH_4_5_0 # Released
{
@@ -402,10 +402,8 @@ LIBSSH_4_5_0 # Released
ssh_userauth_pubkey;
ssh_userauth_publickey;
ssh_userauth_publickey_auto;
- ssh_userauth_publickey_auto_get_current_identity;
ssh_userauth_try_publickey;
ssh_version;
- ssh_vlog;
ssh_write_knownhost;
string_burn;
string_copy;
@@ -449,3 +447,30 @@ LIBSSH_4_8_1 # Released
ssh_session_get_known_hosts_entry;
ssh_threads_get_default;
} LIBSSH_4_8_0;
+
+LIBSSH_4_9_0 # Released
+{
+ global:
+ ssh_channel_open_forward_port;
+ ssh_key_dup;
+ ssh_send_issue_banner;
+ ssh_session_set_disconnect_message;
+ ssh_userauth_publickey_auto_get_current_identity;
+ ssh_vlog;
+} LIBSSH_4_8_1;
+
+LIBSSH_AFTER_4_9_0
+{
+ global:
+ sftp_channel_default_data_callback;
+ sftp_channel_default_subsystem_request;
+ sftp_aio_begin_read;
+ sftp_aio_begin_write;
+ sftp_aio_free;
+ sftp_aio_wait_read;
+ sftp_aio_wait_write;
+ ssh_pki_export_privkey_base64_format;
+ ssh_pki_export_privkey_file_format;
+ ssh_channel_request_pty_size_modes;
+} LIBSSH_4_9_0;
+
diff --git a/src/log.c b/src/log.c
index 8ce1e71e..5bae18b8 100644
--- a/src/log.c
+++ b/src/log.c
@@ -47,7 +47,7 @@ static LIBSSH_THREAD ssh_logging_callback ssh_log_cb;
static LIBSSH_THREAD void *ssh_log_userdata;
/**
- * @defgroup libssh_log The SSH logging functions.
+ * @defgroup libssh_log The SSH logging functions
* @ingroup libssh
*
* Logging functions for debugging and problem resolving.
diff --git a/src/match.c b/src/match.c
index 1a60d732..3e58f733 100644
--- a/src/match.c
+++ b/src/match.c
@@ -43,7 +43,7 @@
#include "libssh/priv.h"
-#define MAX_MATCH_RECURSION 32
+#define MAX_MATCH_RECURSION 16
/*
* Returns true if the given string matches the pattern (which may contain ?
@@ -51,74 +51,77 @@
*/
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;
- }
+ bool had_asterisk = false;
- for (;;) {
- /* If at end of pattern, accept if also at end of string. */
- if (*pattern == '\0') {
- return (*s == '\0');
+ if (s == NULL || pattern == NULL || limit <= 0) {
+ return 0;
}
- while (*pattern == '*') {
- /* Skip the asterisk. */
- had_asterisk = true;
- pattern++;
- }
+ for (;;) {
+ /* If at end of pattern, accept if also at end of string. */
+ if (*pattern == '\0') {
+ return (*s == '\0');
+ }
- if (had_asterisk) {
- /* If at end of pattern, accept immediately. */
- if (!*pattern)
- return 1;
+ /* Skip all the asterisks and adjacent question marks */
+ while (*pattern == '*' || (had_asterisk && *pattern == '?')) {
+ if (*pattern == '*') {
+ had_asterisk = true;
+ }
+ pattern++;
+ }
- /* If next character in pattern is known, optimize. */
- if (*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 != '?') {
+ /*
+ * 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, limit - 1)) {
+ return 1;
+ }
+ /* Failed. */
+ return 0;
+ }
+ /*
+ * Move ahead one character at a time and try to
+ * match at each position.
+ */
+ for (; *s; s++) {
+ if (match_pattern(s, pattern, limit - 1)) {
+ return 1;
+ }
+ }
+ /* Failed. */
+ return 0;
+ }
/*
- * Look instances of the next character in
- * pattern, and try to match starting from
- * those.
+ * There must be at least one more character in the string.
+ * If we are at the end, fail.
*/
- for (; *s; s++)
- if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) {
- return 1;
- }
- /* Failed. */
- return 0;
- }
- /*
- * Move ahead one character at a time and try to
- * match at each position.
- */
- for (; *s; s++) {
- if (match_pattern(s, pattern, limit - 1)) {
- return 1;
+ if (!*s) {
+ return 0;
}
- }
- /* Failed. */
- return 0;
- }
- /*
- * There must be at least one more character in the string.
- * If we are at the end, fail.
- */
- if (!*s) {
- return 0;
- }
- /* Check if the next character of the string is acceptable. */
- if (*pattern != '?' && *pattern != *s) {
- return 0;
- }
+ /* Check if the next character of the string is acceptable. */
+ if (*pattern != '?' && *pattern != *s) {
+ return 0;
+ }
- /* Move to the next character, both in string and in pattern. */
- s++;
- pattern++;
- }
+ /* Move to the next character, both in string and in pattern. */
+ s++;
+ pattern++;
+ }
- /* NOTREACHED */
- return 0;
+ /* NOTREACHED */
+ return 0;
}
/*
@@ -128,11 +131,11 @@ static int match_pattern(const char *s, const char *pattern, size_t limit)
* no match at all.
*/
int match_pattern_list(const char *string, const char *pattern,
- unsigned int len, int dolower) {
+ size_t len, int dolower) {
char sub[1024];
int negated;
int got_positive;
- unsigned int i, subi;
+ size_t i, subi;
got_positive = 0;
for (i = 0; i < len;) {
diff --git a/src/mbedcrypto-compat.h b/src/mbedcrypto-compat.h
new file mode 100644
index 00000000..705294a7
--- /dev/null
+++ b/src/mbedcrypto-compat.h
@@ -0,0 +1,39 @@
+#ifndef MBEDCRYPTO_COMPAT_H
+#define MBEDCRYPTO_COMPAT_H
+
+/* mbedtls/version.h should be available for both v2 and v3
+ * v3 defines the version inside build_info.h so if it isn't defined
+ * in version.h we should have v3
+ */
+#include <mbedtls/version.h>
+#include <mbedtls/cipher.h>
+#ifdef MBEDTLS_VERSION_MAJOR
+#if MBEDTLS_VERSION_MAJOR < 3
+
+static inline size_t mbedtls_cipher_info_get_key_bitlen(
+ const mbedtls_cipher_info_t *info)
+{
+ if (info == NULL) {
+ return 0;
+ }
+ return info->key_bitlen;
+}
+
+static inline size_t mbedtls_cipher_info_get_iv_size(
+ const mbedtls_cipher_info_t *info)
+{
+ if (info == NULL) {
+ return 0;
+ }
+ return (size_t)info->iv_size;
+}
+
+#define MBEDTLS_PRIVATE(X) X
+#endif /* MBEDTLS_VERSION_MAJOR < 3 */
+#else /* MBEDTLS_VERSION_MAJOR */
+#include <mbedtls/build_info.h>
+#if MBEDTLS_VERSION_MAJOR < 3
+#define MBEDTLS_PRIVATE(X) X
+#endif /* MBEDTLS_VERSION_MAJOR < 3 */
+#endif /* MBEDTLS_VERSION_MAJOR */
+#endif /* MBEDCRYPTO_COMPAT_H */
diff --git a/src/mbedcrypto_missing.c b/src/mbedcrypto_missing.c
index ac0d688c..2c1a8d7a 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(const_bignum num, int radix)
+char *ssh_mbedcry_bn2num(const_bignum num, int radix)
{
char *buf = NULL;
size_t olen;
@@ -56,7 +56,7 @@ unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix)
return NULL;
}
- buf = malloc(olen);
+ buf = mbedtls_calloc(1, olen);
if (buf == NULL) {
return NULL;
}
@@ -67,7 +67,7 @@ unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix)
return NULL;
}
- return (unsigned char *) buf;
+ return buf;
}
int ssh_mbedcry_rand(bignum rnd, int bits, int top, int bottom)
diff --git a/src/md_crypto.c b/src/md_crypto.c
new file mode 100644
index 00000000..f7cda8dd
--- /dev/null
+++ b/src/md_crypto.c
@@ -0,0 +1,324 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 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 "libcrypto-compat.h"
+#include "libssh/crypto.h"
+#include "libssh/wrapper.h"
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+SHACTX
+sha1_init(void)
+{
+ int rc;
+ SHACTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha1_ctx_free(SHACTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha1_update(SHACTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1_final(unsigned char *md, SHACTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHACTX c = sha1_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha1_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha1_final(hash, c);
+}
+
+SHA256CTX
+sha256_init(void)
+{
+ int rc;
+ SHA256CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha256_update(SHA256CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256_final(unsigned char *md, SHA256CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHA256CTX c = sha256_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha256_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha256_final(hash, c);
+}
+
+SHA384CTX
+sha384_init(void)
+{
+ int rc;
+ SHA384CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha384_update(SHA384CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384_final(unsigned char *md, SHA384CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHA384CTX c = sha384_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha384_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha384_final(hash, c);
+}
+
+SHA512CTX
+sha512_init(void)
+{
+ int rc = 0;
+ SHA512CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha512_update(SHA512CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512_final(unsigned char *md, SHA512CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHA512CTX c = sha512_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha512_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha512_final(hash, c);
+}
+
+MD5CTX
+md5_init(void)
+{
+ int rc;
+ MD5CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+md5_ctx_free(MD5CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+md5_update(MD5CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+md5_final(unsigned char *md, MD5CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
diff --git a/src/md_gcrypt.c b/src/md_gcrypt.c
new file mode 100644
index 00000000..93c7b0d9
--- /dev/null
+++ b/src/md_gcrypt.c
@@ -0,0 +1,246 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * 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/crypto.h"
+#include "libssh/wrapper.h"
+
+#include <gcrypt.h>
+
+SHACTX
+sha1_init(void)
+{
+ SHACTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA1, 0);
+
+ return ctx;
+}
+
+int
+sha1_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+void
+sha1_ctx_free(SHACTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha1_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha1(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
+ return SSH_OK;
+}
+
+SHA256CTX
+sha256_init(void)
+{
+ SHA256CTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA256, 0);
+
+ return ctx;
+}
+
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha256_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+sha256_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA256_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha256(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
+ return SSH_OK;
+}
+
+SHA384CTX
+sha384_init(void)
+{
+ SHA384CTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA384, 0);
+
+ return ctx;
+}
+
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha384_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+sha384_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA384_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha384(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len);
+ return SSH_OK;
+}
+
+SHA512CTX
+sha512_init(void)
+{
+ SHA512CTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA512, 0);
+
+ return ctx;
+}
+
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha512_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+sha512_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA512_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha512(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len);
+ return SSH_OK;
+}
+
+MD5CTX
+md5_init(void)
+{
+ MD5CTX c = NULL;
+ gcry_md_open(&c, GCRY_MD_MD5, 0);
+
+ return c;
+}
+
+void
+md5_ctx_free(MD5CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+md5_update(MD5CTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+md5_final(unsigned char *md, MD5CTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, MD5_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
diff --git a/src/md_mbedcrypto.c b/src/md_mbedcrypto.c
new file mode 100644
index 00000000..b3529b4b
--- /dev/null
+++ b/src/md_mbedcrypto.c
@@ -0,0 +1,407 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * 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/crypto.h"
+#include "libssh/wrapper.h"
+#include "mbedcrypto-compat.h"
+
+#include <mbedtls/md.h>
+
+SHACTX
+sha1_init(void)
+{
+ SHACTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha1_ctx_free(SHACTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha1_update(SHACTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1_final(unsigned char *md, SHACTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha1_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+SHA256CTX
+sha256_init(void)
+{
+ SHA256CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha256_update(SHA256CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256_final(unsigned char *md, SHA256CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+SHA384CTX
+sha384_init(void)
+{
+ SHA384CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha384_update(SHA384CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384_final(unsigned char *md, SHA384CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha384_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+SHA512CTX
+sha512_init(void)
+{
+ SHA512CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha512_update(SHA512CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512_final(unsigned char *md, SHA512CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha512_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+MD5CTX
+md5_init(void)
+{
+ MD5CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+md5_ctx_free(MD5CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+md5_update(MD5CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+md5_final(unsigned char *md, MD5CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
diff --git a/src/messages.c b/src/messages.c
index c7fcc887..be9462ac 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -54,7 +54,7 @@
* This file contains the message parsing utilities for client and server
* programs using libssh.
*
- * On the server the the main loop of the program will call
+ * On the server the main loop of the program will call
* ssh_message_get(session) to get messages as they come. They are not 1-1 with
* the protocol messages. Then, the user will know what kind of a message it is
* and use the appropriate functions to handle it (or use the default handlers
@@ -79,7 +79,7 @@ static ssh_message ssh_message_new(ssh_session session)
#ifndef WITH_SERVER
-/* Reduced version of the reply default that only reply with
+/* Reduced version of the reply default that only replies with
* SSH_MSG_UNIMPLEMENTED
*/
static int ssh_message_reply_default(ssh_message msg) {
@@ -160,7 +160,7 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
if (channel != NULL) {
rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to send reply for accepting a channel "
"open");
}
@@ -237,7 +237,7 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
msg->channel_request.pxwidth,
msg->channel_request.pxheight);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to iterate callbacks for window change");
}
return SSH_OK;
@@ -317,6 +317,17 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
return SSH_AGAIN;
}
+static int ssh_reply_channel_open_request(ssh_message msg, ssh_channel channel)
+{
+ if (channel != NULL) {
+ return ssh_message_channel_request_open_reply_accept_channel(msg, channel);
+ }
+
+ ssh_message_reply_default(msg);
+
+ return SSH_OK;
+}
+
static int ssh_execute_client_request(ssh_session session, ssh_message msg)
{
ssh_channel channel = NULL;
@@ -329,30 +340,26 @@ static int ssh_execute_client_request(ssh_session session, ssh_message msg)
msg->channel_request_open.originator,
msg->channel_request_open.originator_port,
session->common.callbacks->userdata);
- if (channel != NULL) {
- rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
-
- return rc;
- } else {
- ssh_message_reply_default(msg);
- }
- return SSH_OK;
+ return ssh_reply_channel_open_request(msg, channel);
} else if (msg->type == SSH_REQUEST_CHANNEL_OPEN
&& msg->channel_request_open.type == SSH_CHANNEL_AUTH_AGENT
&& ssh_callbacks_exists(session->common.callbacks, channel_open_request_auth_agent_function)) {
channel = session->common.callbacks->channel_open_request_auth_agent_function (session,
session->common.callbacks->userdata);
- if (channel != NULL) {
- rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
-
- return rc;
- } else {
- ssh_message_reply_default(msg);
- }
+ return ssh_reply_channel_open_request(msg, channel);
+ } else if (msg->type == SSH_REQUEST_CHANNEL_OPEN
+ && msg->channel_request_open.type == SSH_CHANNEL_FORWARDED_TCPIP
+ && ssh_callbacks_exists(session->common.callbacks, channel_open_request_forwarded_tcpip_function)) {
+ channel = session->common.callbacks->channel_open_request_forwarded_tcpip_function(session,
+ msg->channel_request_open.destination,
+ msg->channel_request_open.destination_port,
+ msg->channel_request_open.originator,
+ msg->channel_request_open.originator_port,
+ session->common.callbacks->userdata);
- return SSH_OK;
+ return ssh_reply_channel_open_request(msg, channel);
}
return rc;
@@ -593,6 +600,7 @@ void ssh_message_free(ssh_message msg){
switch(msg->type) {
case SSH_REQUEST_AUTH:
SAFE_FREE(msg->auth_request.username);
+ SAFE_FREE(msg->auth_request.sigtype);
if (msg->auth_request.password) {
explicit_bzero(msg->auth_request.password,
strlen(msg->auth_request.password));
@@ -714,8 +722,8 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
rc = ssh_buffer_pack(buffer,
"dPbsssbsS",
- crypto->digest_len, /* session ID string */
- (size_t)crypto->digest_len, crypto->session_id,
+ crypto->session_id_len, /* session ID string */
+ crypto->session_id_len, crypto->session_id,
SSH2_MSG_USERAUTH_REQUEST, /* type */
msg->auth_request.username,
service,
@@ -774,7 +782,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
cmp = strcmp(service, "ssh-connection");
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid service request: %s",
service);
goto end;
@@ -852,6 +860,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
goto error;
}
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_NONE;
+ msg->auth_request.sigtype = strdup(ssh_string_get_char(algo));
+ if (msg->auth_request.sigtype == NULL) {
+ msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR;
+ SSH_STRING_FREE(algo);
+ algo = NULL;
+ goto error;
+ }
+
// has a valid signature ?
if(has_sign) {
ssh_string sig_blob = NULL;
@@ -1046,7 +1062,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
}
if (session->kbdint == NULL) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive "
+ SSH_LOG(SSH_LOG_DEBUG, "Warning: Got a keyboard-interactive "
"response but it seems we didn't send the request.");
session->kbdint = ssh_kbdint_new();
@@ -1067,10 +1083,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
session->kbdint->nanswers = 0;
}
- SSH_LOG(SSH_LOG_PACKET,"kbdint: %d answers",nanswers);
+ SSH_LOG(SSH_LOG_PACKET,"kbdint: %" PRIu32 " answers", nanswers);
if (nanswers > KBDINT_MAX_PROMPT) {
ssh_set_error(session, SSH_FATAL,
- "Too much answers received from client: %u (0x%.4x)",
+ "Too much answers received from client: %" PRIu32 " (0x%.4" PRIx32 ")",
nanswers, nanswers);
ssh_kbdint_free(session->kbdint);
session->kbdint = NULL;
@@ -1080,8 +1096,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
if(nanswers != session->kbdint->nprompts) {
/* warn but let the application handle this case */
- SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers"
- " mismatch: p=%u a=%u", session->kbdint->nprompts, nanswers);
+ SSH_LOG(SSH_LOG_DEBUG, "Warning: Number of prompts and answers"
+ " mismatch: p=%" PRIu32 " a=%" PRIu32, session->kbdint->nprompts, nanswers);
}
session->kbdint->nanswers = nanswers;
@@ -1160,8 +1176,16 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)");
goto error;
}
-
- if (strcmp(type_c,"session") == 0) {
+
+ if (strcmp(type_c, "session") == 0) {
+ if (session->flags & SSH_SESSION_FLAG_NO_MORE_SESSIONS) {
+ ssh_session_set_disconnect_message(session, "No more sessions allowed!");
+ ssh_set_error(session, SSH_FATAL, "No more sessions allowed!");
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ ssh_disconnect(session);
+ goto error;
+ }
+
msg->channel_request_open.type = SSH_CHANNEL_SESSION;
SAFE_FREE(type_c);
goto end;
@@ -1174,9 +1198,9 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
&destination_port,
&msg->channel_request_open.originator,
&originator_port);
- if (rc != SSH_OK) {
- goto error;
- }
+ if (rc != SSH_OK) {
+ goto error;
+ }
msg->channel_request_open.destination_port = (uint16_t) destination_port;
msg->channel_request_open.originator_port = (uint16_t) originator_port;
@@ -1239,7 +1263,7 @@ end:
*
* @param[in] chan The channel the request is made on.
*
- * @returns SSH_OK on success, SSH_ERROR if an error occured.
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
*/
int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_channel chan) {
ssh_session session;
@@ -1273,7 +1297,7 @@ int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_c
}
SSH_LOG(SSH_LOG_PACKET,
- "Accepting a channel request_open for chan %d",
+ "Accepting a channel request_open for chan %" PRIu32,
chan->remote_channel);
rc = ssh_packet_send(session);
@@ -1330,7 +1354,7 @@ ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) {
*
* @param[in] want_reply The want_reply field from the request.
*
- * @returns SSH_OK on success, SSH_ERROR if an error occured.
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
*/
int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet,
const char *request, uint8_t want_reply) {
@@ -1344,7 +1368,7 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "Received a %s channel_request for channel (%d:%d) (want_reply=%hhd)",
+ "Received a %s channel_request for channel (%" PRIu32 ":%" PRIu32 ") (want_reply=%hhd)",
request, channel->local_channel, channel->remote_channel, want_reply);
msg->type = SSH_REQUEST_CHANNEL;
@@ -1444,6 +1468,14 @@ error:
return SSH_ERROR;
}
+/** @internal
+ *
+ * @brief Sends a successful channel request reply
+ *
+ * @param msg A message to reply to
+ *
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
+ */
int ssh_message_channel_request_reply_success(ssh_message msg) {
uint32_t channel;
int rc;
@@ -1456,7 +1488,7 @@ int ssh_message_channel_request_reply_success(ssh_message msg) {
channel = msg->channel_request.channel->remote_channel;
SSH_LOG(SSH_LOG_PACKET,
- "Sending a channel_request success to channel %d", channel);
+ "Sending a channel_request success to channel %" PRIu32, channel);
rc = ssh_buffer_pack(msg->session->out_buffer,
"bd",
@@ -1487,7 +1519,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
(void)type;
(void)packet;
- SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet");
+ SSH_LOG(SSH_LOG_DEBUG,"Received SSH_MSG_GLOBAL_REQUEST packet");
r = ssh_buffer_unpack(packet, "sb",
&request,
&want_reply);
@@ -1519,12 +1551,12 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD;
msg->global_request.want_reply = want_reply;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
+ SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
msg->global_request.bind_address,
msg->global_request.bind_port);
if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request,
+ SSH_LOG(SSH_LOG_DEBUG, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request,
want_reply, msg->global_request.bind_address,
msg->global_request.bind_port);
session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata);
@@ -1549,7 +1581,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD;
msg->global_request.want_reply = want_reply;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
+ SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
msg->global_request.bind_address,
msg->global_request.bind_port);
@@ -1563,14 +1595,23 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
} else if(strcmp(request, "keepalive@openssh.com") == 0) {
msg->global_request.type = SSH_GLOBAL_REQUEST_KEEPALIVE;
msg->global_request.want_reply = want_reply;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received keepalive@openssh.com %d", want_reply);
+ SSH_LOG(SSH_LOG_DEBUG, "Received keepalive@openssh.com %d", want_reply);
if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) {
session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata);
} else {
ssh_message_global_request_reply_success(msg, 0);
}
+ } else if (strcmp(request, "no-more-sessions@openssh.com") == 0) {
+ msg->global_request.type = SSH_GLOBAL_REQUEST_NO_MORE_SESSIONS;
+ msg->global_request.want_reply = want_reply;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Received no-more-sessions@openssh.com %d", want_reply);
+
+ ssh_message_global_request_reply_success(msg, 0);
+
+ session->flags |= SSH_SESSION_FLAG_NO_MORE_SESSIONS;
} else {
- SSH_LOG(SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s, "
+ SSH_LOG(SSH_LOG_DEBUG, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s, "
"want_reply = %d", request, want_reply);
goto reply_with_failure;
}
@@ -1603,7 +1644,7 @@ reply_with_failure:
error:
SAFE_FREE(msg);
SAFE_FREE(request);
- SSH_LOG(SSH_LOG_WARNING, "Invalid SSH_MSG_GLOBAL_REQUEST packet");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid SSH_MSG_GLOBAL_REQUEST packet");
return rc;
}
diff --git a/src/misc.c b/src/misc.c
index 6472d583..c7a9706c 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <net/if.h>
#endif /* _WIN32 */
@@ -59,6 +60,7 @@
#include <ws2tcpip.h>
#include <shlobj.h>
#include <direct.h>
+#include <netioapi.h>
#ifdef HAVE_IO_H
#include <io.h>
@@ -94,8 +96,10 @@
#define ZLIB_STRING ""
#endif
+#define ARPA_DOMAIN_MAX_LEN 63
+
/**
- * @defgroup libssh_misc The SSH helper functions.
+ * @defgroup libssh_misc The SSH helper functions
* @ingroup libssh
*
* Different helper functions used in the SSH Library.
@@ -104,7 +108,8 @@
*/
#ifdef _WIN32
-char *ssh_get_user_home_dir(void) {
+char *ssh_get_user_home_dir(void)
+{
char tmp[PATH_MAX] = {0};
char *szPath = NULL;
@@ -122,12 +127,13 @@ char *ssh_get_user_home_dir(void) {
}
/* we have read access on file */
-int ssh_file_readaccess_ok(const char *file) {
- if (_access(file, 4) < 0) {
- return 0;
- }
+int ssh_file_readaccess_ok(const char *file)
+{
+ if (_access(file, 4) < 0) {
+ return 0;
+ }
- return 1;
+ return 1;
}
/**
@@ -158,7 +164,8 @@ int ssh_dir_writeable(const char *path)
#define SSH_USEC_IN_SEC 1000000LL
#define SSH_SECONDS_SINCE_1601 11644473600LL
-int gettimeofday(struct timeval *__p, void *__t) {
+int ssh_gettimeofday(struct timeval *__p, void *__t)
+{
union {
unsigned long long ns100; /* time since 1 Jan 1601 in 100ns units */
FILETIME ft;
@@ -171,26 +178,34 @@ int gettimeofday(struct timeval *__p, void *__t) {
return (0);
}
-char *ssh_get_local_username(void) {
+char *ssh_get_local_username(void)
+{
DWORD size = 0;
- char *user;
+ char *user = NULL;
+ int rc;
/* get the size */
GetUserName(NULL, &size);
- user = (char *) malloc(size);
+ user = (char *)malloc(size);
if (user == NULL) {
return NULL;
}
if (GetUserName(user, &size)) {
- return user;
+ rc = ssh_check_username_syntax(user);
+ if (rc == SSH_OK) {
+ return user;
+ }
}
+ free(user);
+
return NULL;
}
-int ssh_is_ipaddr_v4(const char *str) {
+int ssh_is_ipaddr_v4(const char *str)
+{
struct sockaddr_storage ss;
int sslen = sizeof(ss);
int rc = SOCKET_ERROR;
@@ -212,24 +227,40 @@ int ssh_is_ipaddr_v4(const char *str) {
return 0;
}
-int ssh_is_ipaddr(const char *str) {
+int ssh_is_ipaddr(const char *str)
+{
int rc = SOCKET_ERROR;
+ char *s = strdup(str);
- if (strchr(str, ':')) {
+ if (s == NULL) {
+ return -1;
+ }
+ if (strchr(s, ':')) {
struct sockaddr_storage ss;
int sslen = sizeof(ss);
-
- /* TODO link-local (IP:v6:addr%ifname). */
- rc = WSAStringToAddressA((LPSTR) str,
+ char *network_interface = strchr(s, '%');
+
+ /* link-local (IP:v6:addr%ifname). */
+ if (network_interface != NULL) {
+ rc = if_nametoindex(network_interface + 1);
+ if (rc == 0) {
+ free(s);
+ return 0;
+ }
+ *network_interface = '\0';
+ }
+ rc = WSAStringToAddressA((LPSTR) s,
AF_INET6,
NULL,
(struct sockaddr*)&ss,
&sslen);
if (rc == 0) {
+ free(s);
return 1;
}
}
+ free(s);
return ssh_is_ipaddr_v4(str);
}
#else /* _WIN32 */
@@ -302,7 +333,7 @@ char *ssh_get_local_username(void)
struct passwd pwd;
struct passwd *pwdbuf = NULL;
char buf[NSS_BUFLEN_PASSWD];
- char *name;
+ char *name = NULL;
int rc;
rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
@@ -311,15 +342,18 @@ char *ssh_get_local_username(void)
}
name = strdup(pwd.pw_name);
+ rc = ssh_check_username_syntax(name);
- if (name == NULL) {
+ if (rc != SSH_OK) {
+ free(name);
return NULL;
}
return name;
}
-int ssh_is_ipaddr_v4(const char *str) {
+int ssh_is_ipaddr_v4(const char *str)
+{
int rc = -1;
struct in_addr dest;
@@ -331,25 +365,42 @@ int ssh_is_ipaddr_v4(const char *str) {
return 0;
}
-int ssh_is_ipaddr(const char *str) {
+int ssh_is_ipaddr(const char *str)
+{
int rc = -1;
+ char *s = strdup(str);
- if (strchr(str, ':')) {
+ if (s == NULL) {
+ return -1;
+ }
+ if (strchr(s, ':')) {
struct in6_addr dest6;
-
- /* TODO link-local (IP:v6:addr%ifname). */
- rc = inet_pton(AF_INET6, str, &dest6);
+ char *network_interface = strchr(s, '%');
+
+ /* link-local (IP:v6:addr%ifname). */
+ if (network_interface != NULL) {
+ rc = if_nametoindex(network_interface + 1);
+ if (rc == 0) {
+ free(s);
+ return 0;
+ }
+ *network_interface = '\0';
+ }
+ rc = inet_pton(AF_INET6, s, &dest6);
if (rc > 0) {
+ free(s);
return 1;
}
}
+ free(s);
return ssh_is_ipaddr_v4(str);
}
#endif /* _WIN32 */
-char *ssh_lowercase(const char* str) {
+char *ssh_lowercase(const char* str)
+{
char *new, *p;
if (str == NULL) {
@@ -392,15 +443,17 @@ char *ssh_hostport(const char *host, int port)
* @brief Convert a buffer into a colon separated hex string.
* The caller has to free the memory.
*
- * @param what What should be converted to a hex string.
+ * @param[in] what What should be converted to a hex string.
*
- * @param len Length of the buffer to convert.
+ * @param[in] len Length of the buffer to convert.
*
- * @return The hex string or NULL on error.
+ * @return The hex string or NULL on error. The memory needs
+ * to be freed using ssh_string_free_char().
*
* @see ssh_string_free_char()
*/
-char *ssh_get_hexa(const unsigned char *what, size_t len) {
+char *ssh_get_hexa(const unsigned char *what, size_t len)
+{
const char h[] = "0123456789abcdef";
char *hexa;
size_t i;
@@ -428,7 +481,8 @@ char *ssh_get_hexa(const unsigned char *what, size_t len) {
/**
* @deprecated Please use ssh_print_hash() instead
*/
-void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
+void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len)
+{
char *hexa = ssh_get_hexa(what, len);
if (hexa == NULL) {
@@ -455,7 +509,7 @@ void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
* " 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 '.'.
+ * end if the value is printable. Otherwise, it is replaced with '.'.
*
* @param[in] descr A description for the content to be logged
* @param[in] what The buffer to be logged
@@ -622,7 +676,7 @@ void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len)
return;
error:
- SSH_LOG(SSH_LOG_WARN, "Could not print to buffer");
+ SSH_LOG(SSH_LOG_DEBUG, "Could not print to buffer");
return;
}
@@ -649,48 +703,55 @@ error:
* }
* @endcode
*/
-const char *ssh_version(int req_version) {
- if (req_version <= LIBSSH_VERSION_INT) {
- return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING MBED_STRING
- ZLIB_STRING;
- }
+const char *ssh_version(int req_version)
+{
+ if (req_version <= LIBSSH_VERSION_INT) {
+ return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING
+ MBED_STRING ZLIB_STRING;
+ }
- return NULL;
+ return NULL;
}
-struct ssh_list *ssh_list_new(void) {
- struct ssh_list *ret=malloc(sizeof(struct ssh_list));
- if(!ret)
- return NULL;
- ret->root=ret->end=NULL;
- return ret;
+struct ssh_list *ssh_list_new(void)
+{
+ struct ssh_list *ret = malloc(sizeof(struct ssh_list));
+ if (ret == NULL) {
+ return NULL;
+ }
+ ret->root = ret->end = NULL;
+ return ret;
}
-void ssh_list_free(struct ssh_list *list){
- struct ssh_iterator *ptr,*next;
- if(!list)
- return;
- ptr=list->root;
- while(ptr){
- next=ptr->next;
- SAFE_FREE(ptr);
- ptr=next;
- }
- SAFE_FREE(list);
+void ssh_list_free(struct ssh_list *list)
+{
+ struct ssh_iterator *ptr, *next;
+ if (!list)
+ return;
+ ptr = list->root;
+ while (ptr) {
+ next = ptr->next;
+ SAFE_FREE(ptr);
+ ptr = next;
+ }
+ SAFE_FREE(list);
}
-struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list){
- if(!list)
- return NULL;
- return list->root;
+struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
+{
+ if (!list)
+ return NULL;
+ return list->root;
}
-struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){
- struct ssh_iterator *it;
- for(it = ssh_list_get_iterator(list); it != NULL ;it=it->next)
- if(it->data==value)
- return it;
- return NULL;
+struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value)
+{
+ struct ssh_iterator *it;
+
+ for (it = ssh_list_get_iterator(list); it != NULL ; it = it->next)
+ if (it->data == value)
+ return it;
+ return NULL;
}
/**
@@ -703,7 +764,7 @@ struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){
size_t ssh_list_count(const struct ssh_list *list)
{
struct ssh_iterator *it = NULL;
- int count = 0;
+ size_t count = 0;
for (it = ssh_list_get_iterator(list); it != NULL ; it = it->next) {
count++;
@@ -712,16 +773,20 @@ size_t ssh_list_count(const struct ssh_list *list)
return count;
}
-static struct ssh_iterator *ssh_iterator_new(const void *data){
- struct ssh_iterator *iterator=malloc(sizeof(struct ssh_iterator));
- if(!iterator)
- return NULL;
- iterator->next=NULL;
- iterator->data=data;
- return iterator;
+static struct ssh_iterator *ssh_iterator_new(const void *data)
+{
+ struct ssh_iterator *iterator = malloc(sizeof(struct ssh_iterator));
+
+ if (iterator == NULL) {
+ return NULL;
+ }
+ iterator->next = NULL;
+ iterator->data = data;
+ return iterator;
}
-int ssh_list_append(struct ssh_list *list,const void *data){
+int ssh_list_append(struct ssh_list *list,const void *data)
+{
struct ssh_iterator *iterator = NULL;
if (list == NULL) {
@@ -744,7 +809,8 @@ int ssh_list_append(struct ssh_list *list,const void *data){
return SSH_OK;
}
-int ssh_list_prepend(struct ssh_list *list, const void *data){
+int ssh_list_prepend(struct ssh_list *list, const void *data)
+{
struct ssh_iterator *it = NULL;
if (list == NULL) {
@@ -768,8 +834,9 @@ int ssh_list_prepend(struct ssh_list *list, const void *data){
return SSH_OK;
}
-void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){
- struct ssh_iterator *ptr,*prev;
+void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator)
+{
+ struct ssh_iterator *ptr, *prev;
if (list == NULL) {
return;
@@ -803,12 +870,13 @@ void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){
* @brief Removes the top element of the list and returns the data value
* attached to it.
*
- * @param[in[ list The ssh_list to remove the element.
+ * @param[in] list The ssh_list to remove the element.
*
* @returns A pointer to the element being stored in head, or NULL
* if the list is empty.
*/
-const void *_ssh_list_pop_head(struct ssh_list *list){
+const void *_ssh_list_pop_head(struct ssh_list *list)
+{
struct ssh_iterator *iterator = NULL;
const void *data = NULL;
@@ -834,17 +902,21 @@ const void *_ssh_list_pop_head(struct ssh_list *list){
* dirname breaks a null-terminated pathname string into a directory component.
* In the usual case, ssh_dirname() returns the string up to, but not including,
* the final '/'. Trailing '/' characters are not counted as part of the
- * pathname. The caller must free the memory.
+ * pathname. The caller must free the memory using ssh_string_free_char().
*
* @param[in] path The path to parse.
*
* @return The dirname of path or NULL if we can't allocate memory.
* If path does not contain a slash, c_dirname() returns
- * the string ".". If path is the string "/", it returns
+ * the string ".". If path is a string "/", it returns
* the string "/". If path is NULL or an empty string,
- * "." is returned.
+ * "." is returned. The memory needs to be freed using
+ * ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_dirname (const char *path) {
+char *ssh_dirname (const char *path)
+{
char *new = NULL;
size_t len;
@@ -895,11 +967,15 @@ char *ssh_dirname (const char *path) {
* @param[in] path The path to parse.
*
* @return The filename of path or NULL if we can't allocate
- * memory. If path is a the string "/", basename returns
+ * memory. If path is the string "/", basename returns
* the string "/". If path is NULL or an empty string,
- * "." is returned.
+ * "." is returned. The caller needs to free this memory
+ * ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_basename (const char *path) {
+char *ssh_basename (const char *path)
+{
char *new = NULL;
const char *s;
size_t len;
@@ -1032,9 +1108,13 @@ int ssh_mkdirs(const char *pathname, mode_t mode)
*
* @param[in] d The directory to expand.
*
- * @return The expanded directory, NULL on error.
+ * @return The expanded directory, NULL on error. The caller
+ * needs to free the memory using ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_path_expand_tilde(const char *d) {
+char *ssh_path_expand_tilde(const char *d)
+{
char *h = NULL, *r;
const char *p;
size_t ld;
@@ -1101,10 +1181,14 @@ char *ssh_path_expand_tilde(const char *d) {
* %l local hostname
* %r remote username
* %p remote port
- * @returns Expanded string.
+ * @returns Expanded string. The caller needs to free the memory using
+ * ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_path_expand_escape(ssh_session session, const char *s) {
- char host[NI_MAXHOST];
+char *ssh_path_expand_escape(ssh_session session, const char *s)
+{
+ char host[NI_MAXHOST] = {0};
char *buf = NULL;
char *r = NULL;
char *x = NULL;
@@ -1156,7 +1240,15 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
case '%':
goto escape;
case 'd':
- x = strdup(session->opts.sshdir);
+ if (session->opts.sshdir) {
+ x = strdup(session->opts.sshdir);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand sshdir");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'u':
x = ssh_get_local_username();
@@ -1167,20 +1259,35 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
}
break;
case 'h':
- x = strdup(session->opts.host);
+ if (session->opts.host) {
+ x = strdup(session->opts.host);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand host");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'r':
- x = strdup(session->opts.username);
+ if (session->opts.username) {
+ x = strdup(session->opts.username);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand username");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'p':
- if (session->opts.port < 65536) {
- char tmp[6];
-
- snprintf(tmp,
- sizeof(tmp),
- "%u",
- session->opts.port > 0 ? session->opts.port : 22);
- x = strdup(tmp);
+ {
+ char tmp[6];
+
+ snprintf(tmp, sizeof(tmp), "%hu",
+ (uint16_t)(session->opts.port > 0 ? session->opts.port
+ : 22));
+ x = strdup(tmp);
}
break;
default:
@@ -1267,7 +1374,7 @@ int ssh_analyze_banner(ssh_session session, int server)
return -1;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Analyzing banner: %s", banner);
+ SSH_LOG(SSH_LOG_DEBUG, "Analyzing banner: %s", banner);
switch (banner[4]) {
case '2':
@@ -1297,27 +1404,31 @@ int ssh_analyze_banner(ssh_session session, int server)
* 012345678901234567890
*/
if (strlen(openssh) > 9) {
+ errno = 0;
major = strtoul(openssh + 8, &tmp, 10);
if ((tmp == (openssh + 8)) ||
((errno == ERANGE) && (major == ULONG_MAX)) ||
((errno != 0) && (major == 0)) ||
((major < 1) || (major > 100))) {
/* invalid major */
+ errno = 0;
goto done;
}
+ errno = 0;
minor = strtoul(openssh + 10, &tmp, 10);
if ((tmp == (openssh + 10)) ||
((errno == ERANGE) && (major == ULONG_MAX)) ||
((errno != 0) && (major == 0)) ||
(minor > 100)) {
/* invalid minor */
+ errno = 0;
goto done;
}
session->openssh = SSH_VERSION_INT(((int) major), ((int) minor), 0);
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"We are talking to an OpenSSH %s version: %lu.%lu (%x)",
server ? "client" : "server",
major, minor, session->openssh);
@@ -1340,7 +1451,8 @@ done:
* @brief initializes a timestamp to the current time
* @param[out] ts pointer to an allocated ssh_timestamp structure
*/
-void ssh_timestamp_init(struct ssh_timestamp *ts){
+void ssh_timestamp_init(struct ssh_timestamp *ts)
+{
#ifdef HAVE_CLOCK_GETTIME
struct timespec tp;
clock_gettime(CLOCK, &tp);
@@ -1363,17 +1475,18 @@ void ssh_timestamp_init(struct ssh_timestamp *ts){
* @returns difference in milliseconds
*/
-static int ssh_timestamp_difference(struct ssh_timestamp *old,
- struct ssh_timestamp *new){
- long seconds, usecs, msecs;
- seconds = new->seconds - old->seconds;
- usecs = new->useconds - old->useconds;
- if (usecs < 0){
- seconds--;
- usecs += 1000000;
- }
- msecs = seconds * 1000 + usecs/1000;
- return msecs;
+static int
+ssh_timestamp_difference(struct ssh_timestamp *old, struct ssh_timestamp *new)
+{
+ long seconds, usecs, msecs;
+ seconds = new->seconds - old->seconds;
+ usecs = new->useconds - old->useconds;
+ if (usecs < 0){
+ seconds--;
+ usecs += 1000000;
+ }
+ msecs = seconds * 1000 + usecs/1000;
+ return msecs;
}
/**
@@ -1384,14 +1497,20 @@ static int ssh_timestamp_difference(struct ssh_timestamp *old,
* @param[in] usec number of microseconds
* @returns milliseconds, or 10000 if user supplied values are equal to zero
*/
-int ssh_make_milliseconds(long sec, long usec) {
- int res = usec ? (usec / 1000) : 0;
+int ssh_make_milliseconds(unsigned long sec, unsigned long usec)
+{
+ unsigned long res = usec ? (usec / 1000) : 0;
res += (sec * 1000);
if (res == 0) {
res = 10 * 1000; /* use a reasonable default value in case
* SSH_OPTIONS_TIMEOUT is not set in options. */
}
- return res;
+
+ if (res > INT_MAX) {
+ return SSH_TIMEOUT_INFINITE;
+ } else {
+ return (int)res;
+ }
}
/**
@@ -1404,7 +1523,8 @@ int ssh_make_milliseconds(long sec, long usec) {
* @returns 1 if timeout is elapsed
* 0 otherwise
*/
-int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
+int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout)
+{
struct ssh_timestamp now;
switch(timeout) {
@@ -1412,7 +1532,7 @@ int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
* -2 means user-defined timeout as available in
* session->timeout, session->timeout_usec.
*/
- SSH_LOG(SSH_LOG_WARN, "ssh_timeout_elapsed called with -2. this needs to "
+ SSH_LOG(SSH_LOG_DEBUG, "ssh_timeout_elapsed called with -2. this needs to "
"be fixed. please set a breakpoint on misc.c:%d and "
"fix the caller\n", __LINE__);
return 0;
@@ -1436,7 +1556,8 @@ int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
* timeout
* @returns remaining time in milliseconds, 0 if elapsed, -1 if never.
*/
-int ssh_timeout_update(struct ssh_timestamp *ts, int timeout){
+int ssh_timeout_update(struct ssh_timestamp *ts, int timeout)
+{
struct ssh_timestamp now;
int ms, ret;
if (timeout <= 0) {
@@ -1565,20 +1686,20 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t 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");
+ SSH_LOG(SSH_LOG_TRACE, "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");
+ SSH_LOG(SSH_LOG_TRACE, "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");
+ SSH_LOG(SSH_LOG_TRACE, "Buffer too small");
return SSH_ERROR;
}
@@ -1629,13 +1750,13 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len)
*dst++ = '\\';
break;
case SINGLE_QUOTE:
- /* Close the current quoted string and replace '!' for unquoted
+ /* Close the currently quoted string and replace '!' for unquoted
* "\!" */
*dst++ = '\'';
*dst++ = '\\';
break;
case DOUBLE_QUOTE:
- /* Close current quoted string and replace "!" for unquoted
+ /* Close currently quoted string and replace "!" for unquoted
* "\!" */
*dst++ = '"';
*dst++ = '\\';
@@ -1736,7 +1857,7 @@ int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
}
if ((2 * strlen(string) + 1) > buf_len) {
- SSH_LOG(SSH_LOG_WARNING, "Buffer too small");
+ SSH_LOG(SSH_LOG_TRACE, "Buffer too small");
return SSH_ERROR;
}
@@ -1759,23 +1880,23 @@ int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
*
* @brief Replaces the last 6 characters of a string from 'X' to 6 random hexdigits.
*
- * @param[in] template Any input string with last 6 characters as 'X'.
+ * @param[in,out] name Any input string with last 6 characters as 'X'.
* @returns -1 as error when the last 6 characters of the input to be replaced are not 'X'
* 0 otherwise.
*/
-int ssh_tmpname(char *template)
+int ssh_tmpname(char *name)
{
char *tmp = NULL;
size_t i = 0;
int rc = 0;
uint8_t random[6];
- if (template == NULL) {
+ if (name == NULL) {
goto err;
}
- tmp = template + strlen(template) - 6;
- if (tmp < template) {
+ tmp = name + strlen(name) - 6;
+ if (tmp < name) {
goto err;
}
@@ -1811,15 +1932,19 @@ err:
/**
* @internal
*
- * @brief Finds the first occurence of a patterm in a string and replaces it.
+ * @brief Finds the first occurrence of a pattern in a string and replaces it.
*
- * @param[in] src Source string containing the patern to be replaced.
+ * @param[in] src Source string containing the pattern to be replaced.
* @param[in] pattern Pattern to be replaced in the source string.
- * Note: this function replaces the first occurence of pattern only.
+ * Note: this function replaces the first occurrence of
+ * pattern only.
* @param[in] replace String to be replaced is stored in replace.
*
* @returns src_replaced a pointer that points to the replaced string.
- * NULL if allocation fails or if src is NULL.
+ * NULL if allocation fails or if src is NULL. The returned memory needs to be
+ * freed using ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
char *ssh_strreplace(const char *src, const char *pattern, const char *replace)
{
@@ -1859,4 +1984,247 @@ char *ssh_strreplace(const char *src, const char *pattern, const char *replace)
}
}
+/**
+ * @internal
+ *
+ * @brief Processes errno into error string
+ *
+ * @param[in] err_num The errno value
+ * @param[out] buf Pointer to a place where the string could be saved
+ * @param[in] buflen The allocated size of buf
+ *
+ * @return error string
+ */
+char *ssh_strerror(int err_num, char *buf, size_t buflen)
+{
+#if defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE)
+ /* GNU extension on Linux */
+ return strerror_r(err_num, buf, buflen);
+#else
+ int rv;
+
+#if defined(_WIN32)
+ rv = strerror_s(buf, buflen, err_num);
+#else
+ /* POSIX version available for example on FreeBSD or in musl libc */
+ rv = strerror_r(err_num, buf, buflen);
+#endif /* _WIN32 */
+
+ /* make sure the buffer is initialized and terminated with NULL */
+ if (-rv == ERANGE) {
+ buf[0] = '\0';
+ }
+ return buf;
+#endif /* defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE) */
+}
+
+/**
+ * @brief Read the requested number of bytes from a local file.
+ *
+ * A call to read() may perform a short read even when sufficient data is
+ * present in the file. This function can be used to avoid such short reads.
+ *
+ * This function tries to read the requested number of bytes from the file
+ * until one of the following occurs :
+ * - Requested number of bytes are read.
+ * - EOF is encountered before reading the requested number of bytes.
+ * - An error occurs.
+ *
+ * On encountering an error due to an interrupt, this function ignores that
+ * error and continues trying to read the data.
+ *
+ * @param[in] fd The file descriptor of the local file to read from.
+ *
+ * @param[out] buf Pointer to a buffer in which read data will be
+ * stored.
+ *
+ * @param[in] nbytes Number of bytes to read.
+ *
+ * @returns Number of bytes read on success,
+ * SSH_ERROR on error with errno set to indicate the
+ * error.
+ */
+ssize_t ssh_readn(int fd, void *buf, size_t nbytes)
+{
+ size_t total_bytes_read = 0;
+ ssize_t bytes_read;
+
+ if (fd < 0 || buf == NULL || nbytes == 0) {
+ errno = EINVAL;
+ return SSH_ERROR;
+ }
+
+ do {
+ bytes_read = read(fd,
+ ((char *)buf) + total_bytes_read,
+ nbytes - total_bytes_read);
+ if (bytes_read == -1) {
+ if (errno == EINTR) {
+ /* Ignoring errors due to signal interrupts */
+ continue;
+ }
+
+ return SSH_ERROR;
+ }
+
+ if (bytes_read == 0) {
+ /* EOF encountered on the local file before reading nbytes */
+ break;
+ }
+
+ total_bytes_read += (size_t)bytes_read;
+ } while (total_bytes_read < nbytes);
+
+ return total_bytes_read;
+}
+
+/**
+ * @brief Write the requested number of bytes to a local file.
+ *
+ * A call to write() may perform a short write on a local file. This function
+ * can be used to avoid short writes.
+ *
+ * This function tries to write the requested number of bytes until those many
+ * bytes are written or some error occurs.
+ *
+ * On encountering an error due to an interrupt, this function ignores that
+ * error and continues trying to write the data.
+ *
+ * @param[in] fd The file descriptor of the local file to write to.
+ *
+ * @param[in] buf Pointer to a buffer in which data to write is stored.
+ *
+ * @param[in] nbytes Number of bytes to write.
+ *
+ * @returns Number of bytes written on success,
+ * SSH_ERROR on error with errno set to indicate the
+ * error.
+ */
+ssize_t ssh_writen(int fd, const void *buf, size_t nbytes)
+{
+ size_t total_bytes_written = 0;
+ ssize_t bytes_written;
+
+ if (fd < 0 || buf == NULL || nbytes == 0) {
+ errno = EINVAL;
+ return SSH_ERROR;
+ }
+
+ do {
+ bytes_written = write(fd,
+ ((const char *)buf) + total_bytes_written,
+ nbytes - total_bytes_written);
+ if (bytes_written == -1) {
+ if(errno == EINTR) {
+ /* Ignoring errors due to signal interrupts */
+ continue;
+ }
+
+ return SSH_ERROR;
+ }
+
+ total_bytes_written += (size_t)bytes_written;
+ } while (total_bytes_written < nbytes);
+
+ return total_bytes_written;
+}
+
+/**
+ * @brief Checks syntax of a domain name
+ *
+ * The check is made based on the RFC1035 section 2.3.1
+ * Allowed characters are: hyphen, period, digits (0-9) and letters (a-zA-Z)
+ *
+ * The label should be no longer than 63 characters
+ * The label should start with a letter and end with a letter or number
+ * The label in this implementation can start with a number to allow virtual
+ * URLs to pass. Note that this will make IPv4 addresses to pass
+ * this check too.
+ *
+ * @param hostname The domain name to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the hostname passes syntax check
+ * SSH_ERROR otherwise or if hostname is NULL or empty string
+ */
+int ssh_check_hostname_syntax(const char *hostname)
+{
+ char *it = NULL, *s = NULL, *buf = NULL;
+ size_t it_len;
+ char c;
+
+ if (hostname == NULL || strlen(hostname) == 0) {
+ return SSH_ERROR;
+ }
+
+ /* strtok_r writes into the string, keep the input clean */
+ s = strdup(hostname);
+ if (s == NULL) {
+ return SSH_ERROR;
+ }
+
+ it = strtok_r(s, ".", &buf);
+ /* if the token has 0 length */
+ if (it == NULL) {
+ free(s);
+ return SSH_ERROR;
+ }
+ do {
+ it_len = strlen(it);
+ if (it_len > ARPA_DOMAIN_MAX_LEN ||
+ /* the first char must be a letter, but some virtual urls start
+ * with a number */
+ isalnum(it[0]) == 0 ||
+ isalnum(it[it_len - 1]) == 0) {
+ free(s);
+ return SSH_ERROR;
+ }
+ while (*it != '\0') {
+ c = *it;
+ /* the "." is allowed too, but tokenization removes it from the
+ * string */
+ if (isalnum(c) == 0 && c != '-') {
+ free(s);
+ return SSH_ERROR;
+ }
+ it++;
+ }
+ } while ((it = strtok_r(NULL, ".", &buf)) != NULL);
+
+ free(s);
+
+ return SSH_OK;
+}
+
+/**
+ * @brief Checks syntax of a username
+ *
+ * This check disallows metacharacters in the username
+ *
+ * @param username The username to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the username passes syntax check
+ * SSH_ERROR otherwise or if username is NULL or empty string
+ */
+int ssh_check_username_syntax(const char *username)
+{
+ size_t username_len;
+
+ if (username == NULL || *username == '-') {
+ return SSH_ERROR;
+ }
+
+ username_len = strlen(username);
+ if (username_len == 0 || username[username_len - 1] == '\\' ||
+ strpbrk(username, "'`\";&<>|(){}") != NULL) {
+ return SSH_ERROR;
+ }
+ for (size_t i = 0; i < username_len; i++) {
+ if (isspace(username[i]) != 0 && username[i + 1] == '-') {
+ return SSH_ERROR;
+ }
+ }
+
+ return SSH_OK;
+}
+
/** @} */
diff --git a/src/options.c b/src/options.c
index 264eacb7..961aba4e 100644
--- a/src/options.c
+++ b/src/options.c
@@ -32,10 +32,12 @@
#include <winsock2.h>
#endif
#include <sys/types.h>
+#include "libssh/pki_priv.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/options.h"
+#include "libssh/config_parser.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/bind.h"
@@ -51,21 +53,23 @@
* @brief Duplicate the options of a session structure.
*
* If you make several sessions with the same options this is useful. You
- * cannot use twice the same option structure in ssh_session_connect.
+ * cannot use twice the same option structure in ssh_connect.
*
* @param src The session to use to copy the options.
*
* @param dest A pointer to store the allocated session with duplicated
- * options. You have to free the memory.
+ * options. You have to free the memory using ssh_free()
*
- * @returns 0 on sucess, -1 on error with errno set.
+ * @returns 0 on success, -1 on error with errno set.
*
- * @see ssh_session_connect()
+ * @see ssh_connect()
+ * @see ssh_free()
*/
int ssh_options_copy(ssh_session src, ssh_session *dest)
{
ssh_session new;
struct ssh_iterator *it = NULL;
+ struct ssh_list *list = NULL;
char *id = NULL;
int i;
@@ -103,24 +107,51 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
/* Remove the default identities */
- for (id = ssh_list_pop_head(char *, new->opts.identity);
+ for (id = ssh_list_pop_head(char *, new->opts.identity_non_exp);
id != NULL;
- id = ssh_list_pop_head(char *, new->opts.identity)) {
+ id = ssh_list_pop_head(char *, new->opts.identity_non_exp)) {
SAFE_FREE(id);
}
/* Copy the new identities from the source list */
- if (src->opts.identity != NULL) {
+ list = new->opts.identity_non_exp;
+ it = ssh_list_get_iterator(src->opts.identity_non_exp);
+ for (i = 0; i < 2; i++) {
+ while (it) {
+ int rc;
+
+ id = strdup((char *)it->data);
+ if (id == NULL) {
+ ssh_free(new);
+ return -1;
+ }
+
+ rc = ssh_list_append(list, id);
+ if (rc < 0) {
+ free(id);
+ ssh_free(new);
+ return -1;
+ }
+ it = it->next;
+ }
+
+ /* copy the identity list if there is any already */
+ list = new->opts.identity;
it = ssh_list_get_iterator(src->opts.identity);
+ }
+
+ list = new->opts.certificate_non_exp;
+ it = ssh_list_get_iterator(src->opts.certificate_non_exp);
+ for (i = 0; i < 2; i++) {
while (it) {
int rc;
- id = strdup((char *) it->data);
+ id = strdup((char *)it->data);
if (id == NULL) {
ssh_free(new);
return -1;
}
- rc = ssh_list_append(new->opts.identity, id);
+ rc = ssh_list_append(list, id);
if (rc < 0) {
free(id);
ssh_free(new);
@@ -128,6 +159,10 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
it = it->next;
}
+
+ /* copy the certificate list if there is any already */
+ list = new->opts.certificate;
+ it = ssh_list_get_iterator(src->opts.certificate);
}
if (src->opts.sshdir != NULL) {
@@ -196,6 +231,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
}
+ if (src->opts.control_path != NULL) {
+ new->opts.control_path = strdup(src->opts.control_path);
+ if (new->opts.control_path == NULL) {
+ ssh_free(new);
+ return -1;
+ }
+ }
+
memcpy(new->opts.options_seen, src->opts.options_seen,
sizeof(new->opts.options_seen));
@@ -209,6 +252,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
new->opts.flags = src->opts.flags;
new->opts.nodelay = src->opts.nodelay;
new->opts.config_processed = src->opts.config_processed;
+ new->opts.control_master = src->opts.control_master;
new->common.log_verbosity = src->common.log_verbosity;
new->common.callbacks = src->common.callbacks;
@@ -219,14 +263,30 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
- const char *list)
+ const char *list,
+ char **place)
{
- char *p = NULL;
+ /* When the list start with +,-,^ the filtration of unknown algorithms
+ * gets handled inside the helper functions, otherwise the list is taken
+ * as it is. */
+ char *p = (char *)list;
+
+ if (algo < SSH_COMP_C_S) {
+ if (list[0] == '+') {
+ p = ssh_add_to_default_algos(algo, list+1);
+ } else if (list[0] == '-') {
+ p = ssh_remove_from_default_algos(algo, list+1);
+ } else if (list[0] == '^') {
+ p = ssh_prefix_default_algos(algo, list+1);
+ }
+ }
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(algo, list);
- } else {
- p = ssh_keep_known_algos(algo, list);
+ if (p == list) {
+ if (ssh_fips_mode()) {
+ p = ssh_keep_fips_algos(algo, list);
+ } else {
+ p = ssh_keep_known_algos(algo, list);
+ }
}
if (p == NULL) {
@@ -236,8 +296,8 @@ int ssh_options_set_algo(ssh_session session,
return -1;
}
- SAFE_FREE(session->opts.wanted_methods[algo]);
- session->opts.wanted_methods[algo] = p;
+ SAFE_FREE(*place);
+ *place = p;
return 0;
}
@@ -263,9 +323,10 @@ int ssh_options_set_algo(ssh_session session,
* The file descriptor to use (socket_t).\n
* \n
* If you wish to open the socket yourself for a reason
- * or another, set the file descriptor. Don't forget to
- * set the hostname as the hostname is used as a key in
- * the known_host mechanism.
+ * or another, set the file descriptor and take care of closing
+ * it (this is new behavior in libssh 0.10).
+ * Don't forget to set the hostname as the hostname is used
+ * as a key in the known_host mechanism.
*
* - SSH_OPTIONS_BINDADDR:
* The address to bind the client to (const char *).
@@ -312,13 +373,28 @@ int ssh_options_set_algo(ssh_session session,
* Add a new identity file (const char *, format string) to
* the identity list.\n
* \n
- * By default identity, id_dsa and id_rsa are checked.\n
+ * By default id_rsa, id_ecdsa and id_ed25519 files are used.\n
* \n
* The identity used to authenticate with public key will be
* prepended to the list.
* It may include "%s" which will be replaced by the
* user home directory.
*
+ * - SSH_OPTIONS_CERTIFICATE:
+ * Add a new certificate file (const char *, format string) to
+ * the certificate list.\n
+ * \n
+ * By default id_rsa-cert.pub, id_ecdsa-cert.pub and
+ * id_ed25519-cert.pub files are used, when the underlying
+ * private key is present.\n
+ * \n
+ * The certificate itself can not be used to authenticate to
+ * remote server so it needs to be paired with private key
+ * (aka identity file) provided with separate option, from agent
+ * or from PKCS#11 token.
+ * It may include "%s" which will be replaced by the
+ * user home directory.
+ *
* - SSH_OPTIONS_TIMEOUT:
* Set a timeout for the connection in seconds (long).
*
@@ -340,8 +416,9 @@ int ssh_options_set_algo(ssh_session session,
* - SSH_LOG_NOLOG: No logging
* - SSH_LOG_WARNING: Only warnings
* - SSH_LOG_PROTOCOL: High level protocol information
- * - SSH_LOG_PACKET: Lower level protocol infomations, packet level
+ * - SSH_LOG_PACKET: Lower level protocol information, packet level
* - SSH_LOG_FUNCTIONS: Every function path
+ * The default is SSH_LOG_NOLOG.
*
* - SSH_OPTIONS_LOG_VERBOSITY_STR:
* Set the session logging verbosity via a
@@ -353,34 +430,60 @@ int ssh_options_set_algo(ssh_session session,
*
* - SSH_OPTIONS_CIPHERS_C_S:
* Set the symmetric cipher client to server (const char *,
- * comma-separated list).
+ * comma-separated list). The list can be prepended by +,-,^
+ * which can append, remove or move to the beginning
+ * (prioritizing) of the default list respectively. Giving an
+ * empty list after + and ^ will cause error.
*
* - SSH_OPTIONS_CIPHERS_S_C:
* Set the symmetric cipher server to client (const char *,
- * comma-separated list).
+ * comma-separated list). The list can be prepended by +,-,^
+ * which can append, remove or move to the beginning
+ * (prioritizing) of the default list respectively. Giving an
+ * empty list after + and ^ will cause error.
*
* - SSH_OPTIONS_KEY_EXCHANGE:
* Set the key exchange method to be used (const char *,
* comma-separated list). ex:
* "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+ * The list can be prepended by +,-,^ which will append,
+ * remove or move to the beginning (prioritizing) of the
+ * default list respectively. Giving an empty list
+ * after + and ^ will cause error.
*
* - SSH_OPTIONS_HMAC_C_S:
* Set the Message Authentication Code algorithm client to server
- * (const char *, comma-separated list).
+ * (const char *, comma-separated list). The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_HMAC_S_C:
* Set the Message Authentication Code algorithm server to client
- * (const char *, comma-separated list).
+ * (const char *, comma-separated list). The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_HOSTKEYS:
* Set the preferred server host key types (const char *,
* comma-separated list). ex:
- * "ssh-rsa,ssh-dss,ecdh-sha2-nistp256"
+ * "ssh-rsa,ecdh-sha2-nistp256". The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
* Set the preferred public key algorithms to be used for
* authentication (const char *, comma-separated list). ex:
- * "ssh-rsa,rsa-sha2-256,ssh-dss,ecdh-sha2-nistp256"
+ * "ssh-rsa,rsa-sha2-256,ecdh-sha2-nistp256"
+ * The list can be prepended by +,-,^ which will append,
+ * remove or move to the beginning (prioritizing) of the
+ * default list respectively. Giving an empty list
+ * after + and ^ will cause error.
*
* - SSH_OPTIONS_COMPRESSION_C_S:
* Set the compression to use for client to server
@@ -461,10 +564,49 @@ int ssh_options_set_algo(ssh_session session,
* (uint64_t, 0=default)
*
* - SSH_OPTIONS_REKEY_TIME
- * Set the time limit for a session before intializing a rekey
+ * Set the time limit for a session before initializing a rekey
* in seconds. RFC 4253 Section 9 recommends one hour.
* (uint32_t, 0=off)
*
+ * - SSH_OPTIONS_RSA_MIN_SIZE
+ * Set the minimum RSA key size in bits to be accepted by the
+ * client for both authentication and hostkey verification.
+ * The values under 768 bits are not accepted even with this
+ * configuration option as they are considered completely broken.
+ * Setting 0 will revert the value to defaults.
+ * Default is 1024 bits or 2048 bits in FIPS mode.
+ * (int *)
+
+ * - SSH_OPTIONS_IDENTITY_AGENT
+ * Set the path to the SSH agent socket. If unset, the
+ * SSH_AUTH_SOCK environment is consulted.
+ * (const char *)
+
+ * - SSH_OPTIONS_IDENTITIES_ONLY
+ * Use only keys specified in the SSH config, even if agent
+ * offers more.
+ * (bool)
+ *
+ * - SSH_OPTIONS_CONTROL_MASTER
+ * Set the option to enable the sharing of multiple sessions over a
+ * single network connection using connection multiplexing.
+ *
+ * The possible options are among the following:
+ * - SSH_CONTROL_MASTER_AUTO: enable connection sharing if possible
+ * - SSH_CONTROL_MASTER_YES: enable connection sharing unconditionally
+ * - SSH_CONTROL_MASTER_ASK: ask for confirmation if connection sharing is to be enabled
+ * - SSH_CONTROL_MASTER_AUTOASK: enable connection sharing if possible,
+ * but ask for confirmation
+ * - SSH_CONTROL_MASTER_NO: disable connection sharing unconditionally
+ *
+ * The default is SSH_CONTROL_MASTER_NO.
+ *
+ * - SSH_OPTIONS_CONTROL_PATH
+ * Set the path to the control socket used for connection sharing.
+ * Set to "none" to disable connection sharing.
+ * (const char *)
+ *
+ *
* @param value The value to set. This is a generic pointer and the
* datatype which is used should be set according to the
* type set.
@@ -472,12 +614,14 @@ int ssh_options_set_algo(ssh_session session,
* @return 0 on success, < 0 on error.
*/
int ssh_options_set(ssh_session session, enum ssh_options_e type,
- const void *value) {
+ const void *value)
+{
const char *v;
char *p, *q;
long int i;
unsigned int u;
int rc;
+ char **wanted_methods = session->opts.wanted_methods;
if (session == NULL) {
return -1;
@@ -490,33 +634,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- q = strdup(value);
- if (q == NULL) {
- ssh_set_error_oom(session);
+ char *username = NULL, *hostname = NULL;
+ rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
+ if (rc != SSH_OK) {
return -1;
}
- p = strchr(q, '@');
-
- SAFE_FREE(session->opts.host);
-
- if (p) {
- *p = '\0';
- session->opts.host = strdup(p + 1);
- if (session->opts.host == NULL) {
- SAFE_FREE(q);
- ssh_set_error_oom(session);
- return -1;
- }
-
+ if (username != NULL) {
SAFE_FREE(session->opts.username);
- session->opts.username = strdup(q);
- SAFE_FREE(q);
- if (session->opts.username == NULL) {
- ssh_set_error_oom(session);
- return -1;
- }
- } else {
- session->opts.host = q;
+ session->opts.username = username;
+ }
+ if (hostname != NULL) {
+ SAFE_FREE(session->opts.host);
+ session->opts.host = hostname;
}
}
break;
@@ -547,7 +676,9 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
}
i = strtol(q, &p, 10);
if (q == p) {
+ SSH_LOG(SSH_LOG_DEBUG, "No port number was parsed");
SAFE_FREE(q);
+ return -1;
}
SAFE_FREE(q);
if (i <= 0) {
@@ -607,6 +738,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ rc = ssh_check_username_syntax(session->opts.username);
+ if (rc != SSH_OK) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
}
break;
case SSH_OPTIONS_SSH_DIR:
@@ -639,7 +775,27 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
if (q == NULL) {
return -1;
}
- rc = ssh_list_prepend(session->opts.identity, q);
+ if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
+ rc = ssh_list_append(session->opts.identity_non_exp, q);
+ } else {
+ rc = ssh_list_prepend(session->opts.identity_non_exp, q);
+ }
+ if (rc < 0) {
+ free(q);
+ return -1;
+ }
+ break;
+ case SSH_OPTIONS_CERTIFICATE:
+ v = value;
+ if (v == NULL || v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
+ q = strdup(v);
+ if (q == NULL) {
+ return -1;
+ }
+ rc = ssh_list_append(session->opts.certificate_non_exp, q);
if (rc < 0) {
free(q);
return -1;
@@ -659,6 +815,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
break;
case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
@@ -680,6 +837,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
}
break;
case SSH_OPTIONS_TIMEOUT:
@@ -743,7 +901,9 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
}
i = strtol(q, &p, 10);
if (q == p) {
+ SSH_LOG(SSH_LOG_DEBUG, "No log verbositiy was parsed");
SAFE_FREE(q);
+ return -1;
}
SAFE_FREE(q);
if (i < 0) {
@@ -761,7 +921,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_CRYPT_C_S, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_CRYPT_C_S,
+ v,
+ &wanted_methods[SSH_CRYPT_C_S]);
+ if (rc < 0)
return -1;
}
break;
@@ -771,7 +935,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_CRYPT_S_C, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_CRYPT_S_C,
+ v,
+ &wanted_methods[SSH_CRYPT_S_C]);
+ if (rc < 0)
return -1;
}
break;
@@ -781,7 +949,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_KEX, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_KEX,
+ v,
+ &wanted_methods[SSH_KEX]);
+ if (rc < 0)
return -1;
}
break;
@@ -791,7 +963,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_HOSTKEYS, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_HOSTKEYS,
+ v,
+ &wanted_methods[SSH_HOSTKEYS]);
+ if (rc < 0)
return -1;
}
break;
@@ -801,20 +977,12 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
- } else {
- p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
- }
- if (p == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Setting method: no known public key algorithm (%s)",
- v);
+ rc = ssh_options_set_algo(session,
+ SSH_HOSTKEYS,
+ v,
+ &session->opts.pubkey_accepted_types);
+ if (rc < 0)
return -1;
- }
-
- SAFE_FREE(session->opts.pubkey_accepted_types);
- session->opts.pubkey_accepted_types = p;
}
break;
case SSH_OPTIONS_HMAC_C_S:
@@ -823,7 +991,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_MAC_C_S, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_MAC_C_S,
+ v,
+ &wanted_methods[SSH_MAC_C_S]);
+ if (rc < 0)
return -1;
}
break;
@@ -833,7 +1005,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_MAC_S_C, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_MAC_S_C,
+ v,
+ &wanted_methods[SSH_MAC_S_C]);
+ if (rc < 0)
return -1;
}
break;
@@ -843,16 +1019,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (strcasecmp(value,"yes")==0){
- if(ssh_options_set_algo(session,SSH_COMP_C_S,"zlib@openssh.com,zlib") < 0)
- return -1;
- } else if (strcasecmp(value,"no")==0){
- if(ssh_options_set_algo(session,SSH_COMP_C_S,"none") < 0)
- return -1;
- } else {
- if (ssh_options_set_algo(session, SSH_COMP_C_S, v) < 0)
- return -1;
+ const char *tmp = v;
+ if (strcasecmp(value, "yes") == 0){
+ tmp = "zlib@openssh.com,none";
+ } else if (strcasecmp(value, "no") == 0){
+ tmp = "none,zlib@openssh.com";
}
+ rc = ssh_options_set_algo(session,
+ SSH_COMP_C_S,
+ tmp,
+ &wanted_methods[SSH_COMP_C_S]);
+ if (rc < 0)
+ return -1;
}
break;
case SSH_OPTIONS_COMPRESSION_S_C:
@@ -861,16 +1039,19 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (strcasecmp(value,"yes")==0){
- if(ssh_options_set_algo(session,SSH_COMP_S_C,"zlib@openssh.com,zlib") < 0)
- return -1;
- } else if (strcasecmp(value,"no")==0){
- if(ssh_options_set_algo(session,SSH_COMP_S_C,"none") < 0)
- return -1;
- } else {
- if (ssh_options_set_algo(session, SSH_COMP_S_C, v) < 0)
- return -1;
+ const char *tmp = v;
+ if (strcasecmp(value, "yes") == 0){
+ tmp = "zlib@openssh.com,none";
+ } else if (strcasecmp(value, "no") == 0){
+ tmp = "none,zlib@openssh.com";
}
+
+ rc = ssh_options_set_algo(session,
+ SSH_COMP_S_C,
+ tmp,
+ &wanted_methods[SSH_COMP_S_C]);
+ if (rc < 0)
+ return -1;
}
break;
case SSH_OPTIONS_COMPRESSION:
@@ -906,7 +1087,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.StrictHostKeyChecking = (*x & 0xff) > 0 ? 1 : 0;
}
- session->opts.StrictHostKeyChecking = *(int*)value;
break;
case SSH_OPTIONS_PROXYCOMMAND:
v = value;
@@ -923,6 +1103,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
return -1;
}
session->opts.ProxyCommand = q;
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_PROXYCOMMAND;
}
}
break;
@@ -1029,6 +1210,77 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.rekey_time = (*x) * 1000;
}
break;
+ case SSH_OPTIONS_RSA_MIN_SIZE:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ int *x = (int *)value;
+ if (*x > 0 && *x < 768) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The provided value (%u) for minimal RSA key "
+ "size is too small. Use at least 768 bits.", *x);
+ return -1;
+ }
+ session->opts.rsa_min_size = *x;
+ }
+ break;
+ case SSH_OPTIONS_IDENTITY_AGENT:
+ v = value;
+ SAFE_FREE(session->opts.agent_socket);
+ if (v == NULL) {
+ /* The default value will be set by the ssh_options_apply() */
+ } else if (v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ session->opts.agent_socket = ssh_path_expand_tilde(v);
+ if (session->opts.agent_socket == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ }
+ break;
+ case SSH_OPTIONS_IDENTITIES_ONLY:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ bool *x = (bool *)value;
+ session->opts.identities_only = *x;
+ }
+ break;
+ case SSH_OPTIONS_CONTROL_MASTER:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ int *x = (int *) value;
+ if (*x < SSH_CONTROL_MASTER_NO || *x > SSH_CONTROL_MASTER_AUTOASK) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
+ session->opts.control_master = *x;
+ }
+ break;
+ case SSH_OPTIONS_CONTROL_PATH:
+ v = value;
+ if (v == NULL || v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ SAFE_FREE(session->opts.control_path);
+ rc = strcasecmp(v, "none");
+ if (rc != 0) {
+ session->opts.control_path = ssh_path_expand_tilde(v);
+ if (session->opts.control_path == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_CONTROL_PATH;
+ }
+ }
+ break;
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return -1;
@@ -1070,7 +1322,7 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
/**
* @brief This function can get ssh options, it does not support all options provided for
* ssh options set, but mostly those which a user-space program may care about having
- * trusted the ssh driver to infer these values from underlaying configuration files.
+ * trusted the ssh driver to infer these values from underlying configuration files.
* It operates only on those SSH_OPTIONS_* which return char*. If you wish to receive
* the port then please use ssh_options_get_port() which returns an unsigned int.
*
@@ -1090,7 +1342,7 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_IDENTITY:
* Get the first identity file name (const char *).\n
* \n
- * By default identity, id_dsa and id_rsa are checked.
+ * By default id_rsa, id_ecdsa and id_ed25519 files are used.
*
* - SSH_OPTIONS_PROXYCOMMAND:
* Get the proxycommand necessary to log into the
@@ -1103,6 +1355,9 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_KNOWNHOSTS:
* Get the path to the known_hosts file being used.
*
+ * - SSH_OPTIONS_CONTROL_PATH:
+ * Get the path to the control socket being used for connection multiplexing.
+ *
* @param value The value to get into. As a char**, space will be
* allocated by the function for the value, it is
* your responsibility to free the memory using
@@ -1134,7 +1389,11 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
break;
}
case SSH_OPTIONS_IDENTITY: {
- struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
+ struct ssh_iterator *it;
+ it = ssh_list_get_iterator(session->opts.identity);
+ if (it == NULL) {
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
+ }
if (it == NULL) {
return SSH_ERROR;
}
@@ -1153,6 +1412,10 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
src = session->opts.global_knownhosts;
break;
}
+ case SSH_OPTIONS_CONTROL_PATH: {
+ src = session->opts.control_path;
+ break;
+ }
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return SSH_ERROR;
@@ -1175,10 +1438,10 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
* This is a helper for your application to generate the appropriate
* options from the command line arguments.\n
* The argv array and argc value are changed so that the parsed
- * arguments wont appear anymore in them.\n
+ * arguments won't appear anymore in them.\n
* The single arguments (without switches) are not parsed. thus,
* myssh -l user localhost\n
- * The command wont set the hostname value of options to localhost.
+ * The command won't set the hostname value of options to localhost.
*
* @param session The session to configure.
*
@@ -1208,8 +1471,6 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
size_t i = 0;
int argc = *argcptr;
int debuglevel = 0;
- int usersa = 0;
- int usedss = 0;
int compress = 0;
int cont = 1;
size_t current = 0;
@@ -1223,7 +1484,7 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
}
opterr = 0; /* shut up getopt */
- while((opt = getopt(argc, argv, "c:i:Cl:p:vb:rd12")) != -1) {
+ while((opt = getopt(argc, argv, "c:i:Cl:p:vb:r12")) != -1) {
switch(opt) {
case 'l':
user = optarg;
@@ -1235,10 +1496,6 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
debuglevel++;
break;
case 'r':
- usersa++;
- break;
- case 'd':
- usedss++;
break;
case 'c':
cipher = optarg;
@@ -1302,11 +1559,6 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
optind++;
}
- if (usersa && usedss) {
- ssh_set_error(session, SSH_FATAL, "Either RSA or DSS must be chosen");
- cont = 0;
- }
-
ssh_set_log_level(debuglevel);
optind = saveoptind;
@@ -1375,13 +1627,14 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
* @param session SSH session handle
*
* @param filename The options file to use, if NULL the default
- * ~/.ssh/config will be used.
+ * ~/.ssh/config and /etc/ssh/ssh_config will be used.
*
* @return 0 on success, < 0 on error.
*
* @see ssh_options_set()
*/
-int ssh_options_parse_config(ssh_session session, const char *filename) {
+int ssh_options_parse_config(ssh_session session, const char *filename)
+{
char *expanded_filename;
int r;
@@ -1426,8 +1679,8 @@ out:
return r;
}
-int ssh_options_apply(ssh_session session) {
- struct ssh_iterator *it;
+int ssh_options_apply(ssh_session session)
+{
char *tmp;
int rc;
@@ -1445,54 +1698,124 @@ int ssh_options_apply(ssh_session session) {
}
}
- if (session->opts.knownhosts == NULL) {
- tmp = ssh_path_expand_escape(session, "%d/known_hosts");
- } else {
- tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
- }
- if (tmp == NULL) {
- return -1;
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
+ if (session->opts.knownhosts == NULL) {
+ tmp = ssh_path_expand_escape(session, "%d/known_hosts");
+ } else {
+ tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
+ }
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.knownhosts);
+ session->opts.knownhosts = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
- free(session->opts.knownhosts);
- session->opts.knownhosts = tmp;
- if (session->opts.global_knownhosts == NULL) {
- tmp = strdup("/etc/ssh/ssh_known_hosts");
- } else {
- tmp = ssh_path_expand_escape(session, session->opts.global_knownhosts);
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
+ if (session->opts.global_knownhosts == NULL) {
+ tmp = strdup("/etc/ssh/ssh_known_hosts");
+ } else {
+ tmp = ssh_path_expand_escape(session,
+ session->opts.global_knownhosts);
+ }
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.global_knownhosts);
+ session->opts.global_knownhosts = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
}
- if (tmp == NULL) {
- return -1;
+
+
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND) == 0) {
+ if (session->opts.ProxyCommand != NULL) {
+ char *p = NULL;
+ size_t plen = strlen(session->opts.ProxyCommand) +
+ 5 /* strlen("exec ") */;
+
+ if (strncmp(session->opts.ProxyCommand, "exec ", 5) != 0) {
+ p = malloc(plen + 1 /* \0 */);
+ if (p == NULL) {
+ return -1;
+ }
+
+ rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
+ if ((size_t)rc != plen) {
+ free(p);
+ return -1;
+ }
+ tmp = ssh_path_expand_escape(session, p);
+ free(p);
+ } else {
+ tmp = ssh_path_expand_escape(session,
+ session->opts.ProxyCommand);
+ }
+
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.ProxyCommand);
+ session->opts.ProxyCommand = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_PROXYCOMMAND;
+ }
}
- free(session->opts.global_knownhosts);
- session->opts.global_knownhosts = tmp;
- if (session->opts.ProxyCommand != NULL) {
- tmp = ssh_path_expand_escape(session, session->opts.ProxyCommand);
- if (tmp == NULL) {
- return -1;
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_CONTROL_PATH) == 0) {
+ if (session->opts.control_path != NULL) {
+ tmp = ssh_path_expand_escape(session, session->opts.control_path);
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.control_path);
+ session->opts.control_path = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_CONTROL_PATH;
}
- free(session->opts.ProxyCommand);
- session->opts.ProxyCommand = tmp;
}
- for (it = ssh_list_get_iterator(session->opts.identity);
- it != NULL;
- it = it->next) {
- char *id = (char *) it->data;
- if (strncmp(id, "pkcs11:", 6) == 0) {
+ for (tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp);
+ tmp != NULL;
+ tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
+ char *id = tmp;
+ 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;
+ }
+ free(id);
}
+
+ /* use append to keep the order at first call and use prepend
+ * to put anything that comes on the nth calls to the beginning */
+ if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
+ rc = ssh_list_prepend(session->opts.identity, tmp);
+ } else {
+ rc = ssh_list_append(session->opts.identity, tmp);
+ }
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_IDENTITY;
+
+ for (tmp = ssh_list_pop_head(char *, session->opts.certificate_non_exp);
+ tmp != NULL;
+ tmp = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) {
+ char *id = tmp;
+
tmp = ssh_path_expand_escape(session, id);
if (tmp == NULL) {
return -1;
}
free(id);
- it->data = tmp;
+
+ rc = ssh_list_append(session->opts.certificate, tmp);
+ if (rc != SSH_OK) {
+ return -1;
+ }
}
return 0;
@@ -1501,12 +1824,27 @@ int ssh_options_apply(ssh_session session) {
/** @} */
#ifdef WITH_SERVER
+static bool ssh_bind_key_size_allowed(ssh_bind sshbind, ssh_key key)
+{
+ int min_size = 0;
+
+ switch (ssh_key_type(key)) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ min_size = sshbind->rsa_min_size;
+ return ssh_key_size_allowed_rsa(min_size, key);
+ default:
+ return true;
+ }
+}
+
/**
* @addtogroup libssh_server
* @{
*/
-static int ssh_bind_set_key(ssh_bind sshbind, char **key_loc,
- const void *value) {
+static int
+ssh_bind_set_key(ssh_bind sshbind, char **key_loc, const void *value)
+{
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
@@ -1523,26 +1861,12 @@ static int ssh_bind_set_key(ssh_bind sshbind, char **key_loc,
static int ssh_bind_set_algo(ssh_bind sshbind,
enum ssh_kex_types_e algo,
- const char *list)
+ const char *list,
+ char **place)
{
- char *p = NULL;
-
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(algo, list);
- } else {
- p = ssh_keep_known_algos(algo, list);
- }
- if (p == NULL) {
- ssh_set_error(sshbind, SSH_REQUEST_DENIED,
- "Setting method: no algorithm for method \"%s\" (%s)",
- ssh_kex_get_description(algo), list);
- return -1;
- }
-
- SAFE_FREE(sshbind->wanted_methods[algo]);
- sshbind->wanted_methods[algo] = p;
-
- return 0;
+ /* sshbind is needed only for ssh_set_error which takes void*
+ * the typecast is only to satisfy function parameter type */
+ return ssh_options_set_algo((ssh_session)sshbind, algo, list, place);
}
/**
@@ -1556,7 +1880,7 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* - SSH_BIND_OPTIONS_HOSTKEY:
* Set the path to an ssh host key, regardless
* of type. Only one key from per key type
- * (RSA, DSA, ECDSA) is allowed in an ssh_bind
+ * (RSA, ED25519 and ECDSA) is allowed in an ssh_bind
* at a time, and later calls to this function
* with this option for the same key type will
* override prior calls (const char *).
@@ -1580,8 +1904,9 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* - SSH_LOG_NOLOG: No logging
* - SSH_LOG_WARNING: Only warnings
* - SSH_LOG_PROTOCOL: High level protocol information
- * - SSH_LOG_PACKET: Lower level protocol infomations, packet level
+ * - SSH_LOG_PACKET: Lower level protocol information, packet level
* - SSH_LOG_FUNCTIONS: Every function path
+ * The default is SSH_LOG_NOLOG.
*
* - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR:
* Set the session logging verbosity via a
@@ -1591,23 +1916,23 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* SSH_BIND_OPTIONS_LOG_VERBOSITY above (const
* char *).
*
- * - SSH_BIND_OPTIONS_DSAKEY:
- * Set the path to the ssh host dsa key, SSHv2
- * only (const char *).
- *
* - SSH_BIND_OPTIONS_RSAKEY:
- * Set the path to the ssh host rsa key, SSHv2
- * only (const char *).
+ * Deprecated alias to SSH_BIND_OPTIONS_HOSTKEY
+ * (const char *).
*
* - SSH_BIND_OPTIONS_ECDSAKEY:
- * Set the path to the ssh host ecdsa key,
- * SSHv2 only (const char *).
+ * Deprecated alias to SSH_BIND_OPTIONS_HOSTKEY
+ * (const char *).
*
* - SSH_BIND_OPTIONS_BANNER:
* Set the server banner sent to clients (const char *).
*
+ * - SSH_BIND_OPTIONS_DSAKEY:
+ * This is DEPRECATED, please do not use.
+ *
* - SSH_BIND_OPTIONS_IMPORT_KEY:
- * Set the Private Key for the server directly (ssh_key)
+ * Set the Private Key for the server directly
+ * (ssh_key). It will be free'd by ssh_bind_free().
*
* - SSH_BIND_OPTIONS_CIPHERS_C_S:
* Set the symmetric cipher client to server (const char *,
@@ -1655,11 +1980,22 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* possible algorithms is created from the list of keys
* set and then filtered against this list.
* (const char *, comma-separated list).
- *
+ *
* - SSH_BIND_OPTIONS_MODULI
* Set the path to the moduli file. Defaults to
* /etc/ssh/moduli if not specified (const char *).
*
+ * - SSH_BIND_OPTIONS_RSA_MIN_SIZE
+ * Set the minimum RSA key size in bits to be accepted by
+ * the server for both authentication and hostkey
+ * operations. The values under 768 bits are not accepted
+ * even with this configuration option as they are
+ * considered completely broken. Setting 0 will revert
+ * the value to defaults.
+ * Default is 1024 bits or 2048 bits in FIPS mode.
+ * (int)
+ *
+ *
* @param value The value to set. This is a generic pointer and the
* datatype which should be used is described at the
* corresponding value of type above.
@@ -1669,15 +2005,20 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
const void *value)
{
+ bool allowed;
char *p, *q;
const char *v;
int i, rc;
+ char **wanted_methods = sshbind->wanted_methods;
if (sshbind == NULL) {
return -1;
}
switch (type) {
+ case SSH_BIND_OPTIONS_RSAKEY:
+ case SSH_BIND_OPTIONS_ECDSAKEY:
+ /* deprecated */
case SSH_BIND_OPTIONS_HOSTKEY:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
@@ -1692,19 +2033,18 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
if (rc != SSH_OK) {
return -1;
}
+ allowed = ssh_bind_key_size_allowed(sshbind, key);
+ if (!allowed) {
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "The host key size %d is too small.",
+ ssh_key_size(key));
+ ssh_key_free(key);
+ return -1;
+ }
+
key_type = ssh_key_type(key);
switch (key_type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_DSA
- bind_key_loc = &sshbind->dsa;
- bind_key_path_loc = &sshbind->dsakey;
-#else
- ssh_set_error(sshbind,
- SSH_FATAL,
- "DSS key used and libssh compiled "
- "without DSA support");
-#endif
- break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
@@ -1723,9 +2063,9 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
bind_key_path_loc = &sshbind->rsakey;
break;
case SSH_KEYTYPE_ED25519:
- bind_key_loc = &sshbind->ed25519;
- bind_key_path_loc = &sshbind->ed25519key;
- break;
+ bind_key_loc = &sshbind->ed25519;
+ bind_key_path_loc = &sshbind->ed25519key;
+ break;
default:
ssh_set_error(sshbind,
SSH_FATAL,
@@ -1757,18 +2097,17 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_key *bind_key_loc = NULL;
ssh_key key = (ssh_key)value;
+ allowed = ssh_bind_key_size_allowed(sshbind, key);
+ if (!allowed) {
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "The host key size %d is too small.",
+ ssh_key_size(key));
+ return -1;
+ }
+
key_type = ssh_key_type(key);
switch (key_type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_DSA
- bind_key_loc = &sshbind->dsa;
-#else
- ssh_set_error(sshbind,
- SSH_FATAL,
- "DSA key used and libssh compiled "
- "without DSA support");
-#endif
- break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
@@ -1831,7 +2170,9 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
}
i = strtol(q, &p, 10);
if (q == p) {
- SAFE_FREE(q);
+ SSH_LOG(SSH_LOG_DEBUG, "No bind port was parsed");
+ SAFE_FREE(q);
+ return -1;
}
SAFE_FREE(q);
@@ -1858,31 +2199,15 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
}
i = strtol(q, &p, 10);
if (q == p) {
- SAFE_FREE(q);
+ SSH_LOG(SSH_LOG_DEBUG, "No log verbositiy was parsed");
+ SAFE_FREE(q);
+ return -1;
}
SAFE_FREE(q);
ssh_set_log_level(i & 0xffffU);
}
break;
- case SSH_BIND_OPTIONS_DSAKEY:
- rc = ssh_bind_set_key(sshbind, &sshbind->dsakey, value);
- if (rc < 0) {
- return -1;
- }
- break;
- case SSH_BIND_OPTIONS_RSAKEY:
- rc = ssh_bind_set_key(sshbind, &sshbind->rsakey, value);
- if (rc < 0) {
- return -1;
- }
- break;
- case SSH_BIND_OPTIONS_ECDSAKEY:
- rc = ssh_bind_set_key(sshbind, &sshbind->ecdsakey, value);
- if (rc < 0) {
- return -1;
- }
- break;
case SSH_BIND_OPTIONS_BANNER:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
@@ -1902,8 +2227,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_CRYPT_C_S, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_CRYPT_C_S,
+ v,
+ &wanted_methods[SSH_CRYPT_C_S]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_CIPHERS_S_C:
@@ -1912,8 +2242,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_CRYPT_S_C, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_CRYPT_S_C,
+ v,
+ &wanted_methods[SSH_CRYPT_S_C]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_KEY_EXCHANGE:
@@ -1922,7 +2257,10 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- rc = ssh_bind_set_algo(sshbind, SSH_KEX, v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_KEX,
+ v,
+ &wanted_methods[SSH_KEX]);
if (rc < 0) {
return -1;
}
@@ -1934,8 +2272,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_MAC_C_S, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_MAC_C_S,
+ v,
+ &wanted_methods[SSH_MAC_C_S]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_HMAC_S_C:
@@ -1944,8 +2287,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_MAC_S_C, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_MAC_S_C,
+ v,
+ &wanted_methods[SSH_MAC_S_C]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_CONFIG_DIR:
@@ -1970,20 +2318,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
- } else {
- p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
- }
- if (p == NULL) {
- ssh_set_error(sshbind, SSH_REQUEST_DENIED,
- "Setting method: no known public key algorithm (%s)",
- v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_HOSTKEYS,
+ v,
+ &sshbind->pubkey_accepted_key_types);
+ if (rc < 0) {
return -1;
}
-
- SAFE_FREE(sshbind->pubkey_accepted_key_types);
- sshbind->pubkey_accepted_key_types = p;
}
break;
case SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS:
@@ -1992,7 +2333,10 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- rc = ssh_bind_set_algo(sshbind, SSH_HOSTKEYS, v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_HOSTKEYS,
+ v,
+ &wanted_methods[SSH_HOSTKEYS]);
if (rc < 0) {
return -1;
}
@@ -2020,6 +2364,21 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
}
}
break;
+ case SSH_BIND_OPTIONS_RSA_MIN_SIZE:
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return -1;
+ } else {
+ int *x = (int *)value;
+ if (*x > 0 && *x < 768) {
+ ssh_set_error(sshbind, SSH_REQUEST_DENIED,
+ "The provided value (%u) for minimal RSA key "
+ "size is too small. Use at least 768 bits.", *x);
+ return -1;
+ }
+ sshbind->rsa_min_size = *x;
+ }
+ break;
default:
ssh_set_error(sshbind, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return -1;
@@ -2132,7 +2491,7 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
* @param sshbind SSH bind handle
*
* @param filename The options file to use; if NULL only the global
- * configuration is parsed and applied (if it haven't been
+ * configuration is parsed and applied (if it hasn't been
* processed before).
*
* @return 0 on success, < 0 on error.
diff --git a/src/packet.c b/src/packet.c
index 93591565..8508c731 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -113,7 +113,7 @@ static ssh_packet_callback default_packet_handlers[]= {
ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80
#else /* WITH_SERVER */
NULL,
-#endif /* WITH_SERVER */
+#endif /* WITH_SERVER */
ssh_request_success, // SSH2_MSG_REQUEST_SUCCESS 81
ssh_request_denied, // SSH2_MSG_REQUEST_FAILURE 82
NULL, NULL, NULL, NULL, NULL, NULL, NULL,// 83-89
@@ -363,9 +363,14 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* Transitions:
* - session->dh_handshake_state = DH_STATE_INIT_SENT
* then calls dh_handshake_server which triggers:
- * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+ * - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
+ if (!session->server) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
if (session->session_state != SSH_SESSION_STATE_DH) {
rc = SSH_PACKET_DENIED;
break;
@@ -391,7 +396,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* or dh_handshake_state == DH_STATE_REQUEST_SENT (dh-gex)
*
* Transitions:
- * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+ * - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
if (session->session_state != SSH_SESSION_STATE_DH) {
@@ -425,7 +430,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
- * - dh_hanshake_state == DH_STATE_FINISHED
+ * - dh_handshake_state == DH_STATE_FINISHED
*
* Transitions:
* - if authentication was successful:
@@ -454,7 +459,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
- * - dh_hanshake_state == DH_STATE_FINISHED
+ * - dh_handshake_state == DH_STATE_FINISHED
* - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
@@ -492,7 +497,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
- * - dh_hanshake_state == DH_STATE_FINISHED
+ * - dh_handshake_state == DH_STATE_FINISHED
* - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
* or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
@@ -1052,24 +1057,26 @@ static bool ssh_packet_need_rekey(ssh_session session,
* @param user pointer to current ssh_session
* @param data pointer to the data received
* @len length of data received. It might not be enough for a complete packet
- * @returns number of bytes read and processed.
+ * @returns number of bytes read and processed. Zero means only partial packet
+ * received and negative value means error.
*/
-int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
+size_t
+ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
{
ssh_session session = (ssh_session)user;
uint32_t blocksize = 8;
uint32_t lenfield_blocksize = 8;
size_t current_macsize = 0;
uint8_t *ptr = NULL;
- int to_be_read;
+ long to_be_read;
int rc;
uint8_t *cleartext_packet = NULL;
uint8_t *packet_second_block = NULL;
uint8_t *mac = NULL;
- size_t packet_remaining;
+ size_t packet_remaining, packet_offset;
uint32_t packet_len, compsize, payloadsize;
uint8_t padding;
- size_t processed = 0; /* number of byte processed from the callback */
+ size_t processed = 0; /* number of bytes processed from the callback */
enum ssh_packet_filter_result_e filter_result;
struct ssh_crypto_struct *crypto = NULL;
bool etm = false;
@@ -1112,7 +1119,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->packet_state == PACKET_STATE_PROCESSING ?
"PROCESSING" : "unknown");
#endif
- switch(session->packet_state) {
+ switch (session->packet_state) {
case PACKET_STATE_INIT:
if (receivedlen < lenfield_blocksize + etm_packet_offset) {
/*
@@ -1145,11 +1152,13 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
if (!etm) {
- ptr = ssh_buffer_allocate(session->in_buffer, lenfield_blocksize);
+ ptr = ssh_buffer_allocate(session->in_buffer,
+ lenfield_blocksize);
if (ptr == NULL) {
goto error;
}
- packet_len = ssh_packet_decrypt_len(session, ptr, (uint8_t *)data);
+ packet_len = ssh_packet_decrypt_len(session, ptr,
+ (uint8_t *)data);
to_be_read = packet_len - lenfield_blocksize + sizeof(uint32_t);
} else {
/* Length is unencrypted in case of Encrypt-then-MAC */
@@ -1161,7 +1170,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
if (packet_len > MAX_PACKET_LEN) {
ssh_set_error(session,
SSH_FATAL,
- "read_packet(): Packet len too high(%u %.4x)",
+ "read_packet(): Packet len too high(%" PRIu32 " %.4" PRIx32 ")",
packet_len, packet_len);
goto error;
}
@@ -1169,7 +1178,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
/* remote sshd sends invalid sizes? */
ssh_set_error(session,
SSH_FATAL,
- "Given numbers of bytes left to be read < 0 (%d)!",
+ "Given numbers of bytes left to be read < 0 (%ld)!",
to_be_read);
goto error;
}
@@ -1179,28 +1188,27 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
FALL_THROUGH;
case PACKET_STATE_SIZEREAD:
packet_len = session->in_packet.len;
- processed = lenfield_blocksize + etm_packet_offset;
+ packet_offset = processed = lenfield_blocksize + etm_packet_offset;
to_be_read = packet_len + sizeof(uint32_t) + current_macsize;
/* if to_be_read is zero, the whole packet was blocksize bytes. */
if (to_be_read != 0) {
- if (receivedlen < (unsigned int)to_be_read) {
+ if (receivedlen < (unsigned long)to_be_read) {
/* give up, not enough data in buffer */
SSH_LOG(SSH_LOG_PACKET,
"packet: partial packet (read len) "
- "[len=%d, receivedlen=%d, to_be_read=%d]",
+ "[len=%" PRIu32 ", receivedlen=%zu, to_be_read=%ld]",
packet_len,
- (int)receivedlen,
+ receivedlen,
to_be_read);
return 0;
}
- packet_second_block = (uint8_t*)data + lenfield_blocksize + etm_packet_offset;
+ packet_second_block = (uint8_t*)data + packet_offset;
processed = to_be_read - current_macsize;
}
/* remaining encrypted bytes from the packet, MAC not included */
- packet_remaining =
- packet_len - (lenfield_blocksize - sizeof(uint32_t) + etm_packet_offset);
+ packet_remaining = packet_len - (packet_offset - sizeof(uint32_t));
cleartext_packet = ssh_buffer_allocate(session->in_buffer,
packet_remaining);
if (cleartext_packet == NULL) {
@@ -1223,16 +1231,16 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
}
/*
- * Decrypt the packet. In case of EtM mode, the length is already
- * known as it's unencrypted. In the other case, lenfield_blocksize bytes
- * already have been decrypted.
+ * Decrypt the packet. In case of EtM mode, the length is
+ * already known as it's unencrypted. In the other case,
+ * lenfield_blocksize bytes already have been decrypted.
*/
if (packet_remaining > 0) {
rc = ssh_packet_decrypt(session,
cleartext_packet,
(uint8_t *)data,
- lenfield_blocksize + etm_packet_offset,
- processed - (lenfield_blocksize + etm_packet_offset));
+ packet_offset,
+ processed - packet_offset);
if (rc < 0) {
ssh_set_error(session,
SSH_FATAL,
@@ -1242,9 +1250,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
if (crypto->in_hmac != SSH_HMAC_NONE && !etm) {
+ ssh_buffer in = session->in_buffer;
rc = ssh_packet_hmac_verify(session,
- ssh_buffer_get(session->in_buffer),
- ssh_buffer_get_len(session->in_buffer),
+ ssh_buffer_get(in),
+ ssh_buffer_get_len(in),
mac,
crypto->in_hmac);
if (rc < 0) {
@@ -1286,7 +1295,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
if (padding > ssh_buffer_get_len(session->in_buffer)) {
ssh_set_error(session,
SSH_FATAL,
- "Invalid padding: %d (%d left)",
+ "Invalid padding: %d (%" PRIu32 " left)",
padding,
ssh_buffer_get_len(session->in_buffer));
goto error;
@@ -1295,15 +1304,29 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
compsize = ssh_buffer_get_len(session->in_buffer);
#ifdef WITH_ZLIB
- if (crypto && crypto->do_compress_in
- && ssh_buffer_get_len(session->in_buffer) > 0) {
- rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN);
+ if (crypto && crypto->do_compress_in &&
+ ssh_buffer_get_len(session->in_buffer) > 0) {
+ rc = decompress_buffer(session, session->in_buffer,
+ MAX_PACKET_LEN);
if (rc < 0) {
goto error;
}
}
#endif /* WITH_ZLIB */
payloadsize = ssh_buffer_get_len(session->in_buffer);
+ if (session->recv_seq == UINT32_MAX) {
+ /* Overflowing sequence numbers is always fishy */
+ if (crypto == NULL) {
+ /* don't allow sequence number overflow when unencrypted */
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Incoming sequence number overflow");
+ goto error;
+ } else {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Incoming sequence number overflow");
+ }
+ }
session->recv_seq++;
if (crypto != NULL) {
struct ssh_cipher_struct *cipher = NULL;
@@ -1324,13 +1347,27 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->packet_state = PACKET_STATE_PROCESSING;
ssh_packet_parse_type(session);
SSH_LOG(SSH_LOG_PACKET,
- "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]",
- session->in_packet.type, packet_len, padding, compsize, payloadsize);
+ "packet: read type %hhd [len=%" PRIu32 ",padding=%hhd,"
+ "comp=%" PRIu32 ",payload=%" PRIu32 "]",
+ session->in_packet.type, packet_len, padding, compsize,
+ payloadsize);
+ if (crypto == NULL) {
+ /* In strict kex, only a few packets are allowed. Taint the session
+ * if we received packets that are normally allowed but to be
+ * refused if we are in strict kex when KEX is over.
+ */
+ uint8_t type = session->in_packet.type;
+ if (type != SSH2_MSG_KEXINIT && type != SSH2_MSG_NEWKEYS &&
+ (type < SSH2_MSG_KEXDH_INIT ||
+ type > SSH2_MSG_KEX_DH_GEX_REQUEST)) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
+ }
/* Check if the packet is expected */
filter_result = ssh_packet_incoming_filter(session);
- switch(filter_result) {
+ switch (filter_result) {
case SSH_PACKET_ALLOWED:
/* Execute callbacks */
ssh_packet_process(session, session->in_packet.type);
@@ -1342,6 +1379,9 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->in_packet.type);
goto error;
case SSH_PACKET_UNKNOWN:
+ if (crypto == NULL) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
ssh_packet_send_unimplemented(session, session->recv_seq - 1);
break;
}
@@ -1355,7 +1395,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
ptr = ((uint8_t*)data) + processed;
- rc = ssh_packet_socket_callback(ptr, receivedlen - processed,user);
+ rc = ssh_packet_socket_callback(ptr, receivedlen - processed,
+ user);
processed += rc;
}
@@ -1381,8 +1422,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->packet_state);
error:
- session->session_state= SSH_SESSION_STATE_ERROR;
- SSH_LOG(SSH_LOG_PACKET,"Packet: processed %zu bytes", processed);
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ SSH_LOG(SSH_LOG_PACKET, "Packet: processed %zu bytes", processed);
return processed;
}
@@ -1410,18 +1451,23 @@ static void ssh_packet_socket_controlflow_callback(int code, void *userdata)
}
}
-void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){
- session->socket_callbacks.data=ssh_packet_socket_callback;
- session->socket_callbacks.connected=NULL;
- session->socket_callbacks.controlflow = ssh_packet_socket_controlflow_callback;
- session->socket_callbacks.userdata=session;
- ssh_socket_set_callbacks(s,&session->socket_callbacks);
+void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s)
+{
+ struct ssh_socket_callbacks_struct *callbacks = &session->socket_callbacks;
+
+ callbacks->data = ssh_packet_socket_callback;
+ callbacks->connected = NULL;
+ callbacks->controlflow = ssh_packet_socket_controlflow_callback;
+ callbacks->userdata = session;
+ ssh_socket_set_callbacks(s, callbacks);
}
/** @internal
* @brief sets the callbacks for the packet layer
*/
-void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
+void
+ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks)
+{
if (session->packet_callbacks == NULL) {
session->packet_callbacks = ssh_list_new();
if (session->packet_callbacks == NULL) {
@@ -1435,8 +1481,11 @@ void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callback
/** @internal
* @brief remove the callbacks from the packet layer
*/
-void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
+void
+ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks)
+{
struct ssh_iterator *it = NULL;
+
it = ssh_list_find(session->packet_callbacks, callbacks);
if (it != NULL) {
ssh_list_remove(session->packet_callbacks, it);
@@ -1446,12 +1495,15 @@ void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callb
/** @internal
* @brief sets the default packet handlers
*/
-void ssh_packet_set_default_callbacks(ssh_session session){
- session->default_packet_callbacks.start=1;
- session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback);
- session->default_packet_callbacks.user=session;
- session->default_packet_callbacks.callbacks=default_packet_handlers;
- ssh_packet_set_callbacks(session, &session->default_packet_callbacks);
+void ssh_packet_set_default_callbacks(ssh_session session)
+{
+ struct ssh_packet_callbacks_struct *c = &session->default_packet_callbacks;
+
+ c->start = 1;
+ c->n_callbacks = sizeof(default_packet_handlers) / sizeof(ssh_packet_callback);
+ c->user = session;
+ c->callbacks = default_packet_handlers;
+ ssh_packet_set_callbacks(session, c);
}
/** @internal
@@ -1505,7 +1557,33 @@ void ssh_packet_process(ssh_session session, uint8_t type)
SSH_LOG(SSH_LOG_RARE, "Failed to send unimplemented: %s",
ssh_get_error(session));
}
+ if (session->current_crypto == NULL) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
+ }
+}
+
+/** @internal
+ * @brief sends a SSH_MSG_NEWKEYS when enabling the new negotiated ciphers
+ * @param session the SSH session
+ * @return SSH_ERROR on error, else SSH_OK
+ */
+int ssh_packet_send_newkeys(ssh_session session)
+{
+ int rc;
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
+ if (rc < 0) {
+ return rc;
+ }
+
+ rc = ssh_packet_send(session);
+ if (rc == SSH_ERROR) {
+ return rc;
}
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
+ return rc;
}
/** @internal
@@ -1543,12 +1621,12 @@ SSH_PACKET_CALLBACK(ssh_packet_unimplemented){
rc = ssh_buffer_unpack(packet, "d", &seq);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not unpack SSH_MSG_UNIMPLEMENTED packet");
}
SSH_LOG(SSH_LOG_RARE,
- "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq);
+ "Received SSH_MSG_UNIMPLEMENTED (sequence number %" PRIu32 ")",seq);
return SSH_PACKET_USED;
}
@@ -1714,8 +1792,8 @@ static int packet_send2(ssh_session session)
}
SSH_LOG(SSH_LOG_PACKET,
- "packet: wrote [type=%u, len=%u, padding_size=%hhd, comp=%u, "
- "payload=%u]",
+ "packet: wrote [type=%u, len=%" PRIu32 ", padding_size=%hhd, comp=%" PRIu32 ", "
+ "payload=%" PRIu32 "]",
type,
finallen,
padding_size,
@@ -1755,10 +1833,12 @@ static bool
ssh_packet_in_rekey(ssh_session session)
{
/* We know we are rekeying if we are authenticated and the DH
- * status is not finished
+ * status is not finished, but we only queue packets until we've
+ * sent our NEWKEYS.
*/
return (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) &&
- (session->dh_handshake_state != DH_STATE_FINISHED);
+ (session->dh_handshake_state != DH_STATE_FINISHED) &&
+ (session->dh_handshake_state != DH_STATE_NEWKEYS_SENT);
}
int ssh_packet_send(ssh_session session)
@@ -1799,7 +1879,7 @@ int ssh_packet_send(ssh_session session)
if (need_rekey) {
/* Send the KEXINIT packet instead.
- * This recursivelly calls the packet_send(), but it should
+ * This recursively calls the packet_send(), but it should
* not get into rekeying again.
* After that we need to handle the key exchange responses
* up to the point where we can send the rest of the queue.
@@ -1816,6 +1896,10 @@ int ssh_packet_send(ssh_session session)
if (rc == SSH_OK && type == SSH2_MSG_NEWKEYS) {
struct ssh_iterator *it;
+ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) {
+ /* reset packet sequence number when running in strict kex mode */
+ session->send_seq = 0;
+ }
for (it = ssh_list_get_iterator(session->out_queue);
it != NULL;
it = ssh_list_get_iterator(session->out_queue)) {
@@ -1867,7 +1951,7 @@ ssh_init_rekey_state(struct ssh_session_struct *session,
session->opts.rekey_data / cipher->blocksize);
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Set rekey after %" PRIu64 " blocks",
cipher->max_blocks);
}
@@ -1895,7 +1979,7 @@ ssh_packet_set_newkeys(ssh_session session,
session->next_crypto->used |= direction;
if (session->current_crypto != NULL) {
if (session->current_crypto->used & direction) {
- SSH_LOG(SSH_LOG_WARNING, "This direction isn't used anymore.");
+ SSH_LOG(SSH_LOG_TRACE, "This direction isn't used anymore.");
}
/* Mark the current requested direction unused */
session->current_crypto->used &= ~direction;
@@ -1903,7 +1987,7 @@ ssh_packet_set_newkeys(ssh_session session,
/* Both sides switched: do the actual switch now */
if (session->next_crypto->used == SSH_DIRECTION_BOTH) {
- size_t digest_len;
+ size_t session_id_len;
if (session->current_crypto != NULL) {
crypto_free(session->current_crypto);
@@ -1920,8 +2004,8 @@ ssh_packet_set_newkeys(ssh_session session,
return SSH_ERROR;
}
- digest_len = session->current_crypto->digest_len;
- session->next_crypto->session_id = malloc(digest_len);
+ session_id_len = session->current_crypto->session_id_len;
+ session->next_crypto->session_id = malloc(session_id_len);
if (session->next_crypto->session_id == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
@@ -1929,7 +2013,8 @@ ssh_packet_set_newkeys(ssh_session session,
memcpy(session->next_crypto->session_id,
session->current_crypto->session_id,
- digest_len);
+ session_id_len);
+ session->next_crypto->session_id_len = session_id_len;
return SSH_OK;
}
@@ -1968,7 +2053,7 @@ ssh_packet_set_newkeys(ssh_session session,
ssh_init_rekey_state(session, in_cipher);
if (session->opts.rekey_time != 0) {
ssh_timestamp_init(&session->last_rekey_time);
- SSH_LOG(SSH_LOG_PROTOCOL, "Set rekey after %" PRIu32 " seconds",
+ SSH_LOG(SSH_LOG_DEBUG, "Set rekey after %" PRIu32 " seconds",
session->opts.rekey_time/1000);
}
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 39575b17..7edb6791 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -45,36 +45,48 @@
*
* @brief Handle a SSH_DISCONNECT packet.
*/
-SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){
- int rc;
- uint32_t code = 0;
- char *error = NULL;
- ssh_string error_s;
- (void)user;
- (void)type;
-
- rc = ssh_buffer_get_u32(packet, &code);
- if (rc != 0) {
- code = ntohl(code);
- }
-
- 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_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",
- code, error != NULL ? error : "no error");
- ssh_set_error(session, SSH_FATAL,
- "Received SSH_MSG_DISCONNECT: %d:%s",
- code, error != NULL ? error : "no error");
- SAFE_FREE(error);
-
- ssh_socket_close(session->socket);
- session->alive = 0;
- session->session_state = SSH_SESSION_STATE_ERROR;
- /* TODO: handle a graceful disconnect */
- return SSH_PACKET_USED;
+SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback)
+{
+ int rc;
+ uint32_t code = 0;
+ char *error = NULL;
+ ssh_string error_s = NULL;
+
+ (void)user;
+ (void)type;
+
+ rc = ssh_buffer_get_u32(packet, &code);
+ if (rc != 0) {
+ code = ntohl(code);
+ }
+
+ error_s = ssh_buffer_get_ssh_string(packet);
+ if (error_s != NULL) {
+ error = ssh_string_to_char(error_s);
+ SSH_STRING_FREE(error_s);
+ }
+
+ if (error != NULL) {
+ session->peer_discon_msg = strdup(error);
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received SSH_MSG_DISCONNECT %" PRIu32 ":%s",
+ code,
+ error != NULL ? error : "no error");
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Received SSH_MSG_DISCONNECT: %" PRIu32 ":%s",
+ code,
+ error != NULL ? error : "no error");
+ SAFE_FREE(error);
+
+ ssh_session_socket_close(session);
+ /* correctly handle disconnect during authorization */
+ session->auth.state = SSH_AUTH_STATE_FAILED;
+
+ /* TODO: handle a graceful disconnect */
+ return SSH_PACKET_USED;
}
/**
@@ -82,99 +94,125 @@ SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){
*
* @brief Handle a SSH_IGNORE and SSH_DEBUG packet.
*/
-SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){
+SSH_PACKET_CALLBACK(ssh_packet_ignore_callback)
+{
(void)session; /* unused */
- (void)user;
- (void)type;
- (void)packet;
- SSH_LOG(SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG");
- /* TODO: handle a graceful disconnect */
- return SSH_PACKET_USED;
+ (void)user;
+ (void)type;
+ (void)packet;
+
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Received %s packet",
+ type == SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG");
+
+ /* TODO: handle a graceful disconnect */
+ return SSH_PACKET_USED;
}
-SSH_PACKET_CALLBACK(ssh_packet_newkeys){
- ssh_string sig_blob = NULL;
- ssh_signature sig = NULL;
- int rc;
- (void)packet;
- (void)user;
- (void)type;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS");
-
- if (session->session_state != SSH_SESSION_STATE_DH ||
- session->dh_handshake_state != DH_STATE_NEWKEYS_SENT) {
- ssh_set_error(session,
- SSH_FATAL,
- "ssh_packet_newkeys called in wrong state : %d:%d",
- session->session_state,session->dh_handshake_state);
- goto error;
- }
-
- if(session->server){
- /* server things are done in server.c */
- session->dh_handshake_state=DH_STATE_FINISHED;
- } else {
- ssh_key server_key;
-
- /* client */
-
- /* Verify the host's signature. FIXME do it sooner */
- sig_blob = session->next_crypto->dh_server_signature;
- session->next_crypto->dh_server_signature = NULL;
-
- /* get the server public key */
- server_key = ssh_dh_get_next_server_publickey(session);
- if (server_key == NULL) {
- goto error;
- }
+SSH_PACKET_CALLBACK(ssh_packet_newkeys)
+{
+ ssh_string sig_blob = NULL;
+ ssh_signature sig = NULL;
+ int rc;
- rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
- ssh_string_burn(sig_blob);
- SSH_STRING_FREE(sig_blob);
- if (rc != SSH_OK) {
+ (void)packet;
+ (void)user;
+ (void)type;
+
+ SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_NEWKEYS");
+
+ if (session->session_state != SSH_SESSION_STATE_DH ||
+ session->dh_handshake_state != DH_STATE_NEWKEYS_SENT) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "ssh_packet_newkeys called in wrong state : %d:%d",
+ session->session_state,
+ session->dh_handshake_state);
goto error;
}
- /* Check if signature from server matches user preferences */
- if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
- if (!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
- sig->type_c)) {
+ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) {
+ /* reset packet sequence number when running in strict kex mode */
+ session->recv_seq = 0;
+ /* Check that we aren't tainted */
+ if (session->flags & SSH_SESSION_FLAG_KEX_TAINTED) {
ssh_set_error(session,
SSH_FATAL,
- "Public key from server (%s) doesn't match user "
- "preference (%s)",
- sig->type_c,
- session->opts.wanted_methods[SSH_HOSTKEYS]);
+ "Received unexpected packets in strict KEX mode.");
goto error;
}
}
- rc = ssh_pki_signature_verify(session,
- sig,
- server_key,
- session->next_crypto->secret_hash,
- session->next_crypto->digest_len);
- SSH_SIGNATURE_FREE(sig);
- if (rc == SSH_ERROR) {
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,"Signature verified and valid");
+ if (session->server) {
+ /* server things are done in server.c */
+ session->dh_handshake_state=DH_STATE_FINISHED;
+ } else {
+ ssh_key server_key = NULL;
- /* When receiving this packet, we switch on the incomming crypto. */
- rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
- if (rc != SSH_OK) {
- goto error;
+ /* client */
+
+ /* Verify the host's signature. FIXME do it sooner */
+ sig_blob = session->next_crypto->dh_server_signature;
+ session->next_crypto->dh_server_signature = NULL;
+
+ /* get the server public key */
+ server_key = ssh_dh_get_next_server_publickey(session);
+ if (server_key == NULL) {
+ goto error;
+ }
+
+ rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
+ ssh_string_burn(sig_blob);
+ SSH_STRING_FREE(sig_blob);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ /* Check if signature from server matches user preferences */
+ if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
+ rc = ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
+ sig->type_c);
+ if (rc == 0) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Public key from server (%s) doesn't match user "
+ "preference (%s)",
+ sig->type_c,
+ session->opts.wanted_methods[SSH_HOSTKEYS]);
+ goto error;
+ }
+ }
+
+ rc = ssh_pki_signature_verify(session,
+ sig,
+ server_key,
+ session->next_crypto->secret_hash,
+ session->next_crypto->digest_len);
+ SSH_SIGNATURE_FREE(sig);
+ if (rc == SSH_ERROR) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to verify server hostkey signature");
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Signature verified and valid");
+
+ /* When receiving this packet, we switch on the incoming crypto. */
+ rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
- }
- session->dh_handshake_state = DH_STATE_FINISHED;
- session->ssh_connection_callback(session);
- return SSH_PACKET_USED;
+ session->dh_handshake_state = DH_STATE_FINISHED;
+ session->ssh_connection_callback(session);
+ return SSH_PACKET_USED;
+
error:
- SSH_SIGNATURE_FREE(sig);
- ssh_string_burn(sig_blob);
- SSH_STRING_FREE(sig_blob);
- session->session_state = SSH_SESSION_STATE_ERROR;
- return SSH_PACKET_USED;
+ SSH_SIGNATURE_FREE(sig);
+ ssh_string_burn(sig_blob);
+ SSH_STRING_FREE(sig_blob);
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ return SSH_PACKET_USED;
}
/**
@@ -182,16 +220,16 @@ error:
* @brief handles a SSH_SERVICE_ACCEPT packet
*
*/
-SSH_PACKET_CALLBACK(ssh_packet_service_accept){
- (void)packet;
- (void)type;
- (void)user;
+SSH_PACKET_CALLBACK(ssh_packet_service_accept)
+{
+ (void)packet;
+ (void)type;
+ (void)user;
session->auth.service_state = SSH_AUTH_SERVICE_ACCEPTED;
- SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_MSG_SERVICE_ACCEPT");
+ SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_SERVICE_ACCEPT");
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
/**
@@ -204,6 +242,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
int rc;
uint32_t nr_extensions = 0;
uint32_t i;
+
(void)type;
(void)user;
@@ -221,7 +260,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
return SSH_PACKET_USED;
}
- SSH_LOG(SSH_LOG_PACKET, "Follows %u extensions", nr_extensions);
+ SSH_LOG(SSH_LOG_PACKET, "Follows %" PRIu32 " extensions", nr_extensions);
for (i = 0; i < nr_extensions; i++) {
char *name = NULL;
@@ -244,6 +283,8 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
if (ssh_match_group(value, "rsa-sha2-256")) {
session->extensions |= SSH_EXT_SIG_RSA_SHA256;
}
+ } else {
+ SSH_LOG(SSH_LOG_PACKET, "Unknown extension: %s", name);
}
free(name);
free(value);
diff --git a/src/packet_crypt.c b/src/packet_crypt.c
index 734ccafc..fe3f489e 100644
--- a/src/packet_crypt.c
+++ b/src/packet_crypt.c
@@ -130,14 +130,15 @@ int ssh_packet_decrypt(ssh_session session,
return 0;
}
-unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
+unsigned char *ssh_packet_encrypt(ssh_session session, void *data, size_t len)
{
struct ssh_crypto_struct *crypto = NULL;
struct ssh_cipher_struct *cipher = NULL;
HMACCTX ctx = NULL;
char *out = NULL;
- int etm_packet_offset = 0;
- unsigned int finallen, blocksize;
+ int etm_packet_offset = 0, rc;
+ unsigned int blocksize;
+ size_t finallen = DIGEST_MAX_LEN;
uint32_t seq, lenfield_blocksize;
enum ssh_hmac_e type;
bool etm;
@@ -161,7 +162,7 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
if ((len - lenfield_blocksize - etm_packet_offset) % blocksize != 0) {
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set"
- " on at least one blocksize (received %d)", len);
+ " on at least one blocksize (received %zu)", len);
return NULL;
}
out = calloc(1, len);
@@ -185,9 +186,21 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
}
if (!etm) {
- hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
- hmac_update(ctx, data, len);
- hmac_final(ctx, crypto->hmacbuf, &finallen);
+ rc = hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_update(ctx, data, len);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_final(ctx, crypto->hmacbuf, &finallen);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
}
}
@@ -197,14 +210,26 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
if (type != SSH_HMAC_NONE) {
if (etm) {
PUSH_BE_U32(data, 0, len - etm_packet_offset);
- hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
- hmac_update(ctx, data, len);
- hmac_final(ctx, crypto->hmacbuf, &finallen);
+ rc = hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_update(ctx, data, len);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_final(ctx, crypto->hmacbuf, &finallen);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
}
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("mac: ", data, len);
if (finallen != hmac_digest_len(type)) {
- printf("Final len is %d\n", finallen);
+ printf("Final len is %zu\n", finallen);
}
ssh_log_hexdump("Packet hmac", crypto->hmacbuf, hmac_digest_len(type));
#endif
@@ -235,42 +260,71 @@ int ssh_packet_hmac_verify(ssh_session session,
uint8_t *mac,
enum ssh_hmac_e type)
{
- struct ssh_crypto_struct *crypto = NULL;
- unsigned char hmacbuf[DIGEST_MAX_LEN] = {0};
- HMACCTX ctx;
- unsigned int hmaclen;
- uint32_t seq;
-
- /* AEAD types have no mac checking */
- if (type == SSH_HMAC_AEAD_POLY1305 ||
- type == SSH_HMAC_AEAD_GCM) {
- return SSH_OK;
- }
+ struct ssh_crypto_struct *crypto = NULL;
+ unsigned char hmacbuf[DIGEST_MAX_LEN] = {0};
+ HMACCTX ctx;
+ size_t hmaclen = DIGEST_MAX_LEN;
+ uint32_t seq;
+ int cmp;
+ int rc;
- crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
- if (crypto == NULL) {
- return SSH_ERROR;
- }
+ /* AEAD types have no mac checking */
+ if (type == SSH_HMAC_AEAD_POLY1305 ||
+ type == SSH_HMAC_AEAD_GCM) {
+ return SSH_OK;
+ }
- ctx = hmac_init(crypto->decryptMAC, hmac_digest_len(type), type);
- if (ctx == NULL) {
- return -1;
- }
+ crypto = ssh_packet_get_current_crypto(session,
+ SSH_DIRECTION_IN);
+ if (crypto == NULL) {
+ return SSH_ERROR;
+ }
- seq = htonl(session->recv_seq);
+ ctx = hmac_init(crypto->decryptMAC,
+ hmac_digest_len(type),
+ type);
+ if (ctx == NULL) {
+ return SSH_ERROR;
+ }
- hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t));
- hmac_update(ctx, data, len);
- hmac_final(ctx, hmacbuf, &hmaclen);
+ seq = htonl(session->recv_seq);
+
+ rc = hmac_update(ctx,
+ (unsigned char *) &seq,
+ sizeof(uint32_t));
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ rc = hmac_update(ctx,
+ data,
+ len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ rc = hmac_final(ctx,
+ hmacbuf,
+ &hmaclen);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
#ifdef DEBUG_CRYPTO
- ssh_log_hexdump("received mac",mac,hmaclen);
- ssh_log_hexdump("Computed mac",hmacbuf,hmaclen);
- ssh_log_hexdump("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;
- }
+ cmp = secure_memcmp(mac,
+ hmacbuf,
+ hmaclen);
+ if (cmp == 0) {
+ return SSH_OK;
+ }
- return -1;
+ return SSH_ERROR;
}
diff --git a/src/pcap.c b/src/pcap.c
index c089854a..3d295427 100644
--- a/src/pcap.c
+++ b/src/pcap.c
@@ -44,14 +44,11 @@
#include "libssh/socket.h"
/**
- * @internal
- *
* @defgroup libssh_pcap The libssh pcap functions
* @ingroup libssh
*
* The pcap file generation
*
- *
* @{
*/
@@ -63,7 +60,7 @@ struct pcap_hdr_s {
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
- int32_t thiszone; /* GMT to local correction */
+ int32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
@@ -98,12 +95,11 @@ struct pcaprec_hdr_s {
* in a SSH session only. Multiple pcap contexts may be used into
* a single pcap file.
*/
-
struct ssh_pcap_context_struct {
ssh_session session;
ssh_pcap_file file;
int connected;
- /* All of these information are useful to generate
+ /* All of this information is useful to generate
* the dummy IP and TCP packets
*/
uint32_t ipsource;
@@ -126,10 +122,11 @@ struct ssh_pcap_file_struct {
/**
* @brief create a new ssh_pcap_file object
*/
-ssh_pcap_file ssh_pcap_file_new(void) {
- struct ssh_pcap_file_struct *pcap;
+ssh_pcap_file ssh_pcap_file_new(void)
+{
+ struct ssh_pcap_file_struct *pcap = NULL;
- pcap = (struct ssh_pcap_file_struct *) malloc(sizeof(struct ssh_pcap_file_struct));
+ pcap = malloc(sizeof(struct ssh_pcap_file_struct));
if (pcap == NULL) {
return NULL;
}
@@ -141,163 +138,182 @@ ssh_pcap_file ssh_pcap_file_new(void) {
/** @internal
* @brief writes a packet on file
*/
-static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet){
- int err;
- uint32_t len;
- if(pcap == NULL || pcap->output==NULL)
- return SSH_ERROR;
- len=ssh_buffer_get_len(packet);
- err=fwrite(ssh_buffer_get(packet),len,1,pcap->output);
- if(err<0)
- return SSH_ERROR;
- else
- return SSH_OK;
+static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet)
+{
+ int err;
+ uint32_t len;
+ if (pcap == NULL || pcap->output == NULL) {
+ return SSH_ERROR;
+ }
+ len = ssh_buffer_get_len(packet);
+ err = fwrite(ssh_buffer_get(packet), len, 1, pcap->output);
+ if (err < 0) {
+ return SSH_ERROR;
+ } else {
+ return SSH_OK;
+ }
}
/** @internal
* @brief prepends a packet with the pcap header and writes packet
* on file
*/
-int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len){
- ssh_buffer header=ssh_buffer_new();
- struct timeval now;
- int err;
- if(header == NULL)
- return SSH_ERROR;
- gettimeofday(&now,NULL);
+int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len)
+{
+ ssh_buffer header = ssh_buffer_new();
+ struct timeval now;
+ int err;
+
+ if (header == NULL) {
+ return SSH_ERROR;
+ }
+
+ gettimeofday(&now, NULL);
err = ssh_buffer_allocate_size(header,
sizeof(uint32_t) * 4 +
ssh_buffer_get_len(packet));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(now.tv_sec));
+ err = ssh_buffer_add_u32(header, htonl(now.tv_sec));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(now.tv_usec));
+ err = ssh_buffer_add_u32(header, htonl(now.tv_usec));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(ssh_buffer_get_len(packet)));
+ err = ssh_buffer_add_u32(header, htonl(ssh_buffer_get_len(packet)));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(original_len));
+ err = ssh_buffer_add_u32(header, htonl(original_len));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_buffer(header,packet);
+ err = ssh_buffer_add_buffer(header, packet);
if (err < 0) {
goto error;
}
- err=ssh_pcap_file_write(pcap,header);
+ err = ssh_pcap_file_write(pcap, header);
error:
- SSH_BUFFER_FREE(header);
- return err;
+ SSH_BUFFER_FREE(header);
+ return err;
}
/**
- * @brief opens a new pcap file and create header
+ * @brief opens a new pcap file and creates header
*/
-int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){
- ssh_buffer header;
- int err;
- if(pcap == NULL)
- return SSH_ERROR;
- if(pcap->output){
- fclose(pcap->output);
- pcap->output=NULL;
- }
- pcap->output=fopen(filename,"wb");
- if(pcap->output==NULL)
- return SSH_ERROR;
- header=ssh_buffer_new();
- if(header==NULL)
- return SSH_ERROR;
+int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename)
+{
+ ssh_buffer header = NULL;
+ int err;
+
+ if (pcap == NULL) {
+ return SSH_ERROR;
+ }
+ if (pcap->output) {
+ fclose(pcap->output);
+ pcap->output = NULL;
+ }
+ pcap->output = fopen(filename, "wb");
+ if (pcap->output == NULL) {
+ return SSH_ERROR;
+ }
+ header = ssh_buffer_new();
+ if (header == NULL) {
+ return SSH_ERROR;
+ }
err = ssh_buffer_allocate_size(header,
sizeof(uint32_t) * 5 +
sizeof(uint16_t) * 2);
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(PCAP_MAGIC));
+ err = ssh_buffer_add_u32(header, htonl(PCAP_MAGIC));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u16(header,htons(PCAP_VERSION_MAJOR));
+ err = ssh_buffer_add_u16(header, htons(PCAP_VERSION_MAJOR));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u16(header,htons(PCAP_VERSION_MINOR));
+ err = ssh_buffer_add_u16(header, htons(PCAP_VERSION_MINOR));
if (err < 0) {
goto error;
}
- /* currently hardcode GMT to 0 */
- err = ssh_buffer_add_u32(header,htonl(0));
+ /* currently hardcode GMT to 0 */
+ err = ssh_buffer_add_u32(header, htonl(0));
if (err < 0) {
goto error;
}
- /* accuracy */
- err = ssh_buffer_add_u32(header,htonl(0));
+ /* accuracy */
+ err = ssh_buffer_add_u32(header, htonl(0));
if (err < 0) {
goto error;
}
- /* size of the biggest packet */
- err = ssh_buffer_add_u32(header,htonl(MAX_PACKET_LEN));
+ /* size of the biggest packet */
+ err = ssh_buffer_add_u32(header, htonl(MAX_PACKET_LEN));
if (err < 0) {
goto error;
}
- /* we will write sort-of IP */
- err = ssh_buffer_add_u32(header,htonl(DLT_RAW));
+ /* we will write sort-of IP */
+ err = ssh_buffer_add_u32(header, htonl(DLT_RAW));
if (err < 0) {
goto error;
}
- err=ssh_pcap_file_write(pcap,header);
+ err = ssh_pcap_file_write(pcap,header);
error:
- SSH_BUFFER_FREE(header);
- return err;
+ SSH_BUFFER_FREE(header);
+ return err;
}
-int ssh_pcap_file_close(ssh_pcap_file pcap){
- int err;
- if(pcap ==NULL || pcap->output==NULL)
- return SSH_ERROR;
- err=fclose(pcap->output);
- pcap->output=NULL;
- if(err != 0)
- return SSH_ERROR;
- else
- return SSH_OK;
+int ssh_pcap_file_close(ssh_pcap_file pcap)
+{
+ int err;
+
+ if (pcap == NULL || pcap->output == NULL) {
+ return SSH_ERROR;
+ }
+ err = fclose(pcap->output);
+ pcap->output = NULL;
+ if (err != 0) {
+ return SSH_ERROR;
+ } else {
+ return SSH_OK;
+ }
}
-void ssh_pcap_file_free(ssh_pcap_file pcap){
- ssh_pcap_file_close(pcap);
- SAFE_FREE(pcap);
+void ssh_pcap_file_free(ssh_pcap_file pcap)
+{
+ ssh_pcap_file_close(pcap);
+ SAFE_FREE(pcap);
}
/** @internal
* @brief allocates a new ssh_pcap_context object
*/
-
-ssh_pcap_context ssh_pcap_context_new(ssh_session session){
- ssh_pcap_context ctx = (struct ssh_pcap_context_struct *) malloc(sizeof(struct ssh_pcap_context_struct));
- if(ctx==NULL){
- ssh_set_error_oom(session);
- return NULL;
- }
- ZERO_STRUCTP(ctx);
- ctx->session=session;
- return ctx;
+ssh_pcap_context ssh_pcap_context_new(ssh_session session)
+{
+ ssh_pcap_context ctx = (struct ssh_pcap_context_struct *)malloc(sizeof(struct ssh_pcap_context_struct));
+ if (ctx == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ ZERO_STRUCTP(ctx);
+ ctx->session = session;
+ return ctx;
}
-void ssh_pcap_context_free(ssh_pcap_context ctx){
- SAFE_FREE(ctx);
+void ssh_pcap_context_free(ssh_pcap_context ctx)
+{
+ SAFE_FREE(ctx);
}
-void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap){
- ctx->file=pcap;
+void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap)
+{
+ ctx->file = pcap;
}
/** @internal
@@ -315,6 +331,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
socket_t fd;
socklen_t len;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (session == NULL) {
return SSH_ERROR;
@@ -337,7 +354,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
ssh_set_error(session,
SSH_REQUEST_DENIED,
"Getting local IP address: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -347,7 +364,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
ssh_set_error(session,
SSH_REQUEST_DENIED,
"Getting remote IP address: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -383,7 +400,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
*/
int ssh_pcap_context_write(ssh_pcap_context ctx,
enum ssh_pcap_direction direction,
- void *data,
+ void *data,
uint32_t len,
uint32_t origlen)
{
@@ -417,7 +434,7 @@ int ssh_pcap_context_write(ssh_pcap_context ctx,
0); /* checksum */
ctx->file->ipsequence++;
- if (rc != SSH_OK){
+ if (rc != SSH_OK) {
goto error;
}
if (direction == SSH_PCAP_DIR_OUT) {
@@ -506,23 +523,25 @@ error:
/** @brief sets the pcap file used to trace the session
* @param current session
- * @param pcap an handler to a pcap file. A pcap file may be used in several
+ * @param pcap a handler to a pcap file. A pcap file may be used in several
* sessions.
* @returns SSH_ERROR in case of error, SSH_OK otherwise.
*/
-int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){
- ssh_pcap_context ctx=ssh_pcap_context_new(session);
- if(ctx==NULL){
- ssh_set_error_oom(session);
- return SSH_ERROR;
- }
- ctx->file=pcap;
- if(session->pcap_ctx)
- ssh_pcap_context_free(session->pcap_ctx);
- session->pcap_ctx=ctx;
- return SSH_OK;
+int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap)
+{
+ ssh_pcap_context ctx = ssh_pcap_context_new(session);
+ if (ctx == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ ctx->file = pcap;
+ if (session->pcap_ctx) {
+ ssh_pcap_context_free(session->pcap_ctx);
+ }
+ session->pcap_ctx = ctx;
+ return SSH_OK;
}
-
+/** @} */
#else /* WITH_PCAP */
@@ -531,30 +550,36 @@ int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){
#include "libssh/libssh.h"
#include "libssh/priv.h"
-int ssh_pcap_file_close(ssh_pcap_file pcap){
- (void) pcap;
- return SSH_ERROR;
+int ssh_pcap_file_close(ssh_pcap_file pcap)
+{
+ (void)pcap;
+
+ return SSH_ERROR;
}
-void ssh_pcap_file_free(ssh_pcap_file pcap){
- (void) pcap;
+void ssh_pcap_file_free(ssh_pcap_file pcap)
+{
+ (void)pcap;
}
-ssh_pcap_file ssh_pcap_file_new(void){
- return NULL;
+ssh_pcap_file ssh_pcap_file_new(void)
+{
+ return NULL;
}
-int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){
- (void) pcap;
- (void) filename;
- return SSH_ERROR;
+int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename)
+{
+ (void)pcap;
+ (void)filename;
+
+ return SSH_ERROR;
}
-int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile){
- (void) pcapfile;
- ssh_set_error(session,SSH_REQUEST_DENIED,"Pcap support not compiled in");
- return SSH_ERROR;
+int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile)
+{
+ (void)pcapfile;
+
+ ssh_set_error(session, SSH_REQUEST_DENIED, "Pcap support not compiled in");
+ return SSH_ERROR;
}
#endif
-
-/** @} */
diff --git a/src/pki.c b/src/pki.c
index 5d57ca71..816b7e6f 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -43,20 +43,6 @@
#include <sys/stat.h>
#include <sys/types.h>
-#ifdef _WIN32
-# ifdef HAVE_IO_H
-# include <io.h>
-# undef open
-# define open _open
-# undef close
-# define close _close
-# undef read
-# define read _read
-# undef unlink
-# define unlink _unlink
-# endif /* HAVE_IO_H */
-#endif
-
#include "libssh/libssh.h"
#include "libssh/session.h"
#include "libssh/priv.h"
@@ -69,7 +55,7 @@
#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 4096
-#endif
+#endif /* NOT MAX_LINE_SIZE */
#define PKCS11_URI "pkcs11:"
@@ -77,11 +63,6 @@ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey)
{
char *start = NULL;
- start = strstr(privkey, DSA_HEADER_BEGIN);
- if (start != NULL) {
- return SSH_KEYTYPE_DSS;
- }
-
start = strstr(privkey, RSA_HEADER_BEGIN);
if (start != NULL) {
return SSH_KEYTYPE_RSA;
@@ -117,22 +98,31 @@ const char *ssh_pki_key_ecdsa_name(const ssh_key key)
return pki_key_ecdsa_nid_to_name(key->ecdsa_nid);
#else
return NULL;
-#endif
+#endif /* HAVE_ECC */
}
/**
* @brief creates a new empty SSH key
+ *
* @returns an empty ssh_key handle, or NULL on error.
*/
-ssh_key ssh_key_new (void) {
- ssh_key ptr = malloc (sizeof (struct ssh_key_struct));
- if (ptr == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(ptr);
- return ptr;
+ssh_key ssh_key_new (void)
+{
+ ssh_key ptr = malloc (sizeof (struct ssh_key_struct));
+ if (ptr == NULL) {
+ return NULL;
+ }
+ ZERO_STRUCTP(ptr);
+ return ptr;
}
+/**
+ * @brief duplicates the key
+ *
+ * @param key An ssh_key to duplicate
+ *
+ * @return A duplicated ssh_key key
+ */
ssh_key ssh_key_dup(const ssh_key key)
{
if (key == NULL) {
@@ -146,39 +136,22 @@ ssh_key ssh_key_dup(const ssh_key key)
* @brief clean up the key and deallocate all existing keys
* @param[in] key ssh_key to clean
*/
-void ssh_key_clean (ssh_key key){
- if(key == NULL)
+void ssh_key_clean (ssh_key key)
+{
+ if (key == NULL)
return;
-#ifdef HAVE_LIBGCRYPT
- if(key->dsa) gcry_sexp_release(key->dsa);
- if(key->rsa) gcry_sexp_release(key->rsa);
- if(key->ecdsa) gcry_sexp_release(key->ecdsa);
-#elif defined HAVE_LIBCRYPTO
- if(key->dsa) DSA_free(key->dsa);
- if(key->rsa) RSA_free(key->rsa);
-#ifdef HAVE_OPENSSL_ECC
- if(key->ecdsa) EC_KEY_free(key->ecdsa);
-#endif /* HAVE_OPENSSL_ECC */
-#elif defined HAVE_LIBMBEDCRYPTO
- if (key->rsa != NULL) {
- mbedtls_pk_free(key->rsa);
- SAFE_FREE(key->rsa);
- }
- if (key->ecdsa != NULL) {
- mbedtls_ecdsa_free(key->ecdsa);
- SAFE_FREE(key->ecdsa);
- }
-#endif
+ pki_key_clean(key);
+
if (key->ed25519_privkey != NULL){
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* 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
+#endif /* HAVE_LIBCRYPTO*/
SAFE_FREE(key->ed25519_privkey);
}
SAFE_FREE(key->ed25519_pubkey);
@@ -193,21 +166,19 @@ void ssh_key_clean (ssh_key key){
ssh_string_free(key->sk_application);
}
key->cert_type = SSH_KEYTYPE_UNKNOWN;
- key->flags=SSH_KEY_FLAG_EMPTY;
- key->type=SSH_KEYTYPE_UNKNOWN;
+ key->flags = SSH_KEY_FLAG_EMPTY;
+ key->type = SSH_KEYTYPE_UNKNOWN;
key->ecdsa_nid = 0;
- key->type_c=NULL;
- key->dsa = NULL;
- key->rsa = NULL;
- key->ecdsa = NULL;
+ key->type_c = NULL;
}
/**
* @brief deallocate a SSH key
* @param[in] key ssh_key handle to free
*/
-void ssh_key_free (ssh_key key){
- if(key){
+void ssh_key_free (ssh_key key)
+{
+ if (key) {
ssh_key_clean(key);
SAFE_FREE(key);
}
@@ -216,15 +187,16 @@ void ssh_key_free (ssh_key key){
/**
* @brief returns the type of a ssh key
* @param[in] key the ssh_key handle
- * @returns one of SSH_KEYTYPE_RSA, SSH_KEYTYPE_DSS,
+ * @returns one of SSH_KEYTYPE_RSA,
* SSH_KEYTYPE_ECDSA_P256, SSH_KEYTYPE_ECDSA_P384,
- * SSH_KEYTYPE_ECDSA_P521, SSH_KEYTYPE_ED25519, SSH_KEYTYPE_DSS_CERT01,
+ * SSH_KEYTYPE_ECDSA_P521, SSH_KEYTYPE_ED25519,
* SSH_KEYTYPE_RSA_CERT01, SSH_KEYTYPE_ECDSA_P256_CERT01,
* SSH_KEYTYPE_ECDSA_P384_CERT01, SSH_KEYTYPE_ECDSA_P521_CERT01, or
* SSH_KEYTYPE_ED25519_CERT01.
* @returns SSH_KEYTYPE_UNKNOWN if the type is unknown
*/
-enum ssh_keytypes_e ssh_key_type(const ssh_key key){
+enum ssh_keytypes_e ssh_key_type(const ssh_key key)
+{
if (key == NULL) {
return SSH_KEYTYPE_UNKNOWN;
}
@@ -236,6 +208,8 @@ enum ssh_keytypes_e ssh_key_type(const ssh_key key){
*
* @param[in] type The algorithm type to convert.
*
+ * @param[in] hash_type The hash type to convert
+ *
* @return A string for the keytype or NULL if unknown.
*/
const char *
@@ -286,8 +260,6 @@ ssh_key_signature_to_char(enum ssh_keytypes_e type,
*/
const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
switch (type) {
- case SSH_KEYTYPE_DSS:
- return "ssh-dss";
case SSH_KEYTYPE_RSA:
return "ssh-rsa";
case SSH_KEYTYPE_ECDSA:
@@ -300,8 +272,6 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
return "ecdsa-sha2-nistp521";
case SSH_KEYTYPE_ED25519:
return "ssh-ed25519";
- case SSH_KEYTYPE_DSS_CERT01:
- return "ssh-dss-cert-v01@openssh.com";
case SSH_KEYTYPE_RSA_CERT01:
return "ssh-rsa-cert-v01@openssh.com";
case SSH_KEYTYPE_ECDSA_P256_CERT01:
@@ -320,7 +290,9 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
return "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com";
case SSH_KEYTYPE_SK_ED25519_CERT01:
return "sk-ssh-ed25519-cert-v01@openssh.com";
+ case SSH_KEYTYPE_DSS: /* deprecated */
case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_UNKNOWN:
return NULL;
}
@@ -338,8 +310,6 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name)
if (strcmp(name, "ssh-rsa") == 0) {
return SSH_DIGEST_SHA1;
- } else if (strcmp(name, "ssh-dss") == 0) {
- return SSH_DIGEST_SHA1;
} else if (strcmp(name, "rsa-sha2-256") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "rsa-sha2-512") == 0) {
@@ -358,7 +328,7 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name)
return SSH_DIGEST_AUTO;
}
- SSH_LOG(SSH_LOG_WARN, "Unknown signature name %s", name);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature name %s", name);
/* TODO we should rather fail */
return SSH_DIGEST_AUTO;
@@ -390,13 +360,13 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
else if (session->server) {
allowed_list = session->opts.wanted_methods[SSH_HOSTKEYS];
if (allowed_list == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Session invalid: no host key available");
+ SSH_LOG(SSH_LOG_TRACE, "Session invalid: no host key available");
return 0;
}
}
-#endif
+#endif /* WITH_SERVER */
else {
- SSH_LOG(SSH_LOG_WARN, "Session invalid: not set as client nor server");
+ SSH_LOG(SSH_LOG_TRACE, "Session invalid: not set as client nor server");
return 0;
}
@@ -404,6 +374,42 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
return ssh_match_group(allowed_list, type);
}
+bool ssh_key_size_allowed_rsa(int min_size, ssh_key key)
+{
+ int key_size = ssh_key_size(key);
+
+ if (min_size < 768) {
+ if (ssh_fips_mode()) {
+ min_size = 2048;
+ } else {
+ min_size = 1024;
+ }
+ }
+ return (key_size >= min_size);
+}
+
+/**
+ * @brief Check the given key is acceptable in regards to the key size policy
+ * specified by the configuration
+ *
+ * @param[in] session The SSH session
+ * @param[in] key The SSH key
+ * @returns true if the key is allowed, false otherwise
+ */
+bool ssh_key_size_allowed(ssh_session session, ssh_key key)
+{
+ int min_size = 0;
+
+ switch (ssh_key_type(key)) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ min_size = session->opts.rsa_min_size;
+ return ssh_key_size_allowed_rsa(min_size, key);
+ default:
+ return true;
+ }
+}
+
/**
* @brief Convert a key type to a hash type. This is usually unambiguous
* for all the key types, unless the SHA2 extension (RFC 8332) is
@@ -419,9 +425,6 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
enum ssh_keytypes_e type)
{
switch (type) {
- case SSH_KEYTYPE_DSS_CERT01:
- case SSH_KEYTYPE_DSS:
- return SSH_DIGEST_SHA1;
case SSH_KEYTYPE_RSA_CERT01:
/* If we are talking to an old OpenSSH version which does not support
* SHA2 in certificates */
@@ -463,10 +466,12 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
case SSH_KEYTYPE_ED25519:
return SSH_DIGEST_AUTO;
case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Digest algorithm to be used with key type %u "
+ SSH_LOG(SSH_LOG_TRACE, "Digest algorithm to be used with key type %u "
"is not defined", type);
}
@@ -537,19 +542,16 @@ enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name) {
*
* @return The enum ssh key type.
*/
-enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
+enum ssh_keytypes_e ssh_key_type_from_name(const char *name)
+{
if (name == NULL) {
return SSH_KEYTYPE_UNKNOWN;
}
if (strcmp(name, "rsa") == 0) {
return SSH_KEYTYPE_RSA;
- } else if (strcmp(name, "dsa") == 0) {
- return SSH_KEYTYPE_DSS;
} else if (strcmp(name, "ssh-rsa") == 0) {
return SSH_KEYTYPE_RSA;
- } else if (strcmp(name, "ssh-dss") == 0) {
- return SSH_KEYTYPE_DSS;
} else if (strcmp(name, "ssh-ecdsa") == 0
|| strcmp(name, "ecdsa") == 0
|| strcmp(name, "ecdsa-sha2-nistp256") == 0) {
@@ -560,8 +562,6 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
return SSH_KEYTYPE_ECDSA_P521;
} else if (strcmp(name, "ssh-ed25519") == 0){
return SSH_KEYTYPE_ED25519;
- } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) {
- return SSH_KEYTYPE_DSS_CERT01;
} else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
return SSH_KEYTYPE_RSA_CERT01;
} else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) {
@@ -586,16 +586,15 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
}
/**
- * @brief Get the pubic key type corresponding to a certificate type.
+ * @brief Get the public key type corresponding to a certificate type.
*
* @param[in] type The certificate or public key type.
*
* @return The matching public key type.
*/
-enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type) {
+enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type)
+{
switch (type) {
- case SSH_KEYTYPE_DSS_CERT01:
- return SSH_KEYTYPE_DSS;
case SSH_KEYTYPE_RSA_CERT01:
return SSH_KEYTYPE_RSA;
case SSH_KEYTYPE_ECDSA_P256_CERT01:
@@ -622,7 +621,8 @@ enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type) {
*
* @return 1 if it is a public key, 0 if not.
*/
-int ssh_key_is_public(const ssh_key k) {
+int ssh_key_is_public(const ssh_key k)
+{
if (k == NULL) {
return 0;
}
@@ -664,8 +664,8 @@ int ssh_key_cmp(const ssh_key k1,
return 1;
}
- if (k1->type != k2->type) {
- SSH_LOG(SSH_LOG_WARN, "key types don't match!");
+ if (ssh_key_type_plain(k1->type) != ssh_key_type_plain(k2->type)) {
+ SSH_LOG(SSH_LOG_DEBUG, "key types don't match!");
return 1;
}
@@ -685,6 +685,22 @@ int ssh_key_cmp(const ssh_key k1,
}
}
+ if (what == SSH_KEY_CMP_CERTIFICATE) {
+ if (!is_cert_type(k1->type) ||
+ !is_cert_type(k2->type)) {
+ return 1;
+ }
+ if (k1->cert == NULL || k2->cert == NULL) {
+ return 1;
+ }
+ if (ssh_buffer_get_len(k1->cert) != ssh_buffer_get_len(k2->cert)) {
+ return 1;
+ }
+ return memcmp(ssh_buffer_get(k1->cert),
+ ssh_buffer_get(k2->cert),
+ ssh_buffer_get_len(k1->cert));
+ }
+
if (k1->type == SSH_KEYTYPE_ED25519 ||
k1->type == SSH_KEYTYPE_SK_ED25519) {
return pki_ed25519_key_cmp(k1, k2, what);
@@ -713,17 +729,12 @@ void ssh_signature_free(ssh_signature sig)
}
switch(sig->type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_LIBGCRYPT
- gcry_sexp_release(sig->dsa_sig);
-#endif
- break;
case SSH_KEYTYPE_RSA:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(sig->rsa_sig);
#elif defined HAVE_LIBMBEDCRYPTO
SAFE_FREE(sig->rsa_sig);
-#endif
+#endif /* HAVE_LIBGCRYPT */
break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
@@ -734,16 +745,17 @@ void ssh_signature_free(ssh_signature sig)
#elif defined HAVE_LIBMBEDCRYPTO
bignum_safe_free(sig->ecdsa_sig.r);
bignum_safe_free(sig->ecdsa_sig.s);
-#endif
+#endif /* HAVE_GCRYPT_ECC */
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
-#ifndef HAVE_OPENSSL_ED25519
+#ifndef HAVE_LIBCRYPTO
/* When using OpenSSL, the signature is stored in sig->raw_sig */
SAFE_FREE(sig->ed25519_sig);
-#endif
+#endif /* HAVE_LIBCRYPTO */
break;
- case SSH_KEYTYPE_DSS_CERT01:
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -764,7 +776,7 @@ void ssh_signature_free(ssh_signature sig)
}
/**
- * @brief import a base64 formated key from a memory c-string
+ * @brief import a base64 formatted key from a memory c-string
*
* @param[in] b64_key The c-string holding the base64 encoded key
*
@@ -775,7 +787,7 @@ void ssh_signature_free(ssh_signature sig)
* @param[in] auth_data Private data passed to the auth function.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free()
*
* @return SSH_ERROR in case of error, SSH_OK otherwise.
*
@@ -798,7 +810,7 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Trying to decode privkey passphrase=%s",
passphrase ? "true" : "false");
@@ -824,9 +836,10 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
return SSH_OK;
}
+
+
/**
- * @brief Convert a private key to a pem base64 encoded key, or OpenSSH format for
- * keytype ssh-ed25519
+ * @brief Convert a private key to a base64 encoded key in given format
*
* @param[in] privkey The private key to export.
*
@@ -838,15 +851,21 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
* @param[in] auth_data Private data passed to the auth function.
*
* @param[out] b64_key A pointer to store the allocated base64 encoded key. You
- * need to free the buffer.
+ * need to free the buffer using ssh_string_from_char().
+ *
+ * @param[in] format The file format (OpenSSH, PEM, or default)
*
* @return SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_string_free_char()
*/
-int ssh_pki_export_privkey_base64(const ssh_key privkey,
- const char *passphrase,
- ssh_auth_callback auth_fn,
- void *auth_data,
- char **b64_key)
+int
+ssh_pki_export_privkey_base64_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ char **b64_key,
+ enum ssh_file_format_e format)
{
ssh_string blob = NULL;
char *b64 = NULL;
@@ -855,16 +874,32 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey,
return SSH_ERROR;
}
- if (privkey->type == SSH_KEYTYPE_ED25519){
- blob = ssh_pki_openssh_privkey_export(privkey,
- passphrase,
- auth_fn,
- auth_data);
- } else {
+ /*
+ * For historic reasons, the Ed25519 keys are exported in OpenSSH file
+ * format by default also when built with OpenSSL.
+ */
+#ifdef HAVE_LIBCRYPTO
+ if (format == SSH_FILE_FORMAT_DEFAULT &&
+ privkey->type != SSH_KEYTYPE_ED25519) {
+ format = SSH_FILE_FORMAT_PEM;
+ }
+#endif /* HAVE_LIBCRYPTO */
+
+ switch (format) {
+ case SSH_FILE_FORMAT_PEM:
blob = pki_private_key_to_pem(privkey,
passphrase,
auth_fn,
auth_data);
+ break;
+ case SSH_FILE_FORMAT_DEFAULT:
+ /* default except (OpenSSL && !ED25519) handled above */
+ case SSH_FILE_FORMAT_OPENSSH:
+ blob = ssh_pki_openssh_privkey_export(privkey,
+ passphrase,
+ auth_fn,
+ auth_data);
+ break;
}
if (blob == NULL) {
return SSH_ERROR;
@@ -881,6 +916,42 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey,
return SSH_OK;
}
+ /**
+ * @brief Convert a private key to a pem base64 encoded key, or OpenSSH format for
+ * keytype ssh-ed25519
+ *
+ * @param[in] privkey The private key to export.
+ *
+ * @param[in] passphrase The passphrase to use to encrypt the key with or
+ * NULL. An empty string means no passphrase.
+ *
+ * @param[in] auth_fn An auth function you may want to use or NULL.
+ *
+ * @param[in] auth_data Private data passed to the auth function.
+ *
+ * @param[out] b64_key A pointer to store the allocated base64 encoded key. You
+ * need to free the buffer using ssh_string_from_char().
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_string_free_char()
+ */
+int ssh_pki_export_privkey_base64(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ char **b64_key)
+{
+ return ssh_pki_export_privkey_base64_format(privkey,
+ passphrase,
+ auth_fn,
+ auth_data,
+ b64_key,
+ SSH_FILE_FORMAT_DEFAULT);
+}
+
+
+
/**
* @brief Import a private key from a file or a PKCS #11 device.
*
@@ -895,7 +966,7 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey,
* @param[in] auth_data Private data passed to the auth function.
*
* @param[out] pkey A pointer to store the allocated ssh_key. You need to
- * free the key.
+ * free the key using ssh_key_free().
*
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
* denied, SSH_ERROR otherwise.
@@ -912,6 +983,7 @@ int ssh_pki_import_privkey_file(const char *filename,
FILE *file;
off_t size;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (pkey == NULL || filename == NULL || *filename == '\0') {
return SSH_ERROR;
@@ -922,24 +994,24 @@ int ssh_pki_import_privkey_file(const char *filename,
rc = pki_uri_import(filename, pkey, SSH_KEY_PRIVATE);
return rc;
}
-#endif
+#endif /* WITH_PKCS11_URI */
file = fopen(filename, "rb");
if (file == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Error opening %s: %s",
filename,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_EOF;
}
rc = fstat(fileno(file), &sb);
if (rc < 0) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Error getting stat of %s: %s",
filename,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
switch (errno) {
case ENOENT:
case EACCES:
@@ -950,7 +1022,7 @@ int ssh_pki_import_privkey_file(const char *filename,
}
if (sb.st_size > MAX_PRIVKEY_SIZE) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Private key is bigger than 4M.");
fclose(file);
return SSH_ERROR;
@@ -959,7 +1031,7 @@ int ssh_pki_import_privkey_file(const char *filename,
key_buf = malloc(sb.st_size + 1);
if (key_buf == NULL) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
return SSH_ERROR;
}
@@ -968,10 +1040,10 @@ int ssh_pki_import_privkey_file(const char *filename,
if (size != sb.st_size) {
SAFE_FREE(key_buf);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Error reading %s: %s",
filename,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
key_buf[size] = 0;
@@ -987,8 +1059,7 @@ int ssh_pki_import_privkey_file(const char *filename,
}
/**
- * @brief Export a private key to a pem file on disk, or OpenSSH format for
- * keytype ssh-ed25519
+ * @brief Export a private key to a file in format specified in the argument
*
* @param[in] privkey The private key to export.
*
@@ -1001,16 +1072,21 @@ int ssh_pki_import_privkey_file(const char *filename,
*
* @param[in] filename The path where to store the pem file.
*
+ * @param[in] format The file format (OpenSSH, PEM, or default)
+ *
* @return SSH_OK on success, SSH_ERROR on error.
*/
-int ssh_pki_export_privkey_file(const ssh_key privkey,
- const char *passphrase,
- ssh_auth_callback auth_fn,
- void *auth_data,
- const char *filename)
+
+int
+ssh_pki_export_privkey_file_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename,
+ enum ssh_file_format_e format)
{
- ssh_string blob;
- FILE *fp;
+ ssh_string blob = NULL;
+ FILE *fp = NULL;
int rc;
if (privkey == NULL || !ssh_key_is_private(privkey)) {
@@ -1019,21 +1095,38 @@ int ssh_pki_export_privkey_file(const ssh_key privkey,
fp = fopen(filename, "wb");
if (fp == NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
SSH_LOG(SSH_LOG_FUNCTIONS, "Error opening %s: %s",
- filename, strerror(errno));
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_EOF;
}
- if (privkey->type == SSH_KEYTYPE_ED25519){
- blob = ssh_pki_openssh_privkey_export(privkey,
- passphrase,
- auth_fn,
- auth_data);
- } else {
+ /*
+ * For historic reasons, the Ed25519 keys are exported in OpenSSH file
+ * format by default also when built with OpenSSL.
+ */
+#ifdef HAVE_LIBCRYPTO
+ if (format == SSH_FILE_FORMAT_DEFAULT &&
+ privkey->type != SSH_KEYTYPE_ED25519) {
+ format = SSH_FILE_FORMAT_PEM;
+ }
+#endif /* HAVE_LIBCRYPTO */
+
+ switch (format) {
+ case SSH_FILE_FORMAT_PEM:
blob = pki_private_key_to_pem(privkey,
passphrase,
auth_fn,
auth_data);
+ break;
+ case SSH_FILE_FORMAT_DEFAULT:
+ /* default except (OpenSSL && !ED25519) handled above */
+ case SSH_FILE_FORMAT_OPENSSH:
+ blob = ssh_pki_openssh_privkey_export(privkey,
+ passphrase,
+ auth_fn,
+ auth_data);
+ break;
}
if (blob == NULL) {
fclose(fp);
@@ -1052,12 +1145,45 @@ int ssh_pki_export_privkey_file(const ssh_key privkey,
return SSH_OK;
}
-/* temporary function to migrate seemlessly to ssh_key */
-ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) {
+/**
+ * @brief Export a private key to a pem file on disk, or OpenSSH format for
+ * keytype ssh-ed25519
+ *
+ * @param[in] privkey The private key to export.
+ *
+ * @param[in] passphrase The passphrase to use to encrypt the key with or
+ * NULL. An empty string means no passphrase.
+ *
+ * @param[in] auth_fn An auth function you may want to use or NULL.
+ *
+ * @param[in] auth_data Private data passed to the auth function.
+ *
+ * @param[in] filename The path where to store the pem file.
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+int
+ssh_pki_export_privkey_file(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename)
+{
+ return ssh_pki_export_privkey_file_format(privkey,
+ passphrase,
+ auth_fn,
+ auth_data,
+ filename,
+ SSH_FILE_FORMAT_DEFAULT);
+}
+
+/* temporary function to migrate seamlessly to ssh_key */
+ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key)
+{
ssh_public_key pub;
ssh_key tmp;
- if(key == NULL) {
+ if (key == NULL) {
return NULL;
}
@@ -1066,38 +1192,44 @@ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) {
return NULL;
}
- pub = malloc(sizeof(struct ssh_public_key_struct));
+ pub = calloc(1, sizeof(struct ssh_public_key_struct));
if (pub == NULL) {
ssh_key_free(tmp);
return NULL;
}
- ZERO_STRUCTP(pub);
pub->type = tmp->type;
pub->type_c = tmp->type_c;
- pub->dsa_pub = tmp->dsa;
- tmp->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
pub->rsa_pub = tmp->rsa;
tmp->rsa = NULL;
+#else
+ pub->key_pub = tmp->key;
+ tmp->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(tmp);
return pub;
}
-ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key) {
+ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key)
+{
ssh_private_key privkey;
- privkey = malloc(sizeof(struct ssh_private_key_struct));
+ privkey = calloc(1, sizeof(struct ssh_private_key_struct));
if (privkey == NULL) {
ssh_key_free(key);
return NULL;
}
privkey->type = key->type;
- privkey->dsa_priv = key->dsa;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa_priv = key->rsa;
+#else
+ privkey->key_priv = key->key;
+#endif /* HAVE_LIBCRYPTO */
return privkey;
}
@@ -1119,46 +1251,6 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
switch (type) {
- case SSH_KEYTYPE_DSS:
- {
- ssh_string p = NULL;
- ssh_string q = NULL;
- ssh_string g = NULL;
- ssh_string pubkey = NULL;
- ssh_string privkey = NULL;
-
- rc = ssh_buffer_unpack(buffer, "SSSSS", &p, &q, &g,
- &pubkey, &privkey);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
- goto fail;
- }
-
- rc = pki_privkey_build_dss(key, p, q, g, pubkey, privkey);
-#ifdef DEBUG_CRYPTO
- 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_log_hexdump("privkey", ssh_string_data(privkey),
- ssh_string_len(privkey));
-#endif
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(pubkey);
- SSH_STRING_FREE(pubkey);
- ssh_string_burn(privkey);
- SSH_STRING_FREE(privkey);
- if (rc == SSH_ERROR) {
- goto fail;
- }
- }
- break;
case SSH_KEYTYPE_RSA:
{
ssh_string n = NULL;
@@ -1171,7 +1263,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
rc = ssh_buffer_unpack(buffer, "SSSSSS", &n, &e, &d,
&iqmp, &p, &q);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1180,11 +1272,12 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
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_log_hexdump("iqmp",
+ ssh_string_data(iqmp),
+ ssh_string_len(iqmp));
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
+#endif /* DEBUG_CRYPTO */
ssh_string_burn(n);
SSH_STRING_FREE(n);
ssh_string_burn(e);
@@ -1198,7 +1291,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
ssh_string_burn(q);
SSH_STRING_FREE(q);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build RSA private key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build RSA private key");
goto fail;
}
}
@@ -1215,7 +1308,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
rc = ssh_buffer_unpack(buffer, "SSS", &i, &e, &exp);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1235,19 +1328,19 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
ssh_string_burn(exp);
SSH_STRING_FREE(exp);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build ECDSA private key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build ECDSA private key");
goto fail;
}
}
break;
-#endif
+#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
{
ssh_string pubkey = NULL, privkey = NULL;
rc = ssh_buffer_unpack(buffer, "SS", &pubkey, &privkey);
if (rc != SSH_OK){
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1256,12 +1349,11 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
SSH_STRING_FREE(privkey);
SSH_STRING_FREE(pubkey);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build ed25519 key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build ed25519 key");
goto fail;
}
}
break;
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -1274,7 +1366,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown private key type (%d)", type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown private key type (%d)", type);
goto fail;
}
@@ -1288,7 +1380,8 @@ fail:
static int pki_import_pubkey_buffer(ssh_buffer buffer,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_key key = NULL;
int rc;
@@ -1302,39 +1395,6 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
key->flags = SSH_KEY_FLAG_PUBLIC;
switch (type) {
- case SSH_KEYTYPE_DSS:
- {
- ssh_string p = NULL;
- ssh_string q = NULL;
- ssh_string g = NULL;
- ssh_string pubkey = NULL;
-
- rc = ssh_buffer_unpack(buffer, "SSSS", &p, &q, &g, &pubkey);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
- goto fail;
- }
-
- rc = pki_pubkey_build_dss(key, p, q, g, pubkey);
-#ifdef DEBUG_CRYPTO
- 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_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(pubkey);
- SSH_STRING_FREE(pubkey);
- if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build DSA public key");
- goto fail;
- }
- }
- break;
case SSH_KEYTYPE_RSA:
{
ssh_string e = NULL;
@@ -1342,7 +1402,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
rc = ssh_buffer_unpack(buffer, "SS", &e, &n);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1350,13 +1410,13 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
#ifdef DEBUG_CRYPTO
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
+#endif /* DEBUG_CRYPTO */
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(n);
SSH_STRING_FREE(n);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build RSA public key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build RSA public key");
goto fail;
}
}
@@ -1374,7 +1434,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
rc = ssh_buffer_unpack(buffer, "SS", &i, &e);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1390,7 +1450,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
ssh_string_burn(e);
SSH_STRING_FREE(e);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build ECDSA public key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build ECDSA public key");
goto fail;
}
@@ -1403,7 +1463,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
if (type == SSH_KEYTYPE_SK_ECDSA) {
ssh_string application = ssh_buffer_get_ssh_string(buffer);
if (application == NULL) {
- SSH_LOG(SSH_LOG_WARN, "SK Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "SK Unpack error");
goto fail;
}
key->sk_application = application;
@@ -1411,14 +1471,14 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
}
}
break;
-#endif
+#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
{
ssh_string pubkey = ssh_buffer_get_ssh_string(buffer);
if (ssh_string_len(pubkey) != ED25519_KEY_LEN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid public key length");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid public key length");
ssh_string_burn(pubkey);
SSH_STRING_FREE(pubkey);
goto fail;
@@ -1438,14 +1498,13 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
if (type == SSH_KEYTYPE_SK_ED25519) {
ssh_string application = ssh_buffer_get_ssh_string(buffer);
if (application == NULL) {
- SSH_LOG(SSH_LOG_WARN, "SK Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "SK Unpack error");
goto fail;
}
key->sk_application = application;
}
}
break;
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -1456,7 +1515,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown public key protocol %d", type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown public key protocol %d", type);
goto fail;
}
@@ -1470,7 +1529,8 @@ fail:
static int pki_import_cert_buffer(ssh_buffer buffer,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_buffer cert;
ssh_string tmp_s;
const char *type_c;
@@ -1512,9 +1572,6 @@ static int pki_import_cert_buffer(ssh_buffer buffer,
SSH_STRING_FREE(tmp_s);
switch (type) {
- case SSH_KEYTYPE_DSS_CERT01:
- rc = pki_import_pubkey_buffer(buffer, SSH_KEYTYPE_DSS, &key);
- break;
case SSH_KEYTYPE_RSA_CERT01:
rc = pki_import_pubkey_buffer(buffer, SSH_KEYTYPE_RSA, &key);
break;
@@ -1545,7 +1602,7 @@ static int pki_import_cert_buffer(ssh_buffer buffer,
key->type = type;
key->type_c = type_c;
- key->cert = (void*) cert;
+ key->cert = cert;
*pkey = key;
return SSH_OK;
@@ -1557,14 +1614,14 @@ fail:
}
/**
- * @brief Import a base64 formated public key from a memory c-string.
+ * @brief Import a base64 formatted public key from a memory c-string.
*
* @param[in] b64_key The base64 key to format.
*
* @param[in] type The type of the key to format.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -1572,7 +1629,8 @@ fail:
*/
int ssh_pki_import_pubkey_base64(const char *b64_key,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_buffer buffer = NULL;
ssh_string type_s = NULL;
int rc;
@@ -1612,14 +1670,15 @@ int ssh_pki_import_pubkey_base64(const char *b64_key,
* 6.6 "Public Key Algorithms".
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_key_free()
*/
int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_buffer buffer = NULL;
ssh_string type_s = NULL;
enum ssh_keytypes_e type;
@@ -1631,26 +1690,26 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
buffer = ssh_buffer_new();
if (buffer == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
return SSH_ERROR;
}
rc = ssh_buffer_add_data(buffer, ssh_string_data(key_blob),
ssh_string_len(key_blob));
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
goto fail;
}
type_s = ssh_buffer_get_ssh_string(buffer);
if (type_s == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
goto fail;
}
type = ssh_key_type_from_name(ssh_string_get_char(type_s));
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown key type found!");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown key type found!");
goto fail;
}
SSH_STRING_FREE(type_s);
@@ -1671,6 +1730,7 @@ fail:
return SSH_ERROR;
}
+#ifdef WITH_PKCS11_URI
/**
*@brief Detect if the pathname in cmp is a PKCS #11 URI.
*
@@ -1699,7 +1759,10 @@ bool ssh_pki_is_uri(const char *cmp)
*
* @param[in] priv_uri Private PKCS #11 URI.
*
- * @returns pointer to the public PKCS #11 URI
+ * @returns pointer to the public PKCS #11 URI. You need to free
+ * the memory using ssh_string_free_char().
+ *
+ * @see ssh_string_free_char().
*/
char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri)
{
@@ -1711,6 +1774,7 @@ char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri)
return pub_uri_temp;
}
+#endif /* WITH_PKCS11_URI */
/**
* @brief Import a public key from a file or a PKCS #11 device.
@@ -1719,7 +1783,7 @@ char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri)
* PKCS #11 URI corresponding to the public key.
*
* @param[out] pkey A pointer to store the allocated public key. You need to
- * free the memory.
+ * free the memory using ssh_key_free().
*
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
* denied, SSH_ERROR otherwise.
@@ -1730,12 +1794,14 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
{
enum ssh_keytypes_e type;
struct stat sb;
- char *key_buf, *p;
+ char *key_buf = NULL, *p = NULL;
size_t buflen, i;
- const char *q;
- FILE *file;
+ const char *q = NULL;
+ FILE *file = NULL;
off_t size;
int rc, cmp;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_key priv_key = NULL;
if (pkey == NULL || filename == NULL || *filename == '\0') {
return SSH_ERROR;
@@ -1746,20 +1812,20 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
rc = pki_uri_import(filename, pkey, SSH_KEY_PUBLIC);
return rc;
}
-#endif
+#endif /* WITH_PKCS11_URI */
file = fopen(filename, "rb");
if (file == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Error opening %s: %s",
- filename, strerror(errno));
+ SSH_LOG(SSH_LOG_TRACE, "Error opening %s: %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_EOF;
}
rc = fstat(fileno(file), &sb);
if (rc < 0) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN, "Error gettint stat of %s: %s",
- filename, strerror(errno));
+ SSH_LOG(SSH_LOG_TRACE, "Error gettint stat of %s: %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
switch (errno) {
case ENOENT:
case EACCES:
@@ -1776,7 +1842,7 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
key_buf = malloc(sb.st_size + 1);
if (key_buf == NULL) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
return SSH_ERROR;
}
@@ -1785,8 +1851,8 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
if (size != sb.st_size) {
SAFE_FREE(key_buf);
- SSH_LOG(SSH_LOG_WARN, "Error reading %s: %s",
- filename, strerror(errno));
+ SSH_LOG(SSH_LOG_TRACE, "Error reading %s: %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
key_buf[size] = '\0';
@@ -1798,7 +1864,24 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
*pkey = ssh_pki_openssh_pubkey_import(key_buf);
SAFE_FREE(key_buf);
if (*pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Failed to import public key from OpenSSH"
+ SSH_LOG(SSH_LOG_TRACE, "Failed to import public key from OpenSSH"
+ " private key file");
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+ }
+
+ /*
+ * Try to parse key as PEM. Set empty passphrase, so user won't be prompted
+ * for passphrase. Don't try to decrypt encrypted private key.
+ */
+ priv_key = pki_private_key_from_base64(key_buf, "", NULL, NULL);
+ if (priv_key) {
+ rc = ssh_pki_export_privkey_to_pubkey(priv_key, pkey);
+ ssh_key_free(priv_key);
+ SAFE_FREE(key_buf);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_WARN, "Failed to import public key from PEM"
" private key file");
return SSH_ERROR;
}
@@ -1820,6 +1903,10 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
return SSH_ERROR;
}
+ if (i >= buflen) {
+ SAFE_FREE(key_buf);
+ return SSH_ERROR;
+ }
q = &p[i + 1];
for (; i < buflen; i++) {
if (isspace((int)p[i])) {
@@ -1835,14 +1922,14 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
}
/**
- * @brief Import a base64 formated certificate from a memory c-string.
+ * @brief Import a base64 formatted certificate from a memory c-string.
*
* @param[in] b64_cert The base64 cert to format.
*
* @param[in] type The type of the cert to format.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -1850,7 +1937,8 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
*/
int ssh_pki_import_cert_base64(const char *b64_cert,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
return ssh_pki_import_pubkey_base64(b64_cert, type, pkey);
}
@@ -1863,14 +1951,15 @@ int ssh_pki_import_cert_base64(const char *b64_cert,
* 6.6 "Public Key Algorithms".
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_key_free()
*/
int ssh_pki_import_cert_blob(const ssh_string cert_blob,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
return ssh_pki_import_pubkey_blob(cert_blob, pkey);
}
@@ -1880,7 +1969,7 @@ int ssh_pki_import_cert_blob(const ssh_string cert_blob,
* @param[in] filename The path to the certificate.
*
* @param[out] pkey A pointer to store the allocated certificate. You need to
- * free the memory.
+ * free the memory using ssh_key_free().
*
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
* denied, SSH_ERROR otherwise.
@@ -1889,26 +1978,39 @@ int ssh_pki_import_cert_blob(const ssh_string cert_blob,
*/
int ssh_pki_import_cert_file(const char *filename, ssh_key *pkey)
{
- return ssh_pki_import_pubkey_file(filename, pkey);
+ int rc;
+
+ rc = ssh_pki_import_pubkey_file(filename, pkey);
+ if (rc == SSH_OK) {
+ /* check the key is a cert type. */
+ if (!is_cert_type((*pkey)->type)) {
+ SSH_KEY_FREE(*pkey);
+ return SSH_ERROR;
+ }
+ }
+
+ return rc;
}
/**
- * @brief Generates a keypair.
+ * @brief Generates a key pair.
*
* @param[in] type Type of key to create
*
* @param[in] parameter Parameter to the creation of key:
* rsa : length of the key in bits (e.g. 1024, 2048, 4096)
- * dsa : length of the key in bits (e.g. 1024, 2048, 3072)
* @param[out] pkey A pointer to store the allocated private key. You need
- * to free the memory.
+ * to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @warning Generating a key pair may take some time.
+ *
+ * @see ssh_key_free()
*/
int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
- ssh_key *pkey){
+ ssh_key *pkey)
+{
int rc;
ssh_key key = ssh_key_new();
@@ -1926,11 +2028,6 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
if(rc == SSH_ERROR)
goto error;
break;
- case SSH_KEYTYPE_DSS:
- rc = pki_key_generate_dss(key, parameter);
- if(rc == SSH_ERROR)
- goto error;
- break;
#ifdef HAVE_ECC
case SSH_KEYTYPE_ECDSA: /* deprecated */
rc = pki_key_generate_ecdsa(key, parameter);
@@ -1959,14 +2056,13 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
goto error;
}
break;
-#endif
+#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
rc = pki_key_generate_ed25519(key);
if (rc == SSH_ERROR) {
goto error;
}
break;
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -1995,7 +2091,7 @@ error:
* @param[in] privkey The private key to get the public key from.
*
* @param[out] pkey A pointer to store the newly allocated public key. You
- * NEED to free the key.
+ * NEED to free the key using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -2033,11 +2129,11 @@ int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey,
* from.
*
* @param[out] pblob A pointer to store the newly allocated key blob. You
- * NEED to free it.
+ * need to free it using ssh_string_free().
*
* @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)
@@ -2048,7 +2144,42 @@ int ssh_pki_export_pubkey_blob(const ssh_key key,
return SSH_OK;
}
- blob = pki_publickey_to_blob(key);
+ blob = pki_key_to_blob(key, SSH_KEY_PUBLIC);
+ if (blob == NULL) {
+ return SSH_ERROR;
+ }
+
+ *pblob = blob;
+ return SSH_OK;
+}
+
+/**
+ * @internal
+ *
+ * @brief Create a key_blob from a private key.
+ *
+ * The "key_blob" is encoded as per draft-miller-ssh-agent-08 section 4.2
+ * "Adding keys to the agent" for any of the supported key types.
+ *
+ * @param[in] key A private key to create the private ssh_string from.
+ *
+ * @param[out] pblob A pointer to store the newly allocated key blob. You
+ * need to free it using ssh_string_free().
+ *
+ * @return SSH_OK on success, SSH_ERROR otherwise.
+ *
+ * @see ssh_string_free()
+ */
+int ssh_pki_export_privkey_blob(const ssh_key key,
+ ssh_string *pblob)
+{
+ ssh_string blob;
+
+ if (key == NULL) {
+ return SSH_OK;
+ }
+
+ blob = pki_key_to_blob(key, SSH_KEY_PRIVATE);
if (blob == NULL) {
return SSH_ERROR;
}
@@ -2063,11 +2194,11 @@ int ssh_pki_export_pubkey_blob(const ssh_key key,
* @param[in] key The key to hash
*
* @param[out] b64_key A pointer to store the allocated base64 encoded key. You
- * need to free the buffer.
+ * need to free the buffer using ssh_string_free_char()
*
* @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)
@@ -2079,7 +2210,7 @@ int ssh_pki_export_pubkey_base64(const ssh_key key,
return SSH_ERROR;
}
- key_blob = pki_publickey_to_blob(key);
+ key_blob = pki_key_to_blob(key, SSH_KEY_PUBLIC);
if (key_blob == NULL) {
return SSH_ERROR;
}
@@ -2095,6 +2226,18 @@ int ssh_pki_export_pubkey_base64(const ssh_key key,
return SSH_OK;
}
+/**
+ * @brief Export public key to file
+ *
+ * Exports the public key in AuthorizedKeysFile acceptable format.
+ * For more information see `man sshd`
+ *
+ * @param key A key to export
+ *
+ * @param filename The name of the output file
+ *
+ * @returns SSH_OK on success, SSH_ERROR otherwise.
+ */
int ssh_pki_export_pubkey_file(const ssh_key key,
const char *filename)
{
@@ -2164,7 +2307,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
**/
int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) {
ssh_buffer cert_buffer;
- int rc;
+ int rc, cmp;
if (certkey == NULL || privkey == NULL) {
return SSH_ERROR;
@@ -2178,6 +2321,12 @@ int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) {
return SSH_ERROR;
}
+ /* make sure the public keys match */
+ cmp = ssh_key_cmp(certkey, privkey, SSH_KEY_CMP_PUBLIC);
+ if (cmp != 0) {
+ return SSH_ERROR;
+ }
+
cert_buffer = ssh_buffer_new();
if (cert_buffer == NULL) {
return SSH_ERROR;
@@ -2348,22 +2497,11 @@ int pki_key_check_hash_compatible(ssh_key key,
}
switch(key->type) {
- case SSH_KEYTYPE_DSS_CERT01:
- case SSH_KEYTYPE_DSS:
- if (hash_type == SSH_DIGEST_SHA1) {
- if (ssh_fips_mode()) {
- SSH_LOG(SSH_LOG_WARN, "SHA1 is not allowed in FIPS mode");
- return SSH_ERROR;
- } else {
- return SSH_OK;
- }
- }
- break;
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_RSA:
if (hash_type == SSH_DIGEST_SHA1) {
if (ssh_fips_mode()) {
- SSH_LOG(SSH_LOG_WARN, "SHA1 is not allowed in FIPS mode");
+ SSH_LOG(SSH_LOG_TRACE, "SHA1 is not allowed in FIPS mode");
return SSH_ERROR;
} else {
return SSH_OK;
@@ -2404,14 +2542,16 @@ int pki_key_check_hash_compatible(ssh_key key,
return SSH_OK;
}
break;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown key type %d", key->type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown key type %d", key->type);
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_WARN, "Key type %d incompatible with hash type %d",
+ SSH_LOG(SSH_LOG_TRACE, "Key type %d incompatible with hash type %d",
key->type, hash_type);
return SSH_ERROR;
@@ -2424,6 +2564,7 @@ int ssh_pki_signature_verify(ssh_session session,
size_t input_len)
{
int rc;
+ bool allowed;
enum ssh_keytypes_e key_type;
if (session == NULL || sig == NULL || key == NULL || input == NULL) {
@@ -2438,12 +2579,19 @@ int ssh_pki_signature_verify(ssh_session session,
sig->type_c);
if (key_type != sig->type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Can not verify %s signature with %s key",
sig->type_c, key->type_c);
return SSH_ERROR;
}
+ allowed = ssh_key_size_allowed(session, key);
+ if (!allowed) {
+ ssh_set_error(session, SSH_FATAL, "The '%s' key of size %d is not "
+ "allowed by RSA_MIN_SIZE", key->type_c, ssh_key_size(key));
+ 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) {
@@ -2462,7 +2610,7 @@ int ssh_pki_signature_verify(ssh_session session,
ctx = sha256_init();
if (ctx == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Can not create SHA256CTX for application hash");
return SSH_ERROR;
}
@@ -2472,7 +2620,7 @@ int ssh_pki_signature_verify(ssh_session session,
ctx = sha256_init();
if (ctx == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Can not create SHA256CTX for input hash");
return SSH_ERROR;
}
@@ -2562,11 +2710,11 @@ ssh_string ssh_pki_do_sign(ssh_session session,
}
/* Get the session ID */
- session_id = ssh_string_new(crypto->digest_len);
+ session_id = ssh_string_new(crypto->session_id_len);
if (session_id == NULL) {
return NULL;
}
- rc = ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ rc = ssh_string_fill(session_id, crypto->session_id, crypto->session_id_len);
if (rc < 0) {
goto end;
}
@@ -2609,7 +2757,6 @@ end:
return sig_blob;
}
-#ifndef _WIN32
ssh_string ssh_pki_do_sign_agent(ssh_session session,
struct ssh_buffer_struct *buf,
const ssh_key pubkey)
@@ -2626,11 +2773,11 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session,
}
/* prepend session identifier */
- session_id = ssh_string_new(crypto->digest_len);
+ session_id = ssh_string_new(crypto->session_id_len);
if (session_id == NULL) {
return NULL;
}
- rc = ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ rc = ssh_string_fill(session_id, crypto->session_id, crypto->session_id_len);
if (rc < 0) {
SSH_STRING_FREE(session_id);
return NULL;
@@ -2663,7 +2810,6 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session,
return sig_blob;
}
-#endif /* _WIN32 */
#ifdef WITH_SERVER
ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
@@ -2671,7 +2817,7 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
const enum ssh_digest_e digest)
{
struct ssh_crypto_struct *crypto = NULL;
-
+ bool allowed;
ssh_signature sig = NULL;
ssh_string sig_blob = NULL;
@@ -2683,11 +2829,17 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
return NULL;
}
+ allowed = ssh_key_size_allowed(session, privkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_FATAL, "The hostkey size too small");
+ return NULL;
+ }
+
crypto = session->next_crypto ? session->next_crypto :
session->current_crypto;
if (crypto->secret_hash == NULL){
- ssh_set_error(session,SSH_FATAL,"Missing secret_hash");
+ ssh_set_error(session, SSH_FATAL, "Missing secret_hash");
return NULL;
}
@@ -2708,9 +2860,9 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
/* Generate the signature */
sig = pki_do_sign(privkey,
- ssh_buffer_get(sign_input),
- ssh_buffer_get_len(sign_input),
- digest);
+ ssh_buffer_get(sign_input),
+ ssh_buffer_get_len(sign_input),
+ digest);
if (sig == NULL) {
goto end;
}
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index ecde4cdd..82afdf8e 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -49,7 +49,7 @@
* code.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -69,20 +69,20 @@ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
if (rc == SSH_ERROR){
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
return SSH_ERROR;
}
type = ssh_key_type_from_name(type_s);
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown key type '%s' found!", type_s);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown key type '%s' found!", type_s);
return SSH_ERROR;
}
SAFE_FREE(type_s);
rc = pki_import_privkey_buffer(type, key_blob_buffer, &key);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Failed to read key in OpenSSH format");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to read key in OpenSSH format");
goto fail;
}
@@ -133,17 +133,17 @@ static int pki_private_key_decrypt(ssh_string blob,
}
if (ciphers[i].name == NULL){
- SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported cipher %s", ciphername);
return SSH_ERROR;
}
cmp = strcmp(kdfname, "bcrypt");
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported KDF %s", kdfname);
return SSH_ERROR;
}
if (ssh_string_len(blob) % cipher.blocksize != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Encrypted string not multiple of blocksize: %zu",
ssh_string_len(blob));
return SSH_ERROR;
@@ -167,12 +167,12 @@ static int pki_private_key_decrypt(ssh_string blob,
/* We need material for key (keysize bits / 8) and IV (blocksize) */
key_material_len = cipher.keysize/8 + cipher.blocksize;
if (key_material_len > sizeof(key_material)) {
- SSH_LOG(SSH_LOG_WARN, "Key material too big");
+ SSH_LOG(SSH_LOG_TRACE, "Key material too big");
return SSH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG,
- "Decryption: %d key, %d IV, %d rounds, %zu bytes salt",
+ "Decryption: %d key, %d IV, %" PRIu32 " rounds, %zu bytes salt",
cipher.keysize/8,
cipher.blocksize,
rounds,
@@ -181,7 +181,7 @@ static int pki_private_key_decrypt(ssh_string blob,
if (passphrase == NULL) {
if (auth_fn == NULL) {
SAFE_FREE(salt);
- SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
+ SSH_LOG(SSH_LOG_TRACE, "No passphrase provided");
return SSH_ERROR;
}
rc = auth_fn("Passphrase",
@@ -251,7 +251,7 @@ ssh_pki_openssh_import(const char *text_key,
cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (no header)");
goto out;
}
ptr += strlen(OPENSSH_HEADER_BEGIN);
@@ -260,7 +260,7 @@ ssh_pki_openssh_import(const char *text_key,
}
end = strstr(ptr, OPENSSH_HEADER_END);
if (end == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (no footer)");
goto out;
}
base64 = malloc(end - ptr + 1);
@@ -277,7 +277,7 @@ ssh_pki_openssh_import(const char *text_key,
buffer = base64_to_bin(base64);
SAFE_FREE(base64);
if (buffer == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (base64 error)");
goto out;
}
rc = ssh_buffer_unpack(buffer, "PssSdSS",
@@ -290,21 +290,21 @@ ssh_pki_openssh_import(const char *text_key,
&pubkey0,
&privkeys);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (unpack error)");
goto out;
}
cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (bad magic)");
goto out;
}
- SSH_LOG(SSH_LOG_INFO,
- "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %" PRIu32,
ciphername,
kdfname,
nkeys);
if (nkeys != 1) {
- SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
+ SSH_LOG(SSH_LOG_TRACE, "Opening OpenSSH private key: only 1 key supported (%" PRIu32 " available)", nkeys);
goto out;
}
@@ -314,7 +314,7 @@ ssh_pki_openssh_import(const char *text_key,
if (!private) {
rc = ssh_pki_import_pubkey_blob(pubkey0, &key);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Failed to import public key blob");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to import public key blob");
}
/* in either case we clean up here */
goto out;
@@ -343,7 +343,7 @@ ssh_pki_openssh_import(const char *text_key,
rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
if (rc == SSH_ERROR || checkint1 != checkint2) {
- SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
+ SSH_LOG(SSH_LOG_TRACE, "OpenSSH private key unpack error (correct password?)");
goto out;
}
rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
@@ -358,7 +358,7 @@ ssh_pki_openssh_import(const char *text_key,
if (padding != i) {
ssh_key_free(key);
key = NULL;
- SSH_LOG(SSH_LOG_WARN, "Invalid padding");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid padding");
goto out;
}
}
@@ -395,37 +395,6 @@ ssh_key ssh_pki_openssh_pubkey_import(const char *text_key)
/** @internal
- * @brief exports a private key to a string blob.
- * @param[in] privkey private key to convert
- * @param[out] buffer buffer to write the blob in.
- * @returns SSH_OK on success
- * @warning only supports ed25519 key type at the moment.
- */
-static int pki_openssh_export_privkey_blob(const ssh_key privkey,
- ssh_buffer buffer)
-{
- int rc;
-
- if (privkey->type != SSH_KEYTYPE_ED25519) {
- SSH_LOG(SSH_LOG_WARN, "Type %s not supported", privkey->type_c);
- return SSH_ERROR;
- }
- if (privkey->ed25519_privkey == NULL ||
- privkey->ed25519_pubkey == NULL) {
- return SSH_ERROR;
- }
- rc = ssh_buffer_pack(buffer,
- "sdPdPP",
- privkey->type_c,
- (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;
-}
-
-/** @internal
* @brief encrypts an ed25519 private key blob
*
*/
@@ -462,29 +431,29 @@ static int pki_private_key_encrypt(ssh_buffer privkey_buffer,
}
if (ciphers[i].name == NULL){
- SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported cipher %s", ciphername);
return SSH_ERROR;
}
cmp = strcmp(kdfname, "bcrypt");
if (cmp != 0){
- SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported KDF %s", kdfname);
return SSH_ERROR;
}
/* We need material for key (keysize bits / 8) and IV (blocksize) */
key_material_len = cipher.keysize/8 + cipher.blocksize;
if (key_material_len > sizeof(key_material)){
- SSH_LOG(SSH_LOG_WARN, "Key material too big");
+ SSH_LOG(SSH_LOG_TRACE, "Key material too big");
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_WARN, "Encryption: %d key, %d IV, %d rounds, %zu bytes salt",
+ SSH_LOG(SSH_LOG_DEBUG, "Encryption: %d key, %d IV, %" PRIu32 " rounds, %zu bytes salt",
cipher.keysize/8,
cipher.blocksize, rounds, ssh_string_len(salt));
if (passphrase == NULL){
if (auth_fn == NULL){
- SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
+ SSH_LOG(SSH_LOG_TRACE, "No passphrase provided");
return SSH_ERROR;
}
rc = auth_fn("Passphrase",
@@ -536,8 +505,8 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
ssh_auth_callback auth_fn,
void *auth_data)
{
- ssh_buffer buffer;
- ssh_string str = NULL;
+ ssh_buffer buffer = NULL;
+ ssh_string str = NULL, blob = NULL;
ssh_string pubkey_s=NULL;
ssh_buffer privkey_buffer = NULL;
uint32_t rnd;
@@ -554,17 +523,13 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
if (privkey == NULL) {
return NULL;
}
- if (privkey->type != SSH_KEYTYPE_ED25519){
- SSH_LOG(SSH_LOG_WARN, "Unsupported key type %s", privkey->type_c);
- return NULL;
- }
if (passphrase != NULL || auth_fn != NULL){
- SSH_LOG(SSH_LOG_INFO, "Enabling encryption for private key export");
+ SSH_LOG(SSH_LOG_DEBUG, "Enabling encryption for private key export");
to_encrypt = 1;
}
buffer = ssh_buffer_new();
- pubkey_s = pki_publickey_to_blob(privkey);
- if(buffer == NULL || pubkey_s == NULL){
+ rc = ssh_pki_export_pubkey_blob(privkey, &pubkey_s);
+ if (buffer == NULL || rc != SSH_OK) {
goto error;
}
@@ -578,22 +543,17 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
goto error;
}
- /* checkint1 & 2 */
- rc = ssh_buffer_pack(privkey_buffer,
- "dd",
- rnd,
- rnd);
- if (rc == SSH_ERROR){
- goto error;
- }
-
- rc = pki_openssh_export_privkey_blob(privkey, privkey_buffer);
- if (rc == SSH_ERROR){
+ rc = ssh_pki_export_privkey_blob(privkey, &blob);
+ if (rc != SSH_OK) {
goto error;
}
- /* comment */
- rc = ssh_buffer_pack(privkey_buffer, "s", "" /* comment */);
+ rc = ssh_buffer_pack(privkey_buffer,
+ "ddPs",
+ rnd, /* checkint 1 & 2 */
+ rnd,
+ ssh_string_len(blob), ssh_string_data(blob),
+ "" /* comment */);
if (rc == SSH_ERROR){
goto error;
}
@@ -630,7 +590,11 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
goto error;
}
- ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
+ rc = ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
+ if (rc != SSH_OK) {
+ SSH_BUFFER_FREE(kdf_buf);
+ goto error;
+ }
kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf));
if (kdf_options == NULL){
SSH_BUFFER_FREE(kdf_buf);
@@ -706,6 +670,8 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
}
error:
+ ssh_string_burn(blob);
+ ssh_string_free(blob);
if (privkey_buffer != NULL) {
void *bufptr = ssh_buffer_get(privkey_buffer);
explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer));
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 4c95286b..f4ce8bdf 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -29,14 +29,24 @@
#include "config.h"
#include "libssh/priv.h"
+#include "libcrypto-compat.h"
#include <openssl/pem.h>
-#include <openssl/dsa.h>
-#include <openssl/err.h>
-#include <openssl/engine.h>
#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#include <openssl/dsa.h>
#include <openssl/rsa.h>
-#include "libcrypto-compat.h"
+#else
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#if defined(WITH_PKCS11_URI) && defined(WITH_PKCS11_PROVIDER)
+#include <openssl/store.h>
+#include <openssl/provider.h>
+#endif
+#endif /* OPENSSL_VERSION_NUMBER */
#ifdef HAVE_OPENSSL_EC_H
#include <openssl/ec.h>
@@ -81,12 +91,24 @@ static int pem_get_password(char *buf, int size, int rwflag, void *userdata) {
return 0;
}
+void pki_key_clean(ssh_key key)
+{
+ if (key == NULL)
+ return;
+ EVP_PKEY_free(key->key);
+ key->key = NULL;
+}
+
#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int pki_key_ecdsa_to_nid(EC_KEY *k)
{
const EC_GROUP *g = EC_KEY_get0_group(k);
int nid;
+ if (g == NULL) {
+ return -1;
+ }
nid = EC_GROUP_get_curve_name(g);
if (nid) {
return nid;
@@ -94,8 +116,30 @@ static int pki_key_ecdsa_to_nid(EC_KEY *k)
return -1;
}
+#else
+static int pki_key_ecdsa_to_nid(EVP_PKEY *k)
+{
+ char gname[25] = { 0 };
+ int rc;
+
+ rc = EVP_PKEY_get_utf8_string_param(k,
+ OSSL_PKEY_PARAM_GROUP_NAME,
+ gname,
+ 25,
+ NULL);
+ if (rc != 1) {
+ return -1;
+ }
+
+ return pki_key_ecgroup_name_to_nid(gname);
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(EC_KEY *k)
+#else
+static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(EVP_PKEY *k)
+#endif /* OPENSSL_VERSION_NUMBER */
{
int nid;
@@ -158,114 +202,157 @@ int pki_key_ecdsa_nid_from_name(const char *name)
return -1;
}
-static ssh_string make_ecpoint_string(const EC_GROUP *g,
- const EC_POINT *p)
+int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp)
{
- ssh_string s;
- size_t len;
-
- len = EC_POINT_point2oct(g,
- p,
- POINT_CONVERSION_UNCOMPRESSED,
- NULL,
- 0,
- NULL);
- if (len == 0) {
- return NULL;
- }
+ int rc = 0;
+ BIGNUM *bexp = NULL;
- s = ssh_string_new(len);
- if (s == NULL) {
- return NULL;
- }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_POINT *p = NULL;
+ const EC_GROUP *g = NULL;
+ EC_KEY *ecdsa = NULL;
+#else
+ const char *group_name = OSSL_EC_curve_nid2name(nid);
+ OSSL_PARAM_BLD *param_bld = NULL;
- len = EC_POINT_point2oct(g,
- p,
- POINT_CONVERSION_UNCOMPRESSED,
- ssh_string_data(s),
- ssh_string_len(s),
- NULL);
- if (len != ssh_string_len(s)) {
- SSH_STRING_FREE(s);
- return NULL;
+ if (group_name == NULL) {
+ return -1;
}
+#endif /* OPENSSL_VERSION_NUMBER */
- return s;
-}
-
-int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp)
-{
- EC_POINT *p = NULL;
- const EC_GROUP *g = NULL;
- int ok;
- BIGNUM *bexp = NULL;
+ bexp = ssh_make_string_bn(exp);
+ if (bexp == NULL) {
+ return -1;
+ }
key->ecdsa_nid = nid;
key->type_c = pki_key_ecdsa_nid_to_name(nid);
- key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (key->ecdsa == NULL) {
- return -1;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (ecdsa == NULL) {
+ rc = -1;
+ goto cleanup;
}
- g = EC_KEY_get0_group(key->ecdsa);
+ g = EC_KEY_get0_group(ecdsa);
p = EC_POINT_new(g);
if (p == NULL) {
- return -1;
+ rc = -1;
+ goto cleanup;
}
- ok = EC_POINT_oct2point(g,
+ rc = EC_POINT_oct2point(g,
p,
ssh_string_data(e),
ssh_string_len(e),
NULL);
- if (!ok) {
- EC_POINT_free(p);
- return -1;
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
}
/* EC_KEY_set_public_key duplicates p */
- ok = EC_KEY_set_public_key(key->ecdsa, p);
- EC_POINT_free(p);
- if (!ok) {
- return -1;
+ rc = EC_KEY_set_public_key(ecdsa, p);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
}
- bexp = ssh_make_string_bn(exp);
- if (bexp == NULL) {
- EC_KEY_free(key->ecdsa);
- return -1;
- }
/* EC_KEY_set_private_key duplicates exp */
- ok = EC_KEY_set_private_key(key->ecdsa, bexp);
+ rc = EC_KEY_set_private_key(ecdsa, bexp);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ /* ecdsa will be freed when the EVP_PKEY key->key is freed */
+ rc = EVP_PKEY_assign_EC_KEY(key->key, ecdsa);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+ /* ssh_key is now the owner of this memory */
+ ecdsa = NULL;
+
+ /* set rc to 0 if everything went well */
+ rc = 0;
+
+cleanup:
+ EC_KEY_free(ecdsa);
+ EC_POINT_free(p);
BN_free(bexp);
- if (!ok) {
- EC_KEY_free(key->ecdsa);
- return -1;
+ return rc;
+#else
+ param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL){
+ rc = -1;
+ goto cleanup;
}
- return 0;
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME,
+ group_name, strlen(group_name));
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY,
+ ssh_string_data(e), ssh_string_len(e));
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, bexp);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = evp_build_pkey("EC", param_bld, &(key->key), EVP_PKEY_KEYPAIR);
+
+cleanup:
+ OSSL_PARAM_BLD_free(param_bld);
+ BN_free(bexp);
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
{
+ int rc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_POINT *p = NULL;
const EC_GROUP *g = NULL;
+ EC_KEY *ecdsa = NULL;
int ok;
+#else
+ const char *group_name = OSSL_EC_curve_nid2name(nid);
+ OSSL_PARAM_BLD *param_bld;
+#endif /* OPENSSL_VERSION_NUMBER */
key->ecdsa_nid = nid;
key->type_c = pki_key_ecdsa_nid_to_name(nid);
- key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (key->ecdsa == NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (ecdsa == NULL) {
return -1;
}
- g = EC_KEY_get0_group(key->ecdsa);
+ g = EC_KEY_get0_group(ecdsa);
p = EC_POINT_new(g);
if (p == NULL) {
+ EC_KEY_free(ecdsa);
return -1;
}
@@ -275,24 +362,60 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
ssh_string_len(e),
NULL);
if (!ok) {
+ EC_KEY_free(ecdsa);
EC_POINT_free(p);
return -1;
}
/* EC_KEY_set_public_key duplicates p */
- ok = EC_KEY_set_public_key(key->ecdsa, p);
+ ok = EC_KEY_set_public_key(ecdsa, p);
EC_POINT_free(p);
if (!ok) {
+ EC_KEY_free(ecdsa);
+ return -1;
+ }
+
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ EC_KEY_free(ecdsa);
+ return -1;
+ }
+
+ rc = EVP_PKEY_assign_EC_KEY(key->key, ecdsa);
+ if (rc != 1) {
+ EC_KEY_free(ecdsa);
return -1;
}
return 0;
+#else
+ param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL)
+ goto err;
+
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME,
+ group_name, strlen(group_name));
+ if (rc != 1)
+ goto err;
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY,
+ ssh_string_data(e), ssh_string_len(e));
+ if (rc != 1)
+ goto err;
+
+ rc = evp_build_pkey("EC", param_bld, &(key->key), EVP_PKEY_PUBLIC_KEY);
+ OSSL_PARAM_BLD_free(param_bld);
+
+ return rc;
+err:
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+#endif /* OPENSSL_VERSION_NUMBER */
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
ssh_key pki_key_dup(const ssh_key key, int demote)
{
- ssh_key new;
+ ssh_key new = NULL;
int rc;
new = ssh_key_new();
@@ -300,10 +423,6 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
return NULL;
}
-#ifdef WITH_PKCS11_URI
- new->key = key->key;
-#endif
-
new->type = key->type;
new->type_c = key->type_c;
if (demote) {
@@ -313,75 +432,28 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
switch (key->type) {
- case SSH_KEYTYPE_DSS: {
- const BIGNUM *p = NULL, *q = NULL, *g = NULL,
- *pub_key = NULL, *priv_key = NULL;
- BIGNUM *np, *nq, *ng, *npub_key, *npriv_key;
- new->dsa = DSA_new();
- if (new->dsa == NULL) {
- goto fail;
- }
-
- /*
- * p = public prime number
- * q = public 160-bit subprime, q | p-1
- * g = public generator of subgroup
- * pub_key = public key y = g^x
- * priv_key = private key x
- */
- DSA_get0_pqg(key->dsa, &p, &q, &g);
- np = BN_dup(p);
- nq = BN_dup(q);
- ng = BN_dup(g);
- if (np == NULL || nq == NULL || ng == NULL) {
- BN_free(np);
- BN_free(nq);
- BN_free(ng);
- goto fail;
- }
-
- /* Memory management of np, nq and ng is transferred to DSA object */
- rc = DSA_set0_pqg(new->dsa, np, nq, ng);
- if (rc == 0) {
- BN_free(np);
- BN_free(nq);
- BN_free(ng);
- goto fail;
- }
-
- DSA_get0_key(key->dsa, &pub_key, &priv_key);
- npub_key = BN_dup(pub_key);
- if (npub_key == NULL) {
- goto fail;
- }
-
- /* Memory management of npubkey is transferred to DSA object */
- rc = DSA_set0_key(new->dsa, npub_key, NULL);
- if (rc == 0) {
- goto fail;
- }
-
- if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
- npriv_key = BN_dup(priv_key);
- if (npriv_key == NULL) {
- goto fail;
- }
-
- /* Memory management of npriv_key is transferred to DSA object */
- rc = DSA_set0_key(new->dsa, NULL, npriv_key);
- if (rc == 0) {
- goto fail;
- }
- }
-
- break;
- }
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1: {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const BIGNUM *n = NULL, *e = NULL, *d = NULL;
BIGNUM *nn, *ne, *nd;
- new->rsa = RSA_new();
- if (new->rsa == NULL) {
+ RSA *new_rsa = NULL;
+ const RSA *key_rsa = EVP_PKEY_get0_RSA(key->key);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+#ifdef WITH_PKCS11_URI
+ /* Take the PKCS#11 keys as they are */
+ if (key->flags & SSH_KEY_FLAG_PKCS11_URI && !demote) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ goto fail;
+ }
+ new->key = key->key;
+ return new;
+ }
+#endif /* WITH_PKCS11_URI */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ new_rsa = RSA_new();
+ if (new_rsa == NULL) {
goto fail;
}
@@ -395,18 +467,20 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
* dmq1 = d mod (q-1)
* iqmp = q^-1 mod p
*/
- RSA_get0_key(key->rsa, &n, &e, &d);
+ RSA_get0_key(key_rsa, &n, &e, &d);
nn = BN_dup(n);
ne = BN_dup(e);
if (nn == NULL || ne == NULL) {
+ RSA_free(new_rsa);
BN_free(nn);
BN_free(ne);
goto fail;
}
/* Memory management of nn and ne is transferred to RSA object */
- rc = RSA_set0_key(new->rsa, nn, ne, NULL);
+ rc = RSA_set0_key(new_rsa, nn, ne, NULL);
if (rc == 0) {
+ RSA_free(new_rsa);
BN_free(nn);
BN_free(ne);
goto fail;
@@ -419,43 +493,48 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
nd = BN_dup(d);
if (nd == NULL) {
+ RSA_free(new_rsa);
goto fail;
}
/* Memory management of nd is transferred to RSA object */
- rc = RSA_set0_key(new->rsa, NULL, NULL, nd);
+ rc = RSA_set0_key(new_rsa, NULL, NULL, nd);
if (rc == 0) {
+ RSA_free(new_rsa);
goto fail;
}
/* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the
* RSA operations are much faster when these values are available.
*/
- RSA_get0_factors(key->rsa, &p, &q);
+ RSA_get0_factors(key_rsa, &p, &q);
if (p != NULL && q != NULL) { /* need to set both of them */
np = BN_dup(p);
nq = BN_dup(q);
if (np == NULL || nq == NULL) {
+ RSA_free(new_rsa);
BN_free(np);
BN_free(nq);
goto fail;
}
/* Memory management of np and nq is transferred to RSA object */
- rc = RSA_set0_factors(new->rsa, np, nq);
+ rc = RSA_set0_factors(new_rsa, np, nq);
if (rc == 0) {
+ RSA_free(new_rsa);
BN_free(np);
BN_free(nq);
goto fail;
}
}
- RSA_get0_crt_params(key->rsa, &dmp1, &dmq1, &iqmp);
+ RSA_get0_crt_params(key_rsa, &dmp1, &dmq1, &iqmp);
if (dmp1 != NULL || dmq1 != NULL || iqmp != NULL) {
ndmp1 = BN_dup(dmp1);
ndmq1 = BN_dup(dmq1);
niqmp = BN_dup(iqmp);
if (ndmp1 == NULL || ndmq1 == NULL || niqmp == NULL) {
+ RSA_free(new_rsa);
BN_free(ndmp1);
BN_free(ndmq1);
BN_free(niqmp);
@@ -464,8 +543,9 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
/* Memory management of ndmp1, ndmq1 and niqmp is transferred
* to RSA object */
- rc = RSA_set0_crt_params(new->rsa, ndmp1, ndmq1, niqmp);
+ rc = RSA_set0_crt_params(new_rsa, ndmp1, ndmq1, niqmp);
if (rc == 0) {
+ RSA_free(new_rsa);
BN_free(ndmp1);
BN_free(ndmq1);
BN_free(niqmp);
@@ -474,6 +554,26 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
}
+ new->key = EVP_PKEY_new();
+ if (new->key == NULL) {
+ RSA_free(new_rsa);
+ goto fail;
+ }
+
+ rc = EVP_PKEY_assign_RSA(new->key, new_rsa);
+ if (rc != 1) {
+ EVP_PKEY_free(new->key);
+ RSA_free(new_rsa);
+ goto fail;
+ }
+
+ new_rsa = NULL;
+#else
+ rc = evp_dup_rsa_pkey(key, new, demote);
+ if (rc != SSH_OK) {
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
break;
}
case SSH_KEYTYPE_ECDSA_P256:
@@ -481,31 +581,73 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
case SSH_KEYTYPE_ECDSA_P521:
#ifdef HAVE_OPENSSL_ECC
new->ecdsa_nid = key->ecdsa_nid;
-
+#ifdef WITH_PKCS11_URI
+ /* Take the PKCS#11 keys as they are */
+ if (key->flags & SSH_KEY_FLAG_PKCS11_URI && !demote) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ goto fail;
+ }
+ new->key = key->key;
+ return new;
+ }
+#endif /* WITH_PKCS11_URI */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* privkey -> pubkey */
if (demote && ssh_key_is_private(key)) {
- const EC_POINT *p;
+ const EC_POINT *p = NULL;
+ EC_KEY *new_ecdsa = NULL, *old_ecdsa = NULL;
int ok;
- new->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (new->ecdsa == NULL) {
+ new_ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (new_ecdsa == NULL) {
goto fail;
}
- p = EC_KEY_get0_public_key(key->ecdsa);
+ old_ecdsa = EVP_PKEY_get0_EC_KEY(key->key);
+ if (old_ecdsa == NULL) {
+ EC_KEY_free(new_ecdsa);
+ goto fail;
+ }
+
+ p = EC_KEY_get0_public_key(old_ecdsa);
if (p == NULL) {
+ EC_KEY_free(new_ecdsa);
+ goto fail;
+ }
+
+ ok = EC_KEY_set_public_key(new_ecdsa, p);
+ if (ok != 1) {
+ EC_KEY_free(new_ecdsa);
goto fail;
}
- ok = EC_KEY_set_public_key(new->ecdsa, p);
- if (!ok) {
+ new->key = EVP_PKEY_new();
+ if (new->key == NULL) {
+ EC_KEY_free(new_ecdsa);
+ goto fail;
+ }
+
+ ok = EVP_PKEY_assign_EC_KEY(new->key, new_ecdsa);
+ if (ok != 1) {
+ EC_KEY_free(new_ecdsa);
goto fail;
}
} else {
- new->ecdsa = EC_KEY_dup(key->ecdsa);
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ goto fail;
+ }
+ new->key = key->key;
+ }
+#else
+ rc = evp_dup_ecdsa_pkey(key, new, demote);
+ if (rc != SSH_OK) {
+ goto fail;
}
+#endif /* OPENSSL_VERSION_NUMBER */
break;
-#endif
+#endif /* HAVE_OPENSSL_ECC */
case SSH_KEYTYPE_ED25519:
rc = pki_ed25519_key_dup(new, key);
if (rc != SSH_OK) {
@@ -525,164 +667,181 @@ fail:
}
int pki_key_generate_rsa(ssh_key key, int parameter){
- BIGNUM *e;
int rc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ BIGNUM *e = NULL;
+ RSA *key_rsa = NULL;
+#else
+ OSSL_PARAM params[3];
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ unsigned e = 65537;
+#endif /* OPENSSL_VERSION_NUMBER */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ e = BN_new();
+ key_rsa = RSA_new();
+ if (key_rsa == NULL) {
+ return SSH_ERROR;
+ }
- e = BN_new();
- key->rsa = RSA_new();
+ BN_set_word(e, 65537);
+ rc = RSA_generate_key_ex(key_rsa, parameter, e, NULL);
- BN_set_word(e, 65537);
- rc = RSA_generate_key_ex(key->rsa, parameter, e, NULL);
+ BN_free(e);
- BN_free(e);
+ if (rc <= 0 || key_rsa == NULL) {
+ return SSH_ERROR;
+ }
- if (rc <= 0 || key->rsa == NULL)
- return SSH_ERROR;
- return SSH_OK;
-}
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ RSA_free(key_rsa);
+ return SSH_ERROR;
+ }
-int pki_key_generate_dss(ssh_key key, int parameter){
- int rc;
- key->dsa = DSA_new();
- if (key->dsa == NULL) {
+ rc = EVP_PKEY_assign_RSA(key->key, key_rsa);
+ if (rc != 1) {
+ RSA_free(key_rsa);
+ EVP_PKEY_free(key->key);
return SSH_ERROR;
}
- rc = DSA_generate_parameters_ex(key->dsa,
- parameter,
- NULL, /* seed */
- 0, /* seed_len */
- NULL, /* counter_ret */
- NULL, /* h_ret */
- NULL); /* cb */
+
+ key_rsa = NULL;
+#else
+ key->key = NULL;
+
+ rc = EVP_PKEY_keygen_init(pctx);
if (rc != 1) {
- DSA_free(key->dsa);
- key->dsa = NULL;
+ EVP_PKEY_CTX_free(pctx);
return SSH_ERROR;
}
- rc = DSA_generate_key(key->dsa);
- if (rc != 1){
- DSA_free(key->dsa);
- key->dsa=NULL;
+
+ params[0] = OSSL_PARAM_construct_int("bits", &parameter);
+ params[1] = OSSL_PARAM_construct_uint("e", &e);
+ params[2] = OSSL_PARAM_construct_end();
+ rc = EVP_PKEY_CTX_set_params(pctx, params);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(pctx);
return SSH_ERROR;
}
- return SSH_OK;
+
+ rc = EVP_PKEY_generate(pctx, &(key->key));
+
+ EVP_PKEY_CTX_free(pctx);
+
+ if (rc != 1 || key->key == NULL)
+ return SSH_ERROR;
+#endif /* OPENSSL_VERSION_NUMBER */
+ return SSH_OK;
}
#ifdef HAVE_OPENSSL_ECC
-int pki_key_generate_ecdsa(ssh_key key, int parameter) {
+int pki_key_generate_ecdsa(ssh_key key, int parameter)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *ecdsa = NULL;
int ok;
-
+#else
+ const char *group_name = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
switch (parameter) {
+ case 256:
+ key->ecdsa_nid = NID_X9_62_prime256v1;
+ key->type = SSH_KEYTYPE_ECDSA_P256;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ group_name = NISTP256;
+#endif /* OPENSSL_VERSION_NUMBER */
+ break;
case 384:
key->ecdsa_nid = NID_secp384r1;
key->type = SSH_KEYTYPE_ECDSA_P384;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ group_name = NISTP384;
+#endif /* OPENSSL_VERSION_NUMBER */
break;
case 521:
key->ecdsa_nid = NID_secp521r1;
key->type = SSH_KEYTYPE_ECDSA_P521;
- break;
- case 256:
- key->ecdsa_nid = NID_X9_62_prime256v1;
- key->type = SSH_KEYTYPE_ECDSA_P256;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ group_name = NISTP521;
+#endif /* OPENSSL_VERSION_NUMBER */
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid parameter %d for ECDSA key "
+ SSH_LOG(SSH_LOG_TRACE, "Invalid parameter %d for ECDSA key "
"generation", parameter);
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (ecdsa == NULL) {
+ return SSH_ERROR;
+ }
+ ok = EC_KEY_generate_key(ecdsa);
+ if (!ok) {
+ EC_KEY_free(ecdsa);
+ return SSH_ERROR;
+ }
+
+ EC_KEY_set_asn1_flag(ecdsa, OPENSSL_EC_NAMED_CURVE);
- key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (key->ecdsa == NULL) {
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ EC_KEY_free(ecdsa);
return SSH_ERROR;
}
- ok = EC_KEY_generate_key(key->ecdsa);
- if (!ok) {
- EC_KEY_free(key->ecdsa);
+ ok = EVP_PKEY_assign_EC_KEY(key->key, ecdsa);
+ if (ok != 1) {
return SSH_ERROR;
}
- EC_KEY_set_asn1_flag(key->ecdsa, OPENSSL_EC_NAMED_CURVE);
+#else
+ key->key = EVP_EC_gen(group_name);
+ if (key->key == NULL) {
+ return SSH_ERROR;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
return SSH_OK;
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
+/* With OpenSSL 3.0 and higher the parameter 'what'
+ * is ignored and the comparison is done by OpenSSL
+ */
int pki_key_compare(const ssh_key k1,
const ssh_key k2,
enum ssh_keycmp_e what)
{
- switch (k1->type) {
- case SSH_KEYTYPE_DSS: {
- const BIGNUM *p1, *p2, *q1, *q2, *g1, *g2,
- *pub_key1, *pub_key2, *priv_key1, *priv_key2;
- if (DSA_size(k1->dsa) != DSA_size(k2->dsa)) {
- return 1;
- }
- DSA_get0_pqg(k1->dsa, &p1, &q1, &g1);
- DSA_get0_pqg(k2->dsa, &p2, &q2, &g2);
- if (bignum_cmp(p1, p2) != 0) {
- return 1;
- }
- if (bignum_cmp(q1, q2) != 0) {
- return 1;
- }
- if (bignum_cmp(g1, g2) != 0) {
- return 1;
- }
- DSA_get0_key(k1->dsa, &pub_key1, &priv_key1);
- DSA_get0_key(k2->dsa, &pub_key2, &priv_key2);
- if (bignum_cmp(pub_key1, pub_key2) != 0) {
- return 1;
- }
+ int rc;
- if (what == SSH_KEY_CMP_PRIVATE) {
- if (bignum_cmp(priv_key1, priv_key2) != 0) {
- return 1;
- }
- }
- break;
- }
- case SSH_KEYTYPE_RSA:
- case SSH_KEYTYPE_RSA1: {
- const BIGNUM *e1, *e2, *n1, *n2, *p1, *p2, *q1, *q2;
- if (RSA_size(k1->rsa) != RSA_size(k2->rsa)) {
- return 1;
- }
- RSA_get0_key(k1->rsa, &n1, &e1, NULL);
- RSA_get0_key(k2->rsa, &n2, &e2, NULL);
- if (bignum_cmp(e1, e2) != 0) {
- return 1;
- }
- if (bignum_cmp(n1, n2) != 0) {
- return 1;
- }
+ (void)what;
- if (what == SSH_KEY_CMP_PRIVATE) {
- RSA_get0_factors(k1->rsa, &p1, &q1);
- RSA_get0_factors(k2->rsa, &p2, &q2);
- if (bignum_cmp(p1, p2) != 0) {
- return 1;
- }
-
- if (bignum_cmp(q1, q2) != 0) {
- return 1;
- }
- }
- break;
- }
+ switch (ssh_key_type_plain(k1->type)) {
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#ifdef HAVE_OPENSSL_ECC
{
- const EC_POINT *p1 = EC_KEY_get0_public_key(k1->ecdsa);
- const EC_POINT *p2 = EC_KEY_get0_public_key(k2->ecdsa);
- const EC_GROUP *g1 = EC_KEY_get0_group(k1->ecdsa);
- const EC_GROUP *g2 = EC_KEY_get0_group(k2->ecdsa);
+ const EC_KEY *ec1 = EVP_PKEY_get0_EC_KEY(k1->key);
+ const EC_KEY *ec2 = EVP_PKEY_get0_EC_KEY(k2->key);
+ const EC_POINT *p1 = NULL;
+ const EC_POINT *p2 = NULL;
+ const EC_GROUP *g1 = NULL;
+ const EC_GROUP *g2 = NULL;
+
+ if (ec1 == NULL || ec2 == NULL) {
+ return 1;
+ }
- if (p1 == NULL || p2 == NULL) {
+ p1 = EC_KEY_get0_public_key(ec1);
+ p2 = EC_KEY_get0_public_key(ec2);
+ g1 = EC_KEY_get0_group(ec1);
+ g2 = EC_KEY_get0_group(ec2);
+
+ if (p1 == NULL || p2 == NULL || g1 == NULL || g2 == NULL) {
return 1;
}
@@ -695,23 +854,29 @@ int pki_key_compare(const ssh_key k1,
}
if (what == SSH_KEY_CMP_PRIVATE) {
- if (bignum_cmp(EC_KEY_get0_private_key(k1->ecdsa),
- EC_KEY_get0_private_key(k2->ecdsa))) {
+ if (bignum_cmp(EC_KEY_get0_private_key(ec1),
+ EC_KEY_get0_private_key(ec2))) {
return 1;
}
}
-
break;
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
+#endif /* OPENSSL_VERSION_NUMBER */
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ rc = EVP_PKEY_eq(k1->key, k2->key);
+ if (rc != 1) {
+ return 1;
+ }
+ break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- /* ed25519 keys handled globaly */
+ /* ed25519 keys handled globally */
case SSH_KEYTYPE_UNKNOWN:
default:
return 1;
}
-
return 0;
}
@@ -732,37 +897,22 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
}
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) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
goto err;
}
+ pkey = key->key;
+
+ /* Mark the operation as successful as for the other key types */
+ rc = 1;
- 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) */
@@ -779,11 +929,6 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
/* 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:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -791,11 +936,11 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d", key->type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d", key->type);
goto err;
}
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARN, "Failed to initialize EVP_PKEY structure");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to initialize EVP_PKEY structure");
goto err;
}
@@ -822,6 +967,9 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
pkey = NULL;
if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Failed to write private key: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
goto err;
}
@@ -854,20 +1002,13 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
void *auth_data)
{
BIO *mem = NULL;
- DSA *dsa = NULL;
- RSA *rsa = NULL;
-#ifdef HAVE_OPENSSL_ED25519
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *ecdsa = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
uint8_t *ed25519 = NULL;
-#else
- ed25519_privkey *ed25519 = NULL;
-#endif
+ uint8_t *ed25519_pubkey = NULL;
ssh_key key = NULL;
enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN;
-#ifdef HAVE_OPENSSL_ECC
- EC_KEY *ecdsa = NULL;
-#else
- void *ecdsa = NULL;
-#endif
EVP_PKEY *pkey = NULL;
mem = BIO_new_mem_buf((void*)b64_key, -1);
@@ -888,53 +1029,41 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
BIO_free(mem);
if (pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
+ SSH_LOG(SSH_LOG_TRACE,
+ "Error parsing private key: %s",
ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
switch (EVP_PKEY_base_id(pkey)) {
- case EVP_PKEY_DSA:
- dsa = EVP_PKEY_get1_DSA(pkey);
- if (dsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
- ERR_error_string(ERR_get_error(),NULL));
- goto fail;
- }
- type = SSH_KEYTYPE_DSS;
- break;
case EVP_PKEY_RSA:
- rsa = EVP_PKEY_get1_RSA(pkey);
- if (rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
- ERR_error_string(ERR_get_error(),NULL));
- goto fail;
- }
type = SSH_KEYTYPE_RSA;
break;
case EVP_PKEY_EC:
#ifdef HAVE_OPENSSL_ECC
- ecdsa = EVP_PKEY_get1_EC_KEY(pkey);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EVP_PKEY_get0_EC_KEY(pkey);
if (ecdsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
+ SSH_LOG(SSH_LOG_TRACE,
+ "Error parsing private key: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
+#endif /* OPENSSL_VERSION_NUMBER */
/* pki_privatekey_type_from_string always returns P256 for ECDSA
* keys, so we need to figure out the correct type here */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
type = pki_key_ecdsa_to_key_type(ecdsa);
+#else
+ type = pki_key_ecdsa_to_key_type(pkey);
+#endif /* OPENSSL_VERSION_NUMBER */
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid private key.");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid private key.");
goto fail;
}
break;
-#endif
-#ifdef HAVE_OPENSSL_ED25519
+#endif /* HAVE_OPENSSL_ECC */
case EVP_PKEY_ED25519:
{
size_t key_len;
@@ -955,7 +1084,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
ed25519 = malloc(key_len);
if (ed25519 == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory");
goto fail;
}
@@ -967,17 +1096,32 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
+
+ /* length matches the private key length */
+ ed25519_pubkey = malloc(ED25519_KEY_LEN);
+ if (ed25519_pubkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory");
+ goto fail;
+ }
+
+ evp_rc = EVP_PKEY_get_raw_public_key(pkey, (uint8_t *)ed25519_pubkey,
+ &key_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 fail;
+ }
type = SSH_KEYTYPE_ED25519;
+
}
break;
-#endif
default:
- EVP_PKEY_free(pkey);
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d",
EVP_PKEY_base_id(pkey));
+ EVP_PKEY_free(pkey);
return NULL;
}
- EVP_PKEY_free(pkey);
key = ssh_key_new();
if (key == NULL) {
@@ -987,164 +1131,207 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
key->type = type;
key->type_c = ssh_key_type_to_char(type);
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- key->dsa = dsa;
- key->rsa = rsa;
- key->ecdsa = ecdsa;
+ key->key = pkey;
key->ed25519_privkey = ed25519;
+ key->ed25519_pubkey = ed25519_pubkey;
#ifdef HAVE_OPENSSL_ECC
if (is_ecdsa_key_type(key->type)) {
- key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(ecdsa);
+#else
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(key->key);
+#endif /* OPENSSL_VERSION_NUMBER */
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
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
+ SAFE_FREE(ed25519_pubkey);
return NULL;
}
-int pki_privkey_build_dss(ssh_key key,
+int pki_privkey_build_rsa(ssh_key key,
+ ssh_string n,
+ ssh_string e,
+ ssh_string d,
+ ssh_string iqmp,
ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey,
- ssh_string privkey)
+ ssh_string q)
{
int rc;
- BIGNUM *bp, *bq, *bg, *bpub_key, *bpriv_key;
-
- key->dsa = DSA_new();
- if (key->dsa == NULL) {
+ BIGNUM *be = NULL, *bn = NULL, *bd = NULL;
+ BIGNUM *biqmp = NULL, *bp = NULL, *bq = NULL;
+ BIGNUM *aux = NULL, *d_consttime = NULL;
+ BIGNUM *bdmq1 = NULL, *bdmp1 = NULL;
+ BN_CTX *ctx = NULL;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ return SSH_ERROR;
+ }
+#else
+ RSA *key_rsa = RSA_new();
+ if (key_rsa == NULL) {
return SSH_ERROR;
}
+#endif /* OPENSSL_VERSION_NUMBER */
+ bn = ssh_make_string_bn(n);
+ be = ssh_make_string_bn(e);
+ bd = ssh_make_string_bn(d);
+ biqmp = ssh_make_string_bn(iqmp);
bp = ssh_make_string_bn(p);
bq = ssh_make_string_bn(q);
- bg = ssh_make_string_bn(g);
- bpub_key = ssh_make_string_bn(pubkey);
- bpriv_key = ssh_make_string_bn(privkey);
- if (bp == NULL || bq == NULL ||
- bg == NULL || bpub_key == NULL) {
+ if (be == NULL || bn == NULL || bd == NULL ||
+ /*biqmp == NULL ||*/ bp == NULL || bq == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
- /* Memory management of bp, qq and bg is transferred to DSA object */
- rc = DSA_set0_pqg(key->dsa, bp, bq, bg);
- if (rc == 0) {
+ /* Calculate remaining CRT parameters for OpenSSL to be happy
+ * taken from OpenSSH */
+ if ((ctx = BN_CTX_new()) == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
-
- /* Memory management of bpub_key and bpriv_key is transferred to DSA object */
- rc = DSA_set0_key(key->dsa, bpub_key, bpriv_key);
- if (rc == 0) {
+ if ((aux = BN_new()) == NULL ||
+ (bdmq1 = BN_new()) == NULL ||
+ (bdmp1 = BN_new()) == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+ if ((d_consttime = BN_dup(bd)) == NULL) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ BN_set_flags(aux, BN_FLG_CONSTTIME);
+ BN_set_flags(d_consttime, BN_FLG_CONSTTIME);
- return SSH_OK;
-fail:
- DSA_free(key->dsa);
- return SSH_ERROR;
-}
-
-int pki_pubkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey) {
- int rc;
- BIGNUM *bp = NULL, *bq = NULL, *bg = NULL, *bpub_key = NULL;
-
- key->dsa = DSA_new();
- if (key->dsa == NULL) {
- return SSH_ERROR;
+ if ((BN_sub(aux, bq, BN_value_one()) == 0) ||
+ (BN_mod(bdmq1, d_consttime, aux, ctx) == 0) ||
+ (BN_sub(aux, bp, BN_value_one()) == 0) ||
+ (BN_mod(bdmp1, d_consttime, aux, ctx) == 0)) {
+ rc = SSH_ERROR;
+ goto fail;
}
- bp = ssh_make_string_bn(p);
- bq = ssh_make_string_bn(q);
- bg = ssh_make_string_bn(g);
- bpub_key = ssh_make_string_bn(pubkey);
- if (bp == NULL || bq == NULL ||
- bg == NULL || bpub_key == NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* Memory management of be, bn and bd is transferred to RSA object */
+ rc = RSA_set0_key(key_rsa, bn, be, bd);
+ if (rc == 0) {
goto fail;
}
- /* Memory management of bp, bq and bg is transferred to DSA object */
- rc = DSA_set0_pqg(key->dsa, bp, bq, bg);
+ /* Memory management of bp and bq is transferred to RSA object */
+ rc = RSA_set0_factors(key_rsa, bp, bq);
if (rc == 0) {
goto fail;
}
- /* Memory management of npub_key is transferred to DSA object */
- rc = DSA_set0_key(key->dsa, bpub_key, NULL);
+ /* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the RSA
+ * operations are much faster when these values are available.
+ * https://www.openssl.org/docs/man1.0.2/crypto/rsa.html
+ * And OpenSSL fails to export these keys to PEM if these are missing:
+ * https://github.com/openssl/openssl/issues/21826
+ */
+ rc = RSA_set0_crt_params(key_rsa, bdmp1, bdmq1, biqmp);
if (rc == 0) {
goto fail;
}
+ bignum_safe_free(aux);
+ bignum_safe_free(d_consttime);
+
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ goto fail;
+ }
+
+ rc = EVP_PKEY_assign_RSA(key->key, key_rsa);
+ if (rc != 1) {
+ goto fail;
+ }
return SSH_OK;
fail:
- DSA_free(key->dsa);
+ RSA_free(key_rsa);
+ EVP_PKEY_free(key->key);
return SSH_ERROR;
-}
+#else
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, bn);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, be);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, bd);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
-int pki_privkey_build_rsa(ssh_key key,
- ssh_string n,
- ssh_string e,
- ssh_string d,
- UNUSED_PARAM(ssh_string iqmp),
- ssh_string p,
- ssh_string q)
-{
- int rc;
- BIGNUM *be, *bn, *bd/*, *biqmp*/, *bp, *bq;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_FACTOR1, bp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
- key->rsa = RSA_new();
- if (key->rsa == NULL) {
- return SSH_ERROR;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_FACTOR2, bq);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
}
- bn = ssh_make_string_bn(n);
- be = ssh_make_string_bn(e);
- bd = ssh_make_string_bn(d);
- /*biqmp = ssh_make_string_bn(iqmp);*/
- bp = ssh_make_string_bn(p);
- bq = ssh_make_string_bn(q);
- if (be == NULL || bn == NULL || bd == NULL ||
- /*biqmp == NULL ||*/ bp == NULL || bq == NULL) {
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, bdmp1);
+ if (rc != 1) {
+ rc = SSH_ERROR;
goto fail;
}
- /* Memory management of be, bn and bd is transferred to RSA object */
- rc = RSA_set0_key(key->rsa, bn, be, bd);
- if (rc == 0) {
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, bdmq1);
+ if (rc != 1) {
+ rc = SSH_ERROR;
goto fail;
}
- /* Memory management of bp and bq is transferred to RSA object */
- rc = RSA_set0_factors(key->rsa, bp, bq);
- if (rc == 0) {
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, biqmp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
goto fail;
}
- /* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the RSA
- * operations are much faster when these values are available.
- * https://www.openssl.org/docs/man1.0.2/crypto/rsa.html
- */
- /* RSA_set0_crt_params(key->rsa, biqmp, NULL, NULL);
- TODO calculate missing crt_params */
+ rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_KEYPAIR);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Failed to import private key: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ rc = SSH_ERROR;
+ goto fail;
+ }
- return SSH_OK;
fail:
- RSA_free(key->rsa);
- return SSH_ERROR;
+ OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ bignum_safe_free(bd);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+ bignum_safe_free(biqmp);
+
+ bignum_safe_free(aux);
+ bignum_safe_free(d_consttime);
+ bignum_safe_free(bdmp1);
+ bignum_safe_free(bdmq1);
+ BN_CTX_free(ctx);
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
int pki_pubkey_build_rsa(ssh_key key,
@@ -1152,31 +1339,71 @@ int pki_pubkey_build_rsa(ssh_key key,
ssh_string n) {
int rc;
BIGNUM *be = NULL, *bn = NULL;
-
- key->rsa = RSA_new();
- if (key->rsa == NULL) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ return SSH_ERROR;
+ }
+#else
+ RSA *key_rsa = RSA_new();
+ if (key_rsa == NULL) {
return SSH_ERROR;
}
+#endif /* OPENSSL_VERSION_NUMBER */
be = ssh_make_string_bn(e);
bn = ssh_make_string_bn(n);
if (be == NULL || bn == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Memory management of bn and be is transferred to RSA object */
- rc = RSA_set0_key(key->rsa, bn, be, NULL);
+ rc = RSA_set0_key(key_rsa, bn, be, NULL);
if (rc == 0) {
goto fail;
}
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ goto fail;
+ }
+
+ rc = EVP_PKEY_assign_RSA(key->key, key_rsa);
+ if (rc != 1) {
+ goto fail;
+ }
+
return SSH_OK;
fail:
- RSA_free(key->rsa);
+ EVP_PKEY_free(key->key);
+ RSA_free(key_rsa);
return SSH_ERROR;
+#else
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, bn);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, be);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+
+ rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_PUBLIC_KEY);
+
+fail:
+ OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
-ssh_string pki_publickey_to_blob(const ssh_key key)
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
{
ssh_buffer buffer;
ssh_string type_s;
@@ -1186,7 +1413,15 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
ssh_string p = NULL;
ssh_string g = NULL;
ssh_string q = NULL;
+ ssh_string d = NULL;
+ ssh_string iqmp = NULL;
int rc;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ BIGNUM *bp = NULL, *bq = NULL, *bg = NULL, *bpub_key = NULL,
+ *bn = NULL, *be = NULL,
+ *bd = NULL, *biqmp = NULL;
+ OSSL_PARAM *params = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
buffer = ssh_buffer_new();
if (buffer == NULL) {
@@ -1216,62 +1451,37 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
}
switch (key->type) {
- case SSH_KEYTYPE_DSS: {
- const BIGNUM *bp, *bq, *bg, *bpub_key;
- DSA_get0_pqg(key->dsa, &bp, &bq, &bg);
- p = ssh_make_bignum_string((BIGNUM *)bp);
- if (p == NULL) {
- goto fail;
- }
-
- q = ssh_make_bignum_string((BIGNUM *)bq);
- if (q == NULL) {
- goto fail;
- }
-
- g = ssh_make_bignum_string((BIGNUM *)bg);
- if (g == NULL) {
- goto fail;
- }
-
- DSA_get0_key(key->dsa, &bpub_key, NULL);
- n = ssh_make_bignum_string((BIGNUM *)bpub_key);
- if (n == NULL) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1: {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const BIGNUM *be = NULL, *bn = NULL;
+ const RSA *key_rsa = EVP_PKEY_get0_RSA(key->key);
+ RSA_get0_key(key_rsa, &bn, &be, NULL);
+#else
+ const OSSL_PARAM *out_param = NULL;
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, &params);
+ if (rc != 1) {
goto fail;
}
-
- if (ssh_buffer_add_ssh_string(buffer, p) < 0) {
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param E has been found");
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, q) < 0) {
+ rc = OSSL_PARAM_get_BN(out_param, &be);
+ if (rc != 1) {
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, g) < 0) {
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param N has been found");
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
+ rc = OSSL_PARAM_get_BN(out_param, &bn);
+ if (rc != 1) {
goto fail;
}
-
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- p = NULL;
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- g = NULL;
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- q = NULL;
- ssh_string_burn(n);
- SSH_STRING_FREE(n);
- n = NULL;
-
- break;
- }
- case SSH_KEYTYPE_RSA:
- case SSH_KEYTYPE_RSA1: {
- const BIGNUM *be, *bn;
- RSA_get0_key(key->rsa, &bn, &be, NULL);
+#endif /* OPENSSL_VERSION_NUMBER */
e = ssh_make_bignum_string((BIGNUM *)be);
if (e == NULL) {
goto fail;
@@ -1282,31 +1492,165 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, e) < 0) {
- goto fail;
- }
- if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
- goto fail;
- }
+ if (type == SSH_KEY_PUBLIC) {
+ /* The N and E parts are swapped in the public key export ! */
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ } else if (type == SSH_KEY_PRIVATE) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const BIGNUM *bd, *biqmp, *bp, *bq;
+ RSA_get0_key(key_rsa, NULL, NULL, &bd);
+ RSA_get0_factors(key_rsa, &bp, &bq);
+ RSA_get0_crt_params(key_rsa, NULL, NULL, &biqmp);
+#else
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, &params);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param D has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &bd);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR1);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param P has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &bp);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR2);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param Q has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &bq);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_COEFFICIENT1);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param IQMP has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &biqmp);
+ if (rc != 1) {
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ d = ssh_make_bignum_string((BIGNUM *)bd);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ iqmp = ssh_make_bignum_string((BIGNUM *)biqmp);
+ if (iqmp == NULL) {
+ goto fail;
+ }
+
+ p = ssh_make_bignum_string((BIGNUM *)bp);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ q = ssh_make_bignum_string((BIGNUM *)bq);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, iqmp);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, p);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, q);
+ if (rc < 0) {
+ goto fail;
+ }
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ ssh_string_burn(iqmp);
+ SSH_STRING_FREE(iqmp);
+ iqmp = NULL;
+ ssh_string_burn(p);
+ SSH_STRING_FREE(p);
+ p = NULL;
+ ssh_string_burn(q);
+ SSH_STRING_FREE(q);
+ q = NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(bd);
+ bignum_safe_free(biqmp);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+#endif /* OPENSSL_VERSION_NUMBER */
+ }
ssh_string_burn(e);
SSH_STRING_FREE(e);
e = NULL;
ssh_string_burn(n);
SSH_STRING_FREE(n);
n = NULL;
-
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
break;
}
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- rc = pki_ed25519_public_key_to_blob(buffer, key);
- if (rc == SSH_ERROR){
- goto fail;
- }
- if (key->type == SSH_KEYTYPE_SK_ED25519 &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PUBLIC) {
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR){
+ goto fail;
+ }
+ /* public key can contain certificate sk information */
+ if (key->type == SSH_KEYTYPE_SK_ED25519) {
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+ } else {
+ rc = pki_ed25519_private_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR){
+ goto fail;
+ }
}
break;
case SSH_KEYTYPE_ECDSA_P256:
@@ -1314,50 +1658,150 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
#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);
- return NULL;
- }
+ {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EC_GROUP *group = NULL;
+ EC_POINT *point = NULL;
+ const void *pubkey;
+ size_t pubkey_len;
+ OSSL_PARAM *locate_param = NULL;
+#else
+ const EC_GROUP *group = NULL;
+ const EC_POINT *point = NULL;
+ const BIGNUM *exp = NULL;
+ EC_KEY *ec = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
+
+ type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid));
+ if (type_s == NULL) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
- rc = ssh_buffer_add_ssh_string(buffer, type_s);
- SSH_STRING_FREE(type_s);
- if (rc < 0) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
- }
+ rc = ssh_buffer_add_ssh_string(buffer, type_s);
+ SSH_STRING_FREE(type_s);
+ if (rc < 0) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ec = EVP_PKEY_get0_EC_KEY(key->key);
+ if (ec == NULL) {
+ goto fail;
+ }
#ifdef WITH_PKCS11_URI
- if (ssh_key_is_private(key) && !EC_KEY_get0_public_key(key->ecdsa)) {
- SSH_LOG(SSH_LOG_INFO, "It is mandatory to have separate public"
- " ECDSA key objects in the PKCS #11 device. Unlike RSA,"
- " ECDSA public keys cannot be derived from their private keys.");
- goto fail;
- }
-#endif
- e = make_ecpoint_string(EC_KEY_get0_group(key->ecdsa),
- EC_KEY_get0_public_key(key->ecdsa));
- if (e == NULL) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
- }
+ if (ssh_key_is_private(key) && !EC_KEY_get0_public_key(ec)) {
+ SSH_LOG(SSH_LOG_TRACE, "It is mandatory to have separate"
+ " public ECDSA key objects in the PKCS #11 device."
+ " Unlike RSA, ECDSA public keys cannot be derived"
+ " from their private keys.");
+ goto fail;
+ }
+#endif /* WITH_PKCS11_URI */
+ group = EC_KEY_get0_group(ec);
+ point = EC_KEY_get0_public_key(ec);
+ if (group == NULL || point == NULL) {
+ goto fail;
+ }
+ e = pki_key_make_ecpoint_string(group, point);
+#else
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, &params);
+ if (rc < 0) {
+ goto fail;
+ }
- rc = ssh_buffer_add_ssh_string(buffer, e);
- if (rc < 0) {
- goto fail;
- }
+ locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
+#ifdef WITH_PKCS11_URI
+ if (ssh_key_is_private(key) && !locate_param) {
+ SSH_LOG(SSH_LOG_TRACE, "It is mandatory to have separate"
+ " public ECDSA key objects in the PKCS #11 device."
+ " Unlike RSA, ECDSA public keys cannot be derived"
+ " from their private keys.");
+ goto fail;
+ }
+#endif /* WITH_PKCS11_URI */
- ssh_string_burn(e);
- SSH_STRING_FREE(e);
- e = NULL;
+ rc = OSSL_PARAM_get_octet_string_ptr(locate_param, &pubkey, &pubkey_len);
+ if (rc != 1) {
+ goto fail;
+ }
+ /* Convert the data to low-level representation */
+ group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, key->ecdsa_nid);
+ point = EC_POINT_new(group);
+ rc = EC_POINT_oct2point(group, point, pubkey, pubkey_len, NULL);
+ if (group == NULL || point == NULL || rc != 1) {
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+ goto fail;
+ }
- if (key->type == SSH_KEYTYPE_SK_ECDSA &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
- }
+ e = pki_key_make_ecpoint_string(group, point);
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (e == NULL) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
- break;
-#endif
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(e);
+ SSH_STRING_FREE(e);
+ e = NULL;
+ if (type == SSH_KEY_PRIVATE) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, &params);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ rc = OSSL_PARAM_get_BN(locate_param, &bd);
+ if (rc != 1) {
+ goto fail;
+ }
+ d = ssh_make_bignum_string((BIGNUM *)bd);
+ if (d == NULL) {
+ goto fail;
+ }
+ if (ssh_buffer_add_ssh_string(buffer, d) < 0) {
+ goto fail;
+ }
+#else
+ exp = EC_KEY_get0_private_key(ec);
+ if (exp == NULL) {
+ goto fail;
+ }
+ d = ssh_make_bignum_string((BIGNUM *)exp);
+ if (d == NULL) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ } else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
+ /* public key can contain certificate sk information */
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
+ break;
+ }
+#endif /* HAVE_OPENSSL_ECC */
case SSH_KEYTYPE_UNKNOWN:
default:
goto fail;
@@ -1390,96 +1834,25 @@ fail:
SSH_STRING_FREE(q);
ssh_string_burn(n);
SSH_STRING_FREE(n);
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ ssh_string_burn(iqmp);
+ SSH_STRING_FREE(iqmp);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+ bignum_safe_free(bg);
+ bignum_safe_free(bpub_key);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ bignum_safe_free(bd);
+ bignum_safe_free(biqmp);
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
return NULL;
}
-static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
-{
- char buffer[40] = { 0 };
- ssh_string sig_blob = NULL;
- const BIGNUM *pr = NULL, *ps = NULL;
-
- ssh_string r = NULL;
- int r_len, r_offset_in, r_offset_out;
-
- ssh_string s = NULL;
- int s_len, s_offset_in, s_offset_out;
-
- const unsigned char *raw_sig_data = NULL;
- size_t raw_sig_len;
- int rc;
-
- DSA_SIG *dsa_sig;
-
- if (sig == NULL || sig->raw_sig == NULL) {
- return NULL;
- }
- raw_sig_data = ssh_string_data(sig->raw_sig);
- if (raw_sig_data == NULL) {
- return NULL;
- }
- raw_sig_len = ssh_string_len(sig->raw_sig);
-
- dsa_sig = d2i_DSA_SIG(NULL, &raw_sig_data, raw_sig_len);
- if (dsa_sig == NULL) {
- return NULL;
- }
-
- DSA_SIG_get0(dsa_sig, &pr, &ps);
- if (pr == NULL || ps == NULL) {
- goto error;
- }
-
- r = ssh_make_bignum_string((BIGNUM *)pr);
- if (r == NULL) {
- goto error;
- }
-
- s = ssh_make_bignum_string((BIGNUM *)ps);
- if (s == NULL) {
- goto error;
- }
-
- r_len = ssh_string_len(r);
- r_offset_in = (r_len > 20) ? (r_len - 20) : 0;
- r_offset_out = (r_len < 20) ? (20 - r_len) : 0;
-
- s_len = ssh_string_len(s);
- s_offset_in = (s_len > 20) ? (s_len - 20) : 0;
- s_offset_out = (s_len < 20) ? (20 - s_len) : 0;
-
- memcpy(buffer + r_offset_out,
- ((char *)ssh_string_data(r)) + r_offset_in,
- r_len - r_offset_in);
- memcpy(buffer + 20 + s_offset_out,
- ((char *)ssh_string_data(s)) + s_offset_in,
- s_len - s_offset_in);
-
- DSA_SIG_free(dsa_sig);
- SSH_STRING_FREE(r);
- SSH_STRING_FREE(s);
-
- sig_blob = ssh_string_new(40);
- if (sig_blob == NULL) {
- return NULL;
- }
-
- rc = ssh_string_fill(sig_blob, buffer, 40);
- if (rc < 0) {
- SSH_STRING_FREE(sig_blob);
- return NULL;
- }
-
- return sig_blob;
-
-error:
- DSA_SIG_free(dsa_sig);
- SSH_STRING_FREE(r);
- SSH_STRING_FREE(s);
- return NULL;
-}
-
static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig)
{
ssh_string r = NULL;
@@ -1572,9 +1945,6 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
ssh_string sig_blob = NULL;
switch(sig->type) {
- case SSH_KEYTYPE_DSS:
- sig_blob = pki_dsa_signature_to_blob(sig);
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
sig_blob = ssh_string_copy(sig->raw_sig);
@@ -1588,10 +1958,10 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
#ifdef HAVE_OPENSSL_ECC
sig_blob = pki_ecdsa_signature_to_blob(sig);
break;
-#endif
+#endif /* HAVE_OPENSSL_ECC */
default:
case SSH_KEYTYPE_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %s", sig->type_c);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %s", sig->type_c);
return NULL;
}
@@ -1610,14 +1980,25 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey,
size_t rsalen = 0;
size_t len = ssh_string_len(sig_blob);
- if (pubkey->rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Pubkey RSA field NULL");
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const RSA *rsa = EVP_PKEY_get0_RSA(pubkey->key);
+
+ if (rsa == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA field NULL");
goto errout;
}
- rsalen = RSA_size(pubkey->rsa);
+ rsalen = RSA_size(rsa);
+#else
+ if (EVP_PKEY_get_base_id(pubkey->key) != EVP_PKEY_RSA) {
+ SSH_LOG(SSH_LOG_TRACE, "Key has no RSA pubkey");
+ goto errout;
+ }
+
+ rsalen = EVP_PKEY_size(pubkey->key);
+#endif /* OPENSSL_VERSION_NUMBER */
if (len > rsalen) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature is too big: %lu > %lu",
(unsigned long)len,
(unsigned long)rsalen);
@@ -1625,9 +2006,9 @@ 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_LOG(SSH_LOG_DEBUG, "RSA signature len: %lu", (unsigned long)len);
ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len);
-#endif
+#endif /* DEBUG_CRYPTO */
if (len == rsalen) {
sig->raw_sig = ssh_string_copy(sig_blob);
@@ -1667,133 +2048,6 @@ errout:
return SSH_ERROR;
}
-static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey),
- const ssh_string sig_blob,
- ssh_signature sig)
-{
- DSA_SIG *dsa_sig = NULL;
- BIGNUM *pr = NULL, *ps = NULL;
-
- ssh_string r;
- ssh_string s;
-
- size_t len;
-
- int raw_sig_len = 0;
- unsigned char *raw_sig_data = NULL;
- unsigned char *temp_raw_sig = NULL;
-
- int rc;
-
- len = ssh_string_len(sig_blob);
-
- /* 40 is the dual signature blob len. */
- if (len != 40) {
- SSH_LOG(SSH_LOG_WARN,
- "Signature has wrong size: %lu",
- (unsigned long)len);
- goto error;
- }
-
-#ifdef DEBUG_CRYPTO
- 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);
- if (r == NULL) {
- goto error;
- }
- rc = ssh_string_fill(r, ssh_string_data(sig_blob), 20);
- if (rc < 0) {
- SSH_STRING_FREE(r);
- goto error;
- }
-
- pr = ssh_make_string_bn(r);
- ssh_string_burn(r);
- SSH_STRING_FREE(r);
- if (pr == NULL) {
- goto error;
- }
-
- s = ssh_string_new(20);
- if (s == NULL) {
- goto error;
- }
- rc = ssh_string_fill(s, (char *)ssh_string_data(sig_blob) + 20, 20);
- if (rc < 0) {
- SSH_STRING_FREE(s);
- goto error;
- }
-
- ps = ssh_make_string_bn(s);
- ssh_string_burn(s);
- SSH_STRING_FREE(s);
- if (ps == NULL) {
- goto error;
- }
-
- dsa_sig = DSA_SIG_new();
- if (dsa_sig == NULL) {
- goto error;
- }
-
- /* Memory management of pr and ps is transferred to DSA signature
- * object */
- rc = DSA_SIG_set0(dsa_sig, pr, ps);
- if (rc == 0) {
- goto error;
- }
- ps = NULL;
- pr = NULL;
-
- /* Get the expected size of the buffer */
- rc = i2d_DSA_SIG(dsa_sig, NULL);
- if (rc <= 0) {
- goto error;
- }
- raw_sig_len = rc;
-
- raw_sig_data = (unsigned char *)calloc(1, raw_sig_len);
- if (raw_sig_data == NULL) {
- goto error;
- }
- temp_raw_sig = raw_sig_data;
-
- /* It is necessary to use a temporary pointer as i2d_* "advances" the
- * pointer */
- raw_sig_len = i2d_DSA_SIG(dsa_sig, &temp_raw_sig);
- if (raw_sig_len <= 0) {
- goto error;
- }
-
- sig->raw_sig = ssh_string_new(raw_sig_len);
- if (sig->raw_sig == NULL) {
- explicit_bzero(raw_sig_data, raw_sig_len);
- goto error;
- }
-
- rc = ssh_string_fill(sig->raw_sig, raw_sig_data, raw_sig_len);
- if (rc < 0) {
- explicit_bzero(raw_sig_data, raw_sig_len);
- goto error;
- }
-
- explicit_bzero(raw_sig_data, raw_sig_len);
- SAFE_FREE(raw_sig_data);
- DSA_SIG_free(dsa_sig);
-
- return SSH_OK;
-
-error:
- bignum_safe_free(ps);
- bignum_safe_free(pr);
- SAFE_FREE(raw_sig_data);
- DSA_SIG_free(dsa_sig);
- return SSH_ERROR;
-}
-
static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey),
const ssh_string sig_blob,
ssh_signature sig)
@@ -1852,7 +2106,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_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature has remaining bytes in inner "
"sigblob: %lu",
(unsigned long)rlen);
@@ -1941,7 +2195,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
int rc;
if (ssh_key_type_plain(pubkey->type) != type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Incompatible public key provided (%d) expecting (%d)",
type,
pubkey->type);
@@ -1958,12 +2212,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
sig->hash_type = hash_type;
switch(type) {
- case SSH_KEYTYPE_DSS:
- rc = pki_signature_from_dsa_blob(pubkey, sig_blob, sig);
- if (rc != SSH_OK) {
- goto error;
- }
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
rc = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
@@ -1995,7 +2243,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
#endif
default:
case SSH_KEYTYPE_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature type");
goto error;
}
@@ -2038,44 +2286,12 @@ static const EVP_MD *pki_digest_to_md(enum ssh_digest_e hash_type)
static EVP_PKEY *pki_key_to_pkey(ssh_key key)
{
EVP_PKEY *pkey = NULL;
+ int rc = 0;
-#ifdef WITH_PKCS11_URI
- if (key->flags & SSH_KEY_FLAG_PKCS11_URI) {
- pkey = key->key;
- return pkey;
- }
-#endif
-
- switch(key->type) {
- case SSH_KEYTYPE_DSS:
- case SSH_KEYTYPE_DSS_CERT01:
- if (key->dsa == NULL) {
- 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;
+ switch (key->type) {
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_RSA_CERT01:
- if (key->rsa == NULL) {
- 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:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
@@ -2084,25 +2300,21 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key)
case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
-# if defined(HAVE_OPENSSL_ECC)
- if (key->ecdsa == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "NULL key->ecdsa");
+ if (key->key == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "NULL key->key");
goto error;
}
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "Out of memory");
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to reference EVP_PKEY");
return NULL;
}
-
- EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
+ pkey = key->key;
break;
-# endif
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_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");
@@ -2130,7 +2342,6 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key)
return NULL;
}
break;
-#endif
case SSH_KEYTYPE_UNKNOWN:
default:
SSH_LOG(SSH_LOG_TRACE, "Unknown private key algorithm for type: %d",
@@ -2148,7 +2359,7 @@ error:
/**
* @internal
*
- * @brief Sign the given input data. The digest of to be signed is calculated
+ * @brief Sign the given input data. The digest to be signed is calculated
* internally as necessary.
*
* @param[in] privkey The private key to be used for signing.
@@ -2186,14 +2397,6 @@ 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) {
@@ -2217,7 +2420,7 @@ ssh_signature pki_sign_data(const ssh_key privkey,
}
/* Create the context */
- ctx = EVP_MD_CTX_create();
+ ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
SSH_LOG(SSH_LOG_TRACE, "Out of memory");
goto out;
@@ -2232,7 +2435,6 @@ ssh_signature pki_sign_data(const ssh_key privkey,
goto out;
}
-#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,
@@ -2240,23 +2442,6 @@ ssh_signature pki_sign_data(const ssh_key privkey,
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_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_log_hexdump("Generated signature", raw_sig_data, raw_sig_len);
@@ -2294,9 +2479,7 @@ out:
explicit_bzero(raw_sig_data, raw_sig_len);
}
SAFE_FREE(raw_sig_data);
- if (pkey != NULL) {
- EVP_PKEY_free(pkey);
- }
+ EVP_PKEY_free(pkey);
return sig;
}
@@ -2325,15 +2508,15 @@ int pki_verify_data_signature(ssh_signature signature,
unsigned char *raw_sig_data = NULL;
unsigned int raw_sig_len;
+ /* Function return code
+ * Do not change this variable throughout the function until the signature
+ * is successfully verified!
+ */
int rc = SSH_ERROR;
- int evp_rc;
+ int ok;
if (pubkey == NULL || ssh_key_is_private(pubkey) || input == NULL ||
- signature == NULL || (signature->raw_sig == NULL
-#ifndef HAVE_OPENSSL_ED25519
- && signature->ed25519_sig == NULL
-#endif
- ))
+ signature == NULL || signature->raw_sig == NULL)
{
SSH_LOG(SSH_LOG_TRACE, "Bad parameter provided to "
"pki_verify_data_signature()");
@@ -2341,21 +2524,11 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Check if public key and hash type are compatible */
- rc = pki_key_check_hash_compatible(pubkey, signature->hash_type);
- if (rc != SSH_OK) {
+ ok = pki_key_check_hash_compatible(pubkey, signature->hash_type);
+ if (ok != SSH_OK) {
return SSH_ERROR;
}
-#ifndef HAVE_OPENSSL_ED25519
- if (pubkey->type == SSH_KEYTYPE_ED25519 ||
- pubkey->type == SSH_KEYTYPE_ED25519_CERT01 ||
- pubkey->type == SSH_KEYTYPE_SK_ED25519 ||
- pubkey->type == SSH_KEYTYPE_SK_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);
@@ -2378,7 +2551,7 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Create the context */
- ctx = EVP_MD_CTX_create();
+ ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create EVP_MD_CTX: %s",
@@ -2387,48 +2560,69 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Verify the signature */
- evp_rc = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey);
- if (evp_rc != 1){
+ ok = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey);
+ if (ok != 1){
SSH_LOG(SSH_LOG_TRACE,
"EVP_DigestVerifyInit() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
-#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) {
+ ok = EVP_DigestVerify(ctx, raw_sig_data, raw_sig_len, input, input_len);
+ if (ok != 1) {
SSH_LOG(SSH_LOG_TRACE,
- "EVP_DigestVerifyUpdate() failed: %s",
+ "Signature invalid: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
- 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;
- }
+ SSH_LOG(SSH_LOG_TRACE, "Signature valid");
+ rc = SSH_OK;
out:
- if (ctx != NULL) {
- EVP_MD_CTX_free(ctx);
- }
- if (pkey != NULL) {
+ EVP_MD_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ return rc;
+}
+
+int ssh_key_size(ssh_key key)
+{
+ int bits = 0;
+ EVP_PKEY *pkey = NULL;
+
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ pkey = pki_key_to_pkey(key);
+ if (pkey == NULL) {
+ return SSH_ERROR;
+ }
+ bits = EVP_PKEY_bits(pkey);
EVP_PKEY_free(pkey);
+ return bits;
+ case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys have fixed size */
+ return 255;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_UNKNOWN:
+ default:
+ return SSH_ERROR;
}
- return rc;
}
-#ifdef HAVE_OPENSSL_ED25519
int pki_key_generate_ed25519(ssh_key key)
{
int evp_rc;
@@ -2513,50 +2707,21 @@ error:
return SSH_ERROR;
}
-#else
-ssh_signature pki_do_sign_hash(const ssh_key privkey,
- const unsigned char *hash,
- size_t hlen,
- enum ssh_digest_e hash_type)
-{
- ssh_signature sig = NULL;
- int rc;
-
- sig = ssh_signature_new();
- if (sig == NULL) {
- return NULL;
- }
-
- sig->type = privkey->type;
- sig->type_c = ssh_key_signature_to_char(privkey->type, hash_type);
- sig->hash_type = hash_type;
- switch(privkey->type) {
- case SSH_KEYTYPE_ED25519:
- rc = pki_ed25519_sign(privkey, sig, hash, hlen);
- if (rc != SSH_OK) {
- ssh_signature_free(sig);
- return NULL;
- }
- break;
- default:
- ssh_signature_free(sig);
- return NULL;
- }
-
- return sig;
-}
-#endif /* HAVE_OPENSSL_ED25519 */
+#ifdef WITH_PKCS11_URI
+#ifdef WITH_PKCS11_PROVIDER
+static bool pkcs11_provider_failed = false;
+#endif
/**
* @internal
*
- * @brief Populate the public/private ssh_key from the engine with
+ * @brief Populate the public/private ssh_key from the engine/provider with
* PKCS#11 URIs as the look up.
*
* @param[in] uri_name The PKCS#11 URI
* @param[in] nkey The ssh-key context for
- * the key loaded from the engine.
+ * the key loaded from the engine/provider.
* @param[in] key_type The type of the key used. Public/Private.
*
* @return SSH_OK if ssh-key is valid; SSH_ERROR otherwise.
@@ -2565,64 +2730,112 @@ int pki_uri_import(const char *uri_name,
ssh_key *nkey,
enum ssh_key_e key_type)
{
- ENGINE *engine = NULL;
EVP_PKEY *pkey = NULL;
- RSA *rsa = NULL;
ssh_key key = NULL;
enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN;
-#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L && HAVE_OPENSSL_ECC
EC_KEY *ecdsa = NULL;
-#else
- void *ecdsa = NULL;
#endif
- int ok;
-
- ENGINE_load_builtin_engines();
+#ifndef WITH_PKCS11_PROVIDER
+ ENGINE *engine = NULL;
- engine = ENGINE_by_id("pkcs11");
+ /* Do the init only once */
+ engine = pki_get_engine();
if (engine == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Could not load the engine: %s",
- ERR_error_string(ERR_get_error(),NULL));
- return SSH_ERROR;
- }
- SSH_LOG(SSH_LOG_INFO, "Engine loaded successfully");
-
- ok = ENGINE_init(engine);
- if (!ok) {
- SSH_LOG(SSH_LOG_WARN,
- "Could not initialize the engine: %s",
- ERR_error_string(ERR_get_error(),NULL));
- ENGINE_free(engine);
- return SSH_ERROR;
+ SSH_LOG(SSH_LOG_TRACE, "Failed to initialize engine");
+ goto fail;
}
- SSH_LOG(SSH_LOG_INFO, "Engine init success");
-
switch (key_type) {
case SSH_KEY_PRIVATE:
pkey = ENGINE_load_private_key(engine, uri_name, NULL, NULL);
if (pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not load key: %s",
- ERR_error_string(ERR_get_error(),NULL));
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
break;
case SSH_KEY_PUBLIC:
pkey = ENGINE_load_public_key(engine, uri_name, NULL, NULL);
if (pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not load key: %s",
- ERR_error_string(ERR_get_error(),NULL));
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
break;
default:
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid key type: %d", key_type);
goto fail;
}
+#else /* WITH_PKCS11_PROVIDER */
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info = NULL;
+ int rv, expect_type = OSSL_STORE_INFO_PKEY;
+
+ /* The provider can be either configured in openssl.cnf or dynamically
+ * loaded, assuming it does not need any special configuration */
+ if (OSSL_PROVIDER_available(NULL, "pkcs11") == 0 &&
+ !pkcs11_provider_failed) {
+ OSSL_PROVIDER *pkcs11_provider = NULL;
+
+ pkcs11_provider = OSSL_PROVIDER_try_load(NULL, "pkcs11", 1);
+ if (pkcs11_provider == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to initialize provider: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ /* Do not attempt to load it again */
+ pkcs11_provider_failed = true;
+ goto fail;
+ }
+ }
+
+ store = OSSL_STORE_open(uri_name, NULL, NULL, NULL, NULL);
+ if (store == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to open OpenSSL store: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (key_type == SSH_KEY_PUBLIC) {
+ expect_type = OSSL_STORE_INFO_PUBKEY;
+ }
+ rv = OSSL_STORE_expect(store, expect_type);
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to set the store preference. Ignoring the error: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ for (info = OSSL_STORE_load(store);
+ info != NULL;
+ info = OSSL_STORE_load(store)) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if (ossl_type == OSSL_STORE_INFO_PUBKEY && key_type == SSH_KEY_PUBLIC) {
+ pkey = OSSL_STORE_INFO_get1_PUBKEY(info);
+ break;
+ } else if (ossl_type == OSSL_STORE_INFO_PKEY &&
+ key_type == SSH_KEY_PRIVATE) {
+ pkey = OSSL_STORE_INFO_get1_PKEY(info);
+ break;
+ } else {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Ignoring object not matching our type: %d",
+ ossl_type);
+ }
+ }
+ OSSL_STORE_close(store);
+ if (pkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "No key found in the pkcs11 store: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+#endif /* WITH_PKCS11_PROVIDER */
key = ssh_key_new();
if (key == NULL) {
@@ -2631,20 +2844,14 @@ int pki_uri_import(const char *uri_name,
switch (EVP_PKEY_base_id(pkey)) {
case EVP_PKEY_RSA:
- rsa = EVP_PKEY_get1_RSA(pkey);
- if (rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing pub key: %s",
- ERR_error_string(ERR_get_error(),NULL));
- goto fail;
- }
type = SSH_KEYTYPE_RSA;
break;
case EVP_PKEY_EC:
#ifdef HAVE_OPENSSL_ECC
- ecdsa = EVP_PKEY_get1_EC_KEY(pkey);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EVP_PKEY_get0_EC_KEY(pkey);
if (ecdsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Parsing pub key: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
@@ -2653,15 +2860,18 @@ int pki_uri_import(const char *uri_name,
/* pki_privatekey_type_from_string always returns P256 for ECDSA
* keys, so we need to figure out the correct type here */
type = pki_key_ecdsa_to_key_type(ecdsa);
+#else
+ type = pki_key_ecdsa_to_key_type(pkey);
+#endif /* OPENSSL_VERSION_NUMBER */
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid pub key.");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid pub key.");
goto fail;
}
break;
#endif
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid public key type %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid public key type %d",
EVP_PKEY_base_id(pkey));
goto fail;
}
@@ -2669,36 +2879,30 @@ int pki_uri_import(const char *uri_name,
key->key = pkey;
key->type = type;
key->type_c = ssh_key_type_to_char(type);
+ key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PKCS11_URI;
if (key_type == SSH_KEY_PRIVATE) {
- key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PKCS11_URI;
- } else {
- key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PKCS11_URI;
+ key->flags |= SSH_KEY_FLAG_PRIVATE;
}
- key->rsa = rsa;
- key->ecdsa = ecdsa;
#ifdef HAVE_OPENSSL_ECC
if (is_ecdsa_key_type(key->type)) {
- key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(ecdsa);
+#else
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(key->key);
+#endif /* OPENSSL_VERSION_NUMBER */
}
#endif
*nkey = key;
- ENGINE_finish(engine);
- ENGINE_free(engine);
return SSH_OK;
fail:
- ENGINE_finish(engine);
- ENGINE_free(engine);
EVP_PKEY_free(pkey);
ssh_key_free(key);
- RSA_free(rsa);
-#ifdef HAVE_OPENSSL_ECC
- EC_KEY_free(ecdsa);
-#endif
return SSH_ERROR;
}
+#endif /* WITH_PKCS11_URI */
#endif /* _PKI_CRYPTO_H */
diff --git a/src/pki_ed25519.c b/src/pki_ed25519.c
index fdf94b4e..6a5a4a8a 100644
--- a/src/pki_ed25519.c
+++ b/src/pki_ed25519.c
@@ -1,5 +1,5 @@
/*
- * pki_ed25519 .c - PKI infrastructure using ed25519
+ * pki_ed25519.c - PKI infrastructure using ed25519
*
* This file is part of the SSH Library
*
diff --git a/src/pki_ed25519_common.c b/src/pki_ed25519_common.c
index 7aa05269..03859f7c 100644
--- a/src/pki_ed25519_common.c
+++ b/src/pki_ed25519_common.c
@@ -34,11 +34,11 @@ int pki_privkey_build_ed25519(ssh_key key,
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");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* In OpenSSL implementation, the private key is the original private seed,
* without the public key. */
key->ed25519_privkey = malloc(ED25519_KEY_LEN);
@@ -56,7 +56,7 @@ int pki_privkey_build_ed25519(ssh_key key,
goto error;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
memcpy(key->ed25519_privkey, ssh_string_data(privkey),
ED25519_KEY_LEN);
#else
@@ -99,7 +99,7 @@ int pki_ed25519_key_cmp(const ssh_key k1,
if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) {
return 1;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* 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);
@@ -121,6 +121,10 @@ int pki_ed25519_key_cmp(const ssh_key k1,
if (cmp != 0) {
return 1;
}
+ break;
+ case SSH_KEY_CMP_CERTIFICATE:
+ /* handled globally */
+ return 1;
}
return 0;
@@ -137,39 +141,39 @@ int pki_ed25519_key_cmp(const ssh_key k1,
*
* @return SSH_ERROR on error, SSH_OK on success
*/
-int pki_ed25519_key_dup(ssh_key new, const ssh_key key)
+int pki_ed25519_key_dup(ssh_key new_key, 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
+#ifdef HAVE_LIBCRYPTO
/* In OpenSSL implementation, the private key is the original private
* seed, without the public key. */
- new->ed25519_privkey = malloc(ED25519_KEY_LEN);
+ new_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. */
- new->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
+ new_key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
#endif
- if (new->ed25519_privkey == NULL) {
+ if (new_key->ed25519_privkey == NULL) {
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_ED25519
- memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN);
+#ifdef HAVE_LIBCRYPTO
+ memcpy(new_key->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN);
#else
- memcpy(new->ed25519_privkey, key->ed25519_privkey, 2 * ED25519_KEY_LEN);
+ memcpy(new_key->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);
+ new_key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
+ if (new_key->ed25519_pubkey == NULL) {
+ SAFE_FREE(new_key->ed25519_privkey);
return SSH_ERROR;
}
- memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_LEN);
+ memcpy(new_key->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_LEN);
}
return SSH_OK;
@@ -202,6 +206,34 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key)
return rc;
}
+/** @internal
+ * @brief exports a ed25519 private key to a string blob.
+ * @param[in] privkey private key to convert
+ * @param[out] buffer buffer to write the blob in.
+ * @returns SSH_OK on success
+ */
+int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey)
+{
+ int rc;
+
+ if (privkey->type != SSH_KEYTYPE_ED25519) {
+ SSH_LOG(SSH_LOG_TRACE, "Type %s not supported", privkey->type_c);
+ return SSH_ERROR;
+ }
+ if (privkey->ed25519_privkey == NULL ||
+ privkey->ed25519_pubkey == NULL) {
+ return SSH_ERROR;
+ }
+ rc = ssh_buffer_pack(buffer,
+ "dPdPP",
+ (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;
+}
+
/**
* @internal
*
@@ -216,7 +248,7 @@ ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
ssh_string sig_blob;
int rc;
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* When using the OpenSSL implementation, the signature is stored in raw_sig
* which is shared by all algorithms.*/
if (sig->raw_sig == NULL) {
@@ -235,7 +267,7 @@ ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
return NULL;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
rc = ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig),
ssh_string_len(sig->raw_sig));
#else
@@ -266,11 +298,11 @@ int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob)
len = ssh_string_len(sig_blob);
if (len != ED25519_SIG_LEN){
- SSH_LOG(SSH_LOG_WARN, "Invalid ssh-ed25519 signature len: %zu", len);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid ssh-ed25519 signature len: %zu", len);
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
sig->raw_sig = ssh_string_copy(sig_blob);
#else
sig->ed25519_sig = malloc(ED25519_SIG_LEN);
@@ -282,4 +314,3 @@ int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob)
return SSH_OK;
}
-
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index 7f8b140e..65bb77e6 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -45,8 +45,6 @@
#define MAXLINESIZE 80
#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----"
-#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
-#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----"
#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----"
#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----"
@@ -283,6 +281,20 @@ static int passphrase_to_key(char *data, unsigned int datalen,
return 0;
}
+void pki_key_clean(ssh_key key)
+{
+ if (key == NULL)
+ return;
+
+ if (key->rsa)
+ gcry_sexp_release(key->rsa);
+ if (key->ecdsa)
+ gcry_sexp_release(key->ecdsa);
+
+ key->rsa = NULL;
+ key->ecdsa = NULL;
+}
+
static int privatekey_decrypt(int algo, int mode, unsigned int key_len,
unsigned char *iv, unsigned int iv_len,
ssh_buffer data, ssh_auth_callback cb,
@@ -419,10 +431,6 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type,
}
switch(type) {
- case SSH_KEYTYPE_DSS:
- header_begin = DSA_HEADER_BEGIN;
- header_end = DSA_HEADER_END;
- break;
case SSH_KEYTYPE_RSA:
header_begin = RSA_HEADER_BEGIN;
header_end = RSA_HEADER_END;
@@ -626,79 +634,6 @@ error:
return rc;
}
-static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_callback cb,
- void *userdata, const char *desc) {
- const unsigned char *data;
- ssh_buffer buffer = NULL;
- ssh_string p = NULL;
- ssh_string q = NULL;
- ssh_string g = NULL;
- ssh_string y = NULL;
- ssh_string x = NULL;
- ssh_string v = NULL;
- int rc = 1;
-
- buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_DSS, cb, userdata, desc);
- if (buffer == NULL) {
- return 0;
- }
-
- if (!asn1_check_sequence(buffer)) {
- SSH_BUFFER_FREE(buffer);
- return 0;
- }
-
- v = asn1_get_int(buffer);
- if (v == NULL) {
- 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);
- return 0;
- }
-
- p = asn1_get_int(buffer);
- q = asn1_get_int(buffer);
- g = asn1_get_int(buffer);
- y = asn1_get_int(buffer);
- x = asn1_get_int(buffer);
- SSH_BUFFER_FREE(buffer);
-
- if (p == NULL || q == NULL || g == NULL || y == NULL || x == NULL) {
- rc = 0;
- goto error;
- }
-
- if (gcry_sexp_build(r, NULL,
- "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))",
- ssh_string_len(p), ssh_string_data(p),
- ssh_string_len(q), ssh_string_data(q),
- ssh_string_len(g), ssh_string_data(g),
- ssh_string_len(y), ssh_string_data(y),
- ssh_string_len(x), ssh_string_data(x))) {
- rc = 0;
- }
-
-error:
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(y);
- SSH_STRING_FREE(y);
- ssh_string_burn(x);
- SSH_STRING_FREE(x);
- SSH_STRING_FREE(v);
-
- return rc;
-}
-
#ifdef HAVE_GCRYPT_ECC
static int pki_key_ecdsa_to_nid(gcry_sexp_t k)
{
@@ -938,7 +873,7 @@ 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!");
+ SSH_LOG(SSH_LOG_TRACE, "PEM export not supported by gcrypt backend!");
return NULL;
}
@@ -948,7 +883,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
ssh_auth_callback auth_fn,
void *auth_data)
{
- gcry_sexp_t dsa = NULL;
gcry_sexp_t rsa = NULL;
gcry_sexp_t ecdsa = NULL;
ssh_key key = NULL;
@@ -957,30 +891,11 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
type = pki_privatekey_type_from_string(b64_key);
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key.");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key.");
return NULL;
}
switch (type) {
- case SSH_KEYTYPE_DSS:
- if (passphrase == NULL) {
- if (auth_fn) {
- valid = b64decode_dsa_privatekey(b64_key, &dsa, auth_fn,
- auth_data, "Passphrase for private key:");
- } else {
- valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, NULL,
- NULL);
- }
- } else {
- valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, (void *)
- passphrase, NULL);
- }
-
- if (!valid) {
- SSH_LOG(SSH_LOG_WARN, "Parsing private key");
- goto fail;
- }
- break;
case SSH_KEYTYPE_RSA:
if (passphrase == NULL) {
if (auth_fn) {
@@ -996,7 +911,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
}
if (!valid) {
- SSH_LOG(SSH_LOG_WARN, "Parsing private key");
+ SSH_LOG(SSH_LOG_TRACE, "Error parsing private key");
goto fail;
}
break;
@@ -1027,7 +942,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
}
if (!valid) {
- SSH_LOG(SSH_LOG_WARN, "Parsing private key");
+ SSH_LOG(SSH_LOG_TRACE, "Error parsing private key");
goto fail;
}
@@ -1035,7 +950,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
* keys, so we need to figure out the correct type here */
type = pki_key_ecdsa_to_key_type(ecdsa);
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid private key.");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid private key.");
goto fail;
}
break;
@@ -1045,7 +960,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d", type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d", type);
return NULL;
}
@@ -1057,7 +972,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
key->type = type;
key->type_c = ssh_key_type_to_char(type);
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- key->dsa = dsa;
key->rsa = rsa;
key->ecdsa = ecdsa;
#ifdef HAVE_GCRYPT_ECC
@@ -1069,52 +983,12 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
return key;
fail:
ssh_key_free(key);
- gcry_sexp_release(dsa);
gcry_sexp_release(rsa);
gcry_sexp_release(ecdsa);
return NULL;
}
-int pki_privkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey,
- ssh_string privkey)
-{
- gcry_sexp_build(&key->dsa, NULL,
- "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))",
- ssh_string_len(p), ssh_string_data(p),
- ssh_string_len(q), ssh_string_data(q),
- ssh_string_len(g), ssh_string_data(g),
- ssh_string_len(pubkey), ssh_string_data(pubkey),
- ssh_string_len(privkey), ssh_string_data(privkey));
- if (key->dsa == NULL) {
- return SSH_ERROR;
- }
-
- return SSH_OK;
-}
-
-int pki_pubkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey) {
- gcry_sexp_build(&key->dsa, NULL,
- "(public-key(dsa(p %b)(q %b)(g %b)(y %b)))",
- ssh_string_len(p), ssh_string_data(p),
- ssh_string_len(q), ssh_string_data(q),
- ssh_string_len(g), ssh_string_data(g),
- ssh_string_len(pubkey), ssh_string_data(pubkey));
- if (key->dsa == NULL) {
- return SSH_ERROR;
- }
-
- return SSH_OK;
-}
-
int pki_privkey_build_rsa(ssh_key key,
ssh_string n,
ssh_string e,
@@ -1226,32 +1100,6 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
switch(key->type) {
- case SSH_KEYTYPE_DSS:
- err = gcry_sexp_extract_param(key->dsa,
- NULL,
- "pqgyx?",
- &p,
- &q,
- &g,
- &y,
- &x,
- NULL);
- if (err != 0) {
- break;
- }
-
- if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
- err = gcry_sexp_build(&new->dsa,
- NULL,
- "(private-key(dsa(p %m)(q %m)(g %m)(y %m)(x %m)))",
- p, q, g, y, x);
- } else {
- err = gcry_sexp_build(&new->dsa,
- NULL,
- "(public-key(dsa(p %m)(q %m)(g %m)(y %m)))",
- p, q, g, y);
- }
- break;
case SSH_KEYTYPE_RSA:
err = gcry_sexp_extract_param(key->rsa,
NULL,
@@ -1353,9 +1201,9 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int type){
- gcry_sexp_t parms;
+ gcry_sexp_t params;
int rc;
- rc = gcry_sexp_build(&parms,
+ rc = gcry_sexp_build(&params,
NULL,
"(genkey(%s(nbits %d)(transient-key)))",
type_s,
@@ -1364,20 +1212,17 @@ static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int
return SSH_ERROR;
switch (type) {
case SSH_KEYTYPE_RSA:
- rc = gcry_pk_genkey(&key->rsa, parms);
- break;
- case SSH_KEYTYPE_DSS:
- rc = gcry_pk_genkey(&key->dsa, parms);
+ rc = gcry_pk_genkey(&key->rsa, params);
break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
- rc = gcry_pk_genkey(&key->ecdsa, parms);
+ rc = gcry_pk_genkey(&key->ecdsa, params);
break;
default:
assert (! "reached");
}
- gcry_sexp_release(parms);
+ gcry_sexp_release(params);
if (rc != 0)
return SSH_ERROR;
return SSH_OK;
@@ -1386,9 +1231,6 @@ static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int
int pki_key_generate_rsa(ssh_key key, int parameter){
return pki_key_generate(key, parameter, "rsa", SSH_KEYTYPE_RSA);
}
-int pki_key_generate_dss(ssh_key key, int parameter){
- return pki_key_generate(key, parameter, "dsa", SSH_KEYTYPE_DSS);
-}
#ifdef HAVE_GCRYPT_ECC
int pki_key_generate_ecdsa(ssh_key key, int parameter) {
@@ -1455,30 +1297,8 @@ int pki_key_compare(const ssh_key k1,
enum ssh_keycmp_e what)
{
switch (k1->type) {
- case SSH_KEYTYPE_DSS:
- if (_bignum_cmp(k1->dsa, k2->dsa, "p") != 0) {
- return 1;
- }
-
- if (_bignum_cmp(k1->dsa, k2->dsa, "q") != 0) {
- return 1;
- }
-
- if (_bignum_cmp(k1->dsa, k2->dsa, "g") != 0) {
- return 1;
- }
-
- if (_bignum_cmp(k1->dsa, k2->dsa, "y") != 0) {
- return 1;
- }
-
- if (what == SSH_KEY_CMP_PRIVATE) {
- if (_bignum_cmp(k1->dsa, k2->dsa, "x") != 0) {
- return 1;
- }
- }
- break;
case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) {
return 1;
}
@@ -1506,13 +1326,19 @@ int pki_key_compare(const ssh_key k1,
}
break;
case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_SK_ED25519:
- /* ed25519 keys handled globaly */
- return 0;
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys handled globally */
+ return 0;
case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
#ifdef HAVE_GCRYPT_ECC
if (k1->ecdsa_nid != k2->ecdsa_nid) {
return 1;
@@ -1529,15 +1355,9 @@ int pki_key_compare(const ssh_key k1,
}
break;
#endif
- case SSH_KEYTYPE_DSS_CERT01:
- case SSH_KEYTYPE_RSA_CERT01:
- case SSH_KEYTYPE_ECDSA:
- case SSH_KEYTYPE_ECDSA_P256_CERT01:
- case SSH_KEYTYPE_ECDSA_P384_CERT01:
- case SSH_KEYTYPE_ECDSA_P521_CERT01:
- case SSH_KEYTYPE_SK_ECDSA_CERT01:
- case SSH_KEYTYPE_ED25519_CERT01:
- case SSH_KEYTYPE_SK_ED25519_CERT01:
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_ECDSA: /* deprecated */
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
return 1;
@@ -1546,16 +1366,18 @@ int pki_key_compare(const ssh_key k1,
return 0;
}
-ssh_string pki_publickey_to_blob(const ssh_key key)
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
{
ssh_buffer buffer;
ssh_string type_s;
ssh_string str = NULL;
ssh_string e = NULL;
ssh_string n = NULL;
+ ssh_string d = NULL;
ssh_string p = NULL;
ssh_string g = NULL;
ssh_string q = NULL;
+ ssh_string u = NULL;
int rc;
buffer = ssh_buffer_new();
@@ -1586,66 +1408,6 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
}
switch (key->type) {
- case SSH_KEYTYPE_DSS:
- p = ssh_sexp_extract_mpi(key->dsa,
- "p",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (p == NULL) {
- goto fail;
- }
-
- q = ssh_sexp_extract_mpi(key->dsa,
- "q",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (q == NULL) {
- goto fail;
- }
-
- g = ssh_sexp_extract_mpi(key->dsa,
- "g",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (g == NULL) {
- goto fail;
- }
-
- n = ssh_sexp_extract_mpi(key->dsa,
- "y",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (n == NULL) {
- goto fail;
- }
-
- rc = ssh_buffer_add_ssh_string(buffer, p);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, q);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, g);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, n);
- if (rc < 0) {
- goto fail;
- }
-
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(n);
- SSH_STRING_FREE(n);
-
- break;
case SSH_KEYTYPE_RSA:
e = ssh_sexp_extract_mpi(key->rsa,
"e",
@@ -1663,30 +1425,108 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
goto fail;
}
- rc = ssh_buffer_add_ssh_string(buffer, e);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, n);
- if (rc < 0) {
- goto fail;
- }
+ if (type == SSH_KEY_PUBLIC) {
+ /* The N and E parts are swapped in the public key export ! */
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ } else if (type == SSH_KEY_PRIVATE) {
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ d = ssh_sexp_extract_mpi(key->rsa,
+ "d",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ p = ssh_sexp_extract_mpi(key->rsa,
+ "p",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (p == NULL) {
+ goto fail;
+ }
+ q = ssh_sexp_extract_mpi(key->rsa,
+ "q",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ u = ssh_sexp_extract_mpi(key->rsa,
+ "u",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (u == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, u);
+ if (rc < 0) {
+ goto fail;
+ }
+ /* Swap the P and Q as the iqmp in gcrypt is ipmq ... */
+ rc = ssh_buffer_add_ssh_string(buffer, q);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, p);
+ if (rc < 0) {
+ goto fail;
+ }
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ ssh_string_burn(p);
+ SSH_STRING_FREE(p);
+ ssh_string_burn(q);
+ SSH_STRING_FREE(q);
+ ssh_string_burn(u);
+ SSH_STRING_FREE(u);
+ }
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(n);
SSH_STRING_FREE(n);
-
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- rc = pki_ed25519_public_key_to_blob(buffer, key);
- if (rc != SSH_OK){
- goto fail;
- }
- if (key->type == SSH_KEYTYPE_SK_ED25519 &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PUBLIC) {
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
+ /* public key can contain certificate sk information */
+ if (key->type == SSH_KEYTYPE_SK_ED25519) {
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+ } else {
+ rc = pki_ed25519_private_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
}
break;
case SSH_KEYTYPE_ECDSA_P256:
@@ -1697,22 +1537,19 @@ 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);
- return NULL;
+ goto fail;
}
rc = ssh_buffer_add_ssh_string(buffer, type_s);
SSH_STRING_FREE(type_s);
if (rc < 0) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
+ goto fail;
}
e = ssh_sexp_extract_mpi(key->ecdsa, "q", GCRYMPI_FMT_STD,
GCRYMPI_FMT_STD);
if (e == NULL) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
+ goto fail;
}
rc = ssh_buffer_add_ssh_string(buffer, e);
@@ -1724,9 +1561,27 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
SSH_STRING_FREE(e);
e = NULL;
- if (key->type == SSH_KEYTYPE_SK_ECDSA &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PRIVATE) {
+ d = ssh_sexp_extract_mpi(key->ecdsa, "d", GCRYMPI_FMT_STD,
+ GCRYMPI_FMT_STD);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ } else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
+ /* public key can contain certificate sk information */
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
}
break;
@@ -1770,13 +1625,7 @@ fail:
ssh_string pki_signature_to_blob(const ssh_signature sig)
{
- char buffer[40] = { 0 };
-
- const char *r = NULL;
- size_t r_len, r_offset_in, r_offset_out;
-
- const char *s = NULL;
- size_t s_len, s_offset_in, s_offset_out;
+ const char *s = NULL; /* used in RSA */
gcry_sexp_t sexp;
size_t size = 0;
@@ -1784,57 +1633,6 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
int rc;
switch(sig->type) {
- case SSH_KEYTYPE_DSS:
- sexp = gcry_sexp_find_token(sig->dsa_sig, "r", 0);
- if (sexp == NULL) {
- return NULL;
- }
- r = gcry_sexp_nth_data(sexp, 1, &size);
- /* libgcrypt put 0 when first bit is set */
- if (*r == 0) {
- size--;
- r++;
- }
-
- r_len = size;
- r_offset_in = (r_len > 20) ? (r_len - 20) : 0;
- r_offset_out = (r_len < 20) ? (20 - r_len) : 0;
- memcpy(buffer + r_offset_out,
- r + r_offset_in,
- r_len - r_offset_in);
-
- gcry_sexp_release(sexp);
-
- sexp = gcry_sexp_find_token(sig->dsa_sig, "s", 0);
- if (sexp == NULL) {
- return NULL;
- }
- s = gcry_sexp_nth_data(sexp,1,&size);
- if (*s == 0) {
- size--;
- s++;
- }
-
- s_len = size;
- s_offset_in = (s_len > 20) ? (s_len - 20) : 0;
- s_offset_out = (s_len < 20) ? (20 - s_len) : 0;
- memcpy(buffer + 20 + s_offset_out,
- s + s_offset_in,
- s_len - s_offset_in);
-
- gcry_sexp_release(sexp);
-
- sig_blob = ssh_string_new(40);
- if (sig_blob == NULL) {
- return NULL;
- }
-
- rc = ssh_string_fill(sig_blob, buffer, 40);
- if (rc < 0) {
- SSH_STRING_FREE(sig_blob);
- return NULL;
- }
- break;
case SSH_KEYTYPE_RSA:
sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0);
if (sexp == NULL) {
@@ -1921,7 +1719,7 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %d", sig->type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %d", sig->type);
return NULL;
break;
}
@@ -1941,7 +1739,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
int rc;
if (ssh_key_type_plain(pubkey->type) != type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Incompatible public key provided (%d) expecting (%d)",
type,
pubkey->type);
@@ -1960,40 +1758,11 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
len = ssh_string_len(sig_blob);
switch(type) {
- case SSH_KEYTYPE_DSS:
- /* 40 is the dual signature blob len. */
- if (len != 40) {
- SSH_LOG(SSH_LOG_WARN,
- "Signature has wrong size: %lu",
- (unsigned long)len);
- ssh_signature_free(sig);
- return NULL;
- }
-
-#ifdef DEBUG_CRYPTO
- SSH_LOG(SSH_LOG_DEBUG,
- "DSA signature len: %lu",
- (unsigned long)len);
- ssh_log_hexdump("DSA signature", ssh_string_data(sig_blob), len);
-#endif
-
- err = gcry_sexp_build(&sig->dsa_sig,
- NULL,
- "(sig-val(dsa(r %b)(s %b)))",
- 20,
- ssh_string_data(sig_blob),
- 20,
- (unsigned char *)ssh_string_data(sig_blob) + 20);
- if (err) {
- ssh_signature_free(sig);
- return NULL;
- }
- break;
case SSH_KEYTYPE_RSA:
rsalen = (gcry_pk_get_nbits(pubkey->rsa) + 7) / 8;
if (len > rsalen) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature is too big: %lu > %lu",
(unsigned long)len,
(unsigned long)rsalen);
@@ -2074,7 +1843,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
}
if (rlen != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature has remaining bytes in inner "
"sigblob: %lu",
(unsigned long)rlen);
@@ -2112,7 +1881,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature type");
return NULL;
}
@@ -2124,7 +1893,6 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
size_t hlen,
enum ssh_digest_e hash_type)
{
- unsigned char ghash[hlen + 1];
const char *hash_c = NULL;
ssh_signature sig;
gcry_sexp_t sexp;
@@ -2138,28 +1906,6 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
sig->type_c = ssh_key_signature_to_char(privkey->type, hash_type);
sig->hash_type = hash_type;
switch (privkey->type) {
- case SSH_KEYTYPE_DSS:
- /* That is to mark the number as positive */
- if(hash[0] >= 0x80) {
- memcpy(ghash + 1, hash, hlen);
- ghash[0] = 0;
- hash = ghash;
- hlen += 1;
- }
-
- err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash);
- if (err) {
- ssh_signature_free(sig);
- return NULL;
- }
-
- err = gcry_pk_sign(&sig->dsa_sig, sexp, privkey->dsa);
- gcry_sexp_release(sexp);
- if (err) {
- ssh_signature_free(sig);
- return NULL;
- }
- break;
case SSH_KEYTYPE_RSA:
switch (hash_type) {
case SSH_DIGEST_SHA1:
@@ -2173,7 +1919,7 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
break;
case SSH_DIGEST_AUTO:
default:
- SSH_LOG(SSH_LOG_WARN, "Incompatible key algorithm");
+ SSH_LOG(SSH_LOG_TRACE, "Incompatible key algorithm");
return NULL;
}
err = gcry_sexp_build(&sexp,
@@ -2392,32 +2138,6 @@ int pki_verify_data_signature(ssh_signature signature,
}
switch(pubkey->type) {
- case SSH_KEYTYPE_DSS:
- case SSH_KEYTYPE_DSS_CERT01:
- /* That is to mark the number as positive */
- if(hash[0] >= 0x80) {
- hash = ghash;
- hlen += 1;
- }
-
- err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash);
- if (err) {
- SSH_LOG(SSH_LOG_TRACE,
- "DSA hash error: %s", gcry_strerror(err));
- return SSH_ERROR;
- }
- err = gcry_pk_verify(signature->dsa_sig, sexp, pubkey->dsa);
- gcry_sexp_release(sexp);
- if (err) {
- SSH_LOG(SSH_LOG_TRACE, "Invalid DSA signature");
- if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) {
- SSH_LOG(SSH_LOG_TRACE,
- "DSA verify error: %s",
- gcry_strerror(err));
- }
- return SSH_ERROR;
- }
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA_CERT01:
err = gcry_sexp_build(&sexp,
@@ -2495,13 +2215,45 @@ int pki_verify_data_signature(ssh_signature signature,
return SSH_OK;
}
+int ssh_key_size(ssh_key key)
+{
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ return gcry_pk_get_nbits(key->rsa);
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ return gcry_pk_get_nbits(key->ecdsa);
+ case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys have fixed size */
+ return 255;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_UNKNOWN:
+ default:
+ return SSH_ERROR;
+ }
+}
+
+#ifdef WITH_PKCS11_URI
int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type)
{
(void) uri_name;
(void) key;
(void) key_type;
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"gcrypt does not support PKCS #11");
return SSH_ERROR;
}
+#endif /* WITH_PKCS11_URI */
#endif /* HAVE_LIBGCRYPT */
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index 720fe1de..d3fda0ae 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -26,6 +26,7 @@
#ifdef HAVE_LIBMBEDCRYPTO
#include <mbedtls/pk.h>
#include <mbedtls/error.h>
+#include "mbedcrypto-compat.h"
#include "libssh/priv.h"
#include "libssh/pki.h"
@@ -37,6 +38,22 @@
#define MAX_PASSPHRASE_SIZE 1024
#define MAX_KEY_SIZE 32
+void pki_key_clean(ssh_key key)
+{
+ if (key == NULL)
+ return;
+
+ if (key->rsa != NULL) {
+ mbedtls_pk_free(key->rsa);
+ SAFE_FREE(key->rsa);
+ }
+
+ if (key->ecdsa != NULL) {
+ mbedtls_ecdsa_free(key->ecdsa);
+ SAFE_FREE(key->ecdsa);
+ }
+}
+
ssh_string pki_private_key_to_pem(const ssh_key key, const char *passphrase,
ssh_auth_callback auth_fn, void *auth_data)
{
@@ -50,7 +67,7 @@ static int pki_key_ecdsa_to_nid(mbedtls_ecdsa_context *ecdsa)
{
mbedtls_ecp_group_id id;
- id = ecdsa->grp.id;
+ id = ecdsa->MBEDTLS_PRIVATE(grp.id);
if (id == MBEDTLS_ECP_DP_SECP256R1) {
return NID_mbedtls_nistp256;
} else if (id == MBEDTLS_ECP_DP_SECP384R1) {
@@ -84,118 +101,110 @@ ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase,
ssh_auth_callback auth_fn, void *auth_data)
{
ssh_key key = NULL;
- mbedtls_pk_context *rsa = NULL;
- mbedtls_pk_context *ecdsa = NULL;
- ed25519_privkey *ed25519 = NULL;
- enum ssh_keytypes_e type;
+ mbedtls_pk_context *pk = NULL;
+ mbedtls_pk_type_t mbed_type;
int valid;
/* mbedtls pk_parse_key expects strlen to count the 0 byte */
size_t b64len = strlen(b64_key) + 1;
unsigned char tmp[MAX_PASSPHRASE_SIZE] = {0};
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_ctr_drbg_context *ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
+#endif
- type = pki_privatekey_type_from_string(b64_key);
- if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key.");
- return NULL;
+ pk = malloc(sizeof(mbedtls_pk_context));
+ if (pk == NULL) {
+ goto fail;
}
-
- switch (type) {
- case SSH_KEYTYPE_RSA:
- rsa = malloc(sizeof(mbedtls_pk_context));
- if (rsa == NULL) {
- return NULL;
- }
-
- mbedtls_pk_init(rsa);
-
- if (passphrase == NULL) {
- if (auth_fn) {
- valid = auth_fn("Passphrase for private key:", (char *) tmp,
- MAX_PASSPHRASE_SIZE, 0, 0, auth_data);
- if (valid < 0) {
- goto fail;
- }
- /* TODO fix signedness and strlen */
- valid = mbedtls_pk_parse_key(rsa,
- (const unsigned char *) b64_key,
- b64len, tmp,
- strnlen((const char *) tmp, MAX_PASSPHRASE_SIZE));
- } else {
- valid = mbedtls_pk_parse_key(rsa,
- (const unsigned char *) b64_key,
- b64len, NULL,
- 0);
- }
- } else {
- valid = mbedtls_pk_parse_key(rsa,
- (const unsigned char *) b64_key, b64len,
- (const unsigned char *) passphrase,
- strnlen(passphrase, MAX_PASSPHRASE_SIZE));
- }
-
- if (valid != 0) {
- char error_buf[100];
- mbedtls_strerror(valid, error_buf, 100);
- SSH_LOG(SSH_LOG_WARN,"Parsing private key %s", error_buf);
+ mbedtls_pk_init(pk);
+
+ if (passphrase == NULL) {
+ if (auth_fn) {
+ valid = auth_fn("Passphrase for private key:",
+ (char *)tmp,
+ MAX_PASSPHRASE_SIZE,
+ 0,
+ 0,
+ auth_data);
+ if (valid < 0) {
goto fail;
}
- break;
- case SSH_KEYTYPE_ECDSA_P256:
- case SSH_KEYTYPE_ECDSA_P384:
- case SSH_KEYTYPE_ECDSA_P521:
- ecdsa = malloc(sizeof(mbedtls_pk_context));
- if (ecdsa == NULL) {
- return NULL;
- }
-
- mbedtls_pk_init(ecdsa);
-
- if (passphrase == NULL) {
- if (auth_fn) {
- valid = auth_fn("Passphrase for private key:", (char *) tmp,
- MAX_PASSPHRASE_SIZE, 0, 0, auth_data);
- if (valid < 0) {
- goto fail;
- }
- valid = mbedtls_pk_parse_key(ecdsa,
- (const unsigned char *) b64_key,
- b64len, tmp,
- strnlen((const char *) tmp, MAX_PASSPHRASE_SIZE));
- } else {
- valid = mbedtls_pk_parse_key(ecdsa,
- (const unsigned char *) b64_key,
- b64len, NULL,
- 0);
- }
- } else {
- valid = mbedtls_pk_parse_key(ecdsa,
- (const unsigned char *) b64_key, b64len,
- (const unsigned char *) passphrase,
- strnlen(passphrase, MAX_PASSPHRASE_SIZE));
- }
-
- if (valid != 0) {
- char error_buf[100];
- mbedtls_strerror(valid, error_buf, 100);
- SSH_LOG(SSH_LOG_WARN,"Parsing private key %s", error_buf);
- goto fail;
- }
- break;
- case SSH_KEYTYPE_ED25519:
- /* Cannot open ed25519 keys with libmbedcrypto */
- default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d",
- type);
- return NULL;
+#if MBEDTLS_VERSION_MAJOR > 2
+ valid = mbedtls_pk_parse_key(
+ pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ tmp,
+ strnlen((const char *)tmp, MAX_PASSPHRASE_SIZE),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+#else
+ valid = mbedtls_pk_parse_key(
+ pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ tmp,
+ strnlen((const char *)tmp, MAX_PASSPHRASE_SIZE));
+#endif
+ } else {
+#if MBEDTLS_VERSION_MAJOR > 2
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ NULL,
+ 0,
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+#else
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ NULL,
+ 0);
+#endif
+ }
+ } else {
+#if MBEDTLS_VERSION_MAJOR > 2
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ (const unsigned char *)passphrase,
+ strnlen(passphrase, MAX_PASSPHRASE_SIZE),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+#else
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ (const unsigned char *)passphrase,
+ strnlen(passphrase, MAX_PASSPHRASE_SIZE));
+#endif
+ }
+ if (valid != 0) {
+ char error_buf[100];
+ mbedtls_strerror(valid, error_buf, 100);
+ SSH_LOG(SSH_LOG_WARN, "Parsing private key %s", error_buf);
+ goto fail;
}
+ mbed_type = mbedtls_pk_get_type(pk);
+
key = ssh_key_new();
if (key == NULL) {
goto fail;
}
- if (ecdsa != NULL) {
- mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(*ecdsa);
+ switch (mbed_type) {
+ case MBEDTLS_PK_RSA:
+ case MBEDTLS_PK_RSA_ALT:
+ key->rsa = pk;
+ pk = NULL;
+ key->type = SSH_KEYTYPE_RSA;
+ break;
+ case MBEDTLS_PK_ECKEY:
+ case MBEDTLS_PK_ECDSA: {
+ /* type will be set later */
+ mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(*pk);
+ pk = NULL;
key->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
if (key->ecdsa == NULL) {
@@ -204,40 +213,36 @@ ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase,
mbedtls_ecdsa_init(key->ecdsa);
mbedtls_ecdsa_from_keypair(key->ecdsa, keypair);
- mbedtls_pk_free(ecdsa);
- SAFE_FREE(ecdsa);
+ mbedtls_pk_free(pk);
+ SAFE_FREE(pk);
key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
/* pki_privatekey_type_from_string always returns P256 for ECDSA
- * keys, so we need to figure out the correct type here */
- type = pki_key_ecdsa_to_key_type(key->ecdsa);
- if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid private key.");
+ * keys, so we need to figure out the correct type here */
+ key->type = pki_key_ecdsa_to_key_type(key->ecdsa);
+ if (key->type == SSH_KEYTYPE_UNKNOWN) {
+ SSH_LOG(SSH_LOG_TRACE, "Invalid private key.");
goto fail;
}
- } else {
- key->ecdsa = NULL;
+ break;
+ }
+ default:
+ SSH_LOG(SSH_LOG_WARN,
+ "Unknown or invalid private key type %d",
+ mbed_type);
+ return NULL;
}
- key->type = type;
- key->type_c = ssh_key_type_to_char(type);
+ key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- key->rsa = rsa;
- key->ed25519_privkey = ed25519;
- rsa = NULL;
- ecdsa = NULL;
return key;
fail:
ssh_key_free(key);
- if (rsa != NULL) {
- mbedtls_pk_free(rsa);
- SAFE_FREE(rsa);
- }
- if (ecdsa != NULL) {
- mbedtls_pk_free(ecdsa);
- SAFE_FREE(ecdsa);
+ if (pk != NULL) {
+ mbedtls_pk_free(pk);
+ SAFE_FREE(pk);
}
return NULL;
}
@@ -276,19 +281,19 @@ int pki_privkey_build_rsa(ssh_key key,
ssh_string_data(d), ssh_string_len(d),
ssh_string_data(e), ssh_string_len(e));
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to import private RSA key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to import private RSA key");
goto fail;
}
rc = mbedtls_rsa_complete(rsa);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to complete private RSA key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to complete private RSA key");
goto fail;
}
rc = mbedtls_rsa_check_privkey(rsa);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN, "Inconsistent private RSA key");
+ SSH_LOG(SSH_LOG_TRACE, "Inconsistent private RSA key");
goto fail;
}
@@ -304,6 +309,10 @@ int pki_pubkey_build_rsa(ssh_key key, ssh_string e, ssh_string n)
{
mbedtls_rsa_context *rsa = NULL;
const mbedtls_pk_info_t *pk_info = NULL;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi N;
+ mbedtls_mpi E;
+#endif
int rc;
key->rsa = malloc(sizeof(mbedtls_pk_context));
@@ -320,26 +329,59 @@ int pki_pubkey_build_rsa(ssh_key key, ssh_string e, ssh_string n)
goto fail;
}
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_init(&N);
+ mbedtls_mpi_init(&E);
+#endif
+
rsa = mbedtls_pk_rsa(*key->rsa);
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_mpi_read_binary(&N, ssh_string_data(n),
+ ssh_string_len(n));
+#else
rc = mbedtls_mpi_read_binary(&rsa->N, ssh_string_data(n),
ssh_string_len(n));
+#endif
if (rc != 0) {
goto fail;
}
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_mpi_read_binary(&E, ssh_string_data(e),
+ ssh_string_len(e));
+#else
rc = mbedtls_mpi_read_binary(&rsa->E, ssh_string_data(e),
ssh_string_len(e));
+#endif
if (rc != 0) {
goto fail;
}
- rsa->len = (mbedtls_mpi_bitlen(&rsa->N) + 7) >> 3;
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_import(rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
- return SSH_OK;
+ rc = mbedtls_rsa_complete(rsa);
+ if (rc != 0) {
+ goto fail;
+ }
+#else
+ rsa->len = (mbedtls_mpi_bitlen(&rsa->N) + 7) >> 3;
+#endif
+ rc = SSH_OK;
+ goto exit;
fail:
+ rc = SSH_ERROR;
mbedtls_pk_free(key->rsa);
SAFE_FREE(key->rsa);
- return SSH_ERROR;
+exit:
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+#endif
+ return rc;
}
ssh_key pki_key_dup(const ssh_key key, int demote)
@@ -347,7 +389,13 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
ssh_key new = NULL;
int rc;
const mbedtls_pk_info_t *pk_info = NULL;
-
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi N;
+ mbedtls_mpi E;
+ mbedtls_mpi D;
+ mbedtls_mpi P;
+ mbedtls_mpi Q;
+#endif
new = ssh_key_new();
if (new == NULL) {
@@ -362,6 +410,13 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
new->flags = key->flags;
}
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_init(&N);
+ mbedtls_mpi_init(&E);
+ mbedtls_mpi_init(&D);
+ mbedtls_mpi_init(&P);
+ mbedtls_mpi_init(&Q);
+#endif
switch(key->type) {
case SSH_KEYTYPE_RSA: {
@@ -376,11 +431,26 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
mbedtls_pk_setup(new->rsa, pk_info);
- if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) &&
- mbedtls_pk_can_do(new->rsa, MBEDTLS_PK_RSA)) {
- rsa = mbedtls_pk_rsa(*key->rsa);
- new_rsa = mbedtls_pk_rsa(*new->rsa);
+ if (!mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) ||
+ !mbedtls_pk_can_do(new->rsa, MBEDTLS_PK_RSA))
+ {
+ goto fail;
+ }
+
+ rsa = mbedtls_pk_rsa(*key->rsa);
+ new_rsa = mbedtls_pk_rsa(*new->rsa);
+ if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, &N, &P, &Q, &D, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+ rc = mbedtls_rsa_import(new_rsa, &N, &P, &Q, &D, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+#else
rc = mbedtls_mpi_copy(&new_rsa->N, &rsa->N);
if (rc != 0) {
goto fail;
@@ -390,42 +460,70 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
if (rc != 0) {
goto fail;
}
+
new_rsa->len = (mbedtls_mpi_bitlen(&new_rsa->N) + 7) >> 3;
- if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
- rc = mbedtls_mpi_copy(&new_rsa->D, &rsa->D);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->D, &rsa->D);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->P, &rsa->P);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->P, &rsa->P);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->Q, &rsa->Q);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->Q, &rsa->Q);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->DP, &rsa->DP);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->DP, &rsa->DP);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->DQ, &rsa->DQ);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->DQ, &rsa->DQ);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->QP, &rsa->QP);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->QP, &rsa->QP);
+ if (rc != 0) {
+ goto fail;
}
+#endif
} else {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+ rc = mbedtls_rsa_import(new_rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+#else
+ rc = mbedtls_mpi_copy(&new_rsa->N, &rsa->N);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->E, &rsa->E);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ new_rsa->len = (mbedtls_mpi_bitlen(&new_rsa->N) + 7) >> 3;
+#endif
+ }
+
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_complete(new_rsa);
+ if (rc != 0) {
goto fail;
}
+#endif
break;
}
@@ -443,12 +541,14 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
mbedtls_ecdsa_init(new->ecdsa);
if (demote && ssh_key_is_private(key)) {
- rc = mbedtls_ecp_copy(&new->ecdsa->Q, &key->ecdsa->Q);
+ rc = mbedtls_ecp_copy(&new->ecdsa->MBEDTLS_PRIVATE(Q),
+ &key->ecdsa->MBEDTLS_PRIVATE(Q));
if (rc != 0) {
goto fail;
}
- rc = mbedtls_ecp_group_copy(&new->ecdsa->grp, &key->ecdsa->grp);
+ rc = mbedtls_ecp_group_copy(&new->ecdsa->MBEDTLS_PRIVATE(grp),
+ &key->ecdsa->MBEDTLS_PRIVATE(grp));
if (rc != 0) {
goto fail;
}
@@ -467,10 +567,19 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
goto fail;
}
- return new;
+ goto cleanup;
+
fail:
- ssh_key_free(new);
- return NULL;
+ SSH_KEY_FREE(new);
+cleanup:
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+ mbedtls_mpi_free(&D);
+ mbedtls_mpi_free(&P);
+ mbedtls_mpi_free(&Q);
+#endif
+ return new;
}
int pki_key_generate_rsa(ssh_key key, int parameter)
@@ -508,36 +617,140 @@ int pki_key_generate_rsa(ssh_key key, int parameter)
int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
{
- switch (k1->type) {
+ int rc = 0;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi N1;
+ mbedtls_mpi N2;
+ mbedtls_mpi P1;
+ mbedtls_mpi P2;
+ mbedtls_mpi Q1;
+ mbedtls_mpi Q2;
+ mbedtls_mpi E1;
+ mbedtls_mpi E2;
+
+ mbedtls_mpi_init(&N1);
+ mbedtls_mpi_init(&N2);
+ mbedtls_mpi_init(&P1);
+ mbedtls_mpi_init(&P2);
+ mbedtls_mpi_init(&Q1);
+ mbedtls_mpi_init(&Q2);
+ mbedtls_mpi_init(&E1);
+ mbedtls_mpi_init(&E2);
+#endif
+
+ switch (ssh_key_type_plain(k1->type)) {
case SSH_KEYTYPE_RSA: {
mbedtls_rsa_context *rsa1, *rsa2;
- if (mbedtls_pk_can_do(k1->rsa, MBEDTLS_PK_RSA) &&
- mbedtls_pk_can_do(k2->rsa, MBEDTLS_PK_RSA)) {
- if (mbedtls_pk_get_type(k1->rsa) != mbedtls_pk_get_type(k2->rsa) ||
- mbedtls_pk_get_bitlen(k1->rsa) !=
- mbedtls_pk_get_bitlen(k2->rsa)) {
- return 1;
+ if (!mbedtls_pk_can_do(k1->rsa, MBEDTLS_PK_RSA) ||
+ !mbedtls_pk_can_do(k2->rsa, MBEDTLS_PK_RSA))
+ {
+ break;
+ }
+
+ if (mbedtls_pk_get_type(k1->rsa) != mbedtls_pk_get_type(k2->rsa) ||
+ mbedtls_pk_get_bitlen(k1->rsa) !=
+ mbedtls_pk_get_bitlen(k2->rsa))
+ {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (what == SSH_KEY_CMP_PUBLIC) {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rc = mbedtls_rsa_export(rsa1, &N1, NULL, NULL, NULL, &E1);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ rc = mbedtls_rsa_export(rsa2, &N2, NULL, NULL, NULL, &E2);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
}
+ if (mbedtls_mpi_cmp_mpi(&N1, &N2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&E1, &E2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+#else
rsa1 = mbedtls_pk_rsa(*k1->rsa);
rsa2 = mbedtls_pk_rsa(*k2->rsa);
if (mbedtls_mpi_cmp_mpi(&rsa1->N, &rsa2->N) != 0) {
- return 1;
+ rc = 1;
+ goto cleanup;
}
if (mbedtls_mpi_cmp_mpi(&rsa1->E, &rsa2->E) != 0) {
- return 1;
+ rc = 1;
+ goto cleanup;
+ }
+#endif
+ } else if (what == SSH_KEY_CMP_PRIVATE) {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rc = mbedtls_rsa_export(rsa1, &N1, &P1, &Q1, NULL, &E1);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
}
- if (what == SSH_KEY_CMP_PRIVATE) {
- if (mbedtls_mpi_cmp_mpi(&rsa1->P, &rsa2->P) != 0) {
- return 1;
- }
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ rc = mbedtls_rsa_export(rsa2, &N2, &P2, &Q2, NULL, &E2);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
+ }
- if (mbedtls_mpi_cmp_mpi(&rsa1->Q, &rsa2->Q) != 0) {
- return 1;
- }
+ if (mbedtls_mpi_cmp_mpi(&N1, &N2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&E1, &E2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&P1, &P2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&Q1, &Q2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+#else
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ if (mbedtls_mpi_cmp_mpi(&rsa1->N, &rsa2->N) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->E, &rsa2->E) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->P, &rsa2->P) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->Q, &rsa2->Q) != 0) {
+ rc = 1;
+ goto cleanup;
}
+#endif
}
break;
}
@@ -548,25 +761,39 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
mbedtls_ecp_keypair *ecdsa1 = k1->ecdsa;
mbedtls_ecp_keypair *ecdsa2 = k2->ecdsa;
- if (ecdsa1->grp.id != ecdsa2->grp.id) {
- return 1;
+ if (ecdsa1->MBEDTLS_PRIVATE(grp).id !=
+ ecdsa2->MBEDTLS_PRIVATE(grp).id) {
+ rc = 1;
+ goto cleanup;
}
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.X, &ecdsa2->Q.X)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X),
+ &ecdsa2->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)))
+ {
+ rc = 1;
+ goto cleanup;
}
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.Y, &ecdsa2->Q.Y)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y),
+ &ecdsa2->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)))
+ {
+ rc = 1;
+ goto cleanup;
}
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.Z, &ecdsa2->Q.Z)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z),
+ &ecdsa2->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z)))
+ {
+ rc = 1;
+ goto cleanup;
}
if (what == SSH_KEY_CMP_PRIVATE) {
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->d, &ecdsa2->d)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(d),
+ &ecdsa2->MBEDTLS_PRIVATE(d)))
+ {
+ rc = 1;
+ goto cleanup;
}
}
@@ -575,12 +802,25 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
/* ed25519 keys handled globally */
- return 0;
+ rc = 0;
+ break;
default:
- return 1;
- }
-
- return 0;
+ rc = 1;
+ break;
+ }
+
+cleanup:
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N1);
+ mbedtls_mpi_free(&N2);
+ mbedtls_mpi_free(&P1);
+ mbedtls_mpi_free(&P2);
+ mbedtls_mpi_free(&Q1);
+ mbedtls_mpi_free(&Q2);
+ mbedtls_mpi_free(&E1);
+ mbedtls_mpi_free(&E2);
+#endif
+ return rc;
}
ssh_string make_ecpoint_string(const mbedtls_ecp_group *g, const
@@ -638,15 +878,28 @@ static const char* pki_key_ecdsa_nid_to_char(int nid)
return "unknown";
}
-ssh_string pki_publickey_to_blob(const ssh_key key)
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
{
ssh_buffer buffer = NULL;
ssh_string type_s = NULL;
ssh_string e = NULL;
ssh_string n = NULL;
ssh_string str = NULL;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi E;
+ mbedtls_mpi N;
+ mbedtls_mpi D;
+ mbedtls_mpi IQMP;
+ mbedtls_mpi P;
+ mbedtls_mpi Q;
+#endif
int rc;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_init(&E);
+ mbedtls_mpi_init(&N);
+#endif
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
return NULL;
@@ -685,31 +938,151 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
rsa = mbedtls_pk_rsa(*key->rsa);
- e = ssh_make_bignum_string(&rsa->E);
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ e = ssh_make_bignum_string(&E);
if (e == NULL) {
goto fail;
}
- n = ssh_make_bignum_string(&rsa->N);
+ n = ssh_make_bignum_string(&N);
if (n == NULL) {
goto fail;
}
-
- if (ssh_buffer_add_ssh_string(buffer, e) < 0) {
+#else
+ e = ssh_make_bignum_string(&rsa->E);
+ if (e == NULL) {
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
+ n = ssh_make_bignum_string(&rsa->N);
+ if (n == NULL) {
goto fail;
}
+#endif
+ if (type == SSH_KEY_PUBLIC) {
+ /* The N and E parts are swapped in the public key export ! */
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ } else if (type == SSH_KEY_PRIVATE) {
+ ssh_string p = NULL;
+ ssh_string q = NULL;
+ ssh_string d = NULL;
+ ssh_string iqmp = NULL;
+
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, NULL, &P, &Q, &D, NULL);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ p = ssh_make_bignum_string(&P);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ q = ssh_make_bignum_string(&Q);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ d = ssh_make_bignum_string(&D);
+ if (d == NULL) {
+ goto fail;
+ }
+ rc = mbedtls_rsa_export_crt(rsa, NULL, NULL, &IQMP)
+ if (rc != 0) {
+ goto fail;
+ }
+
+ iqmp = ssh_make_bignum_string(&IQMP);
+ if (iqmp == NULL) {
+ goto fail;
+ }
+
+#else
+ p = ssh_make_bignum_string(&rsa->P);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ q = ssh_make_bignum_string(&rsa->Q);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ d = ssh_make_bignum_string(&rsa->D);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ iqmp = ssh_make_bignum_string(&rsa->QP);
+ if (iqmp == NULL) {
+ goto fail;
+ }
+#endif
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, iqmp);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, p);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, q);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ ssh_string_burn(iqmp);
+ SSH_STRING_FREE(iqmp);
+ iqmp = NULL;
+ ssh_string_burn(p);
+ SSH_STRING_FREE(p);
+ p = NULL;
+ ssh_string_burn(q);
+ SSH_STRING_FREE(q);
+ q = NULL;
+ }
ssh_string_burn(e);
SSH_STRING_FREE(e);
e = NULL;
ssh_string_burn(n);
SSH_STRING_FREE(n);
n = NULL;
-
break;
}
case SSH_KEYTYPE_ECDSA_P256:
@@ -730,7 +1103,8 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
return NULL;
}
- e = make_ecpoint_string(&key->ecdsa->grp, &key->ecdsa->Q);
+ e = make_ecpoint_string(&key->ecdsa->MBEDTLS_PRIVATE(grp),
+ &key->ecdsa->MBEDTLS_PRIVATE(Q));
if (e == NULL) {
SSH_BUFFER_FREE(buffer);
@@ -746,21 +1120,51 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
SSH_STRING_FREE(e);
e = NULL;
- if (key->type == SSH_KEYTYPE_SK_ECDSA &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
- }
+ if (type == SSH_KEY_PRIVATE) {
+ ssh_string d = NULL;
+ d = ssh_make_bignum_string(&key->ecdsa->MBEDTLS_PRIVATE(d));
+
+ if (d == NULL) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ } else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
+ /* public key can contain certificate sk information */
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- rc = pki_ed25519_public_key_to_blob(buffer, key);
- if (rc != SSH_OK) {
- goto fail;
- }
- if (key->type == SSH_KEYTYPE_SK_ED25519 &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PUBLIC) {
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
+ /* public key can contain certificate sk information */
+ if (key->type == SSH_KEYTYPE_SK_ED25519) {
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+ } else {
+ rc = pki_ed25519_private_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
}
break;
default:
@@ -779,6 +1183,10 @@ makestring:
}
SSH_BUFFER_FREE(buffer);
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+#endif
return str;
fail:
SSH_BUFFER_FREE(buffer);
@@ -788,6 +1196,10 @@ fail:
SSH_STRING_FREE(e);
ssh_string_burn(n);
SSH_STRING_FREE(n);
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+#endif
return NULL;
}
@@ -858,7 +1270,7 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
sig_blob = pki_ed25519_signature_to_blob(sig);
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %s",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %s",
sig->type_c);
return NULL;
}
@@ -878,20 +1290,20 @@ static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, const
size_t len = ssh_string_len(sig_blob);
if (pubkey->rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Pubkey RSA field NULL");
+ SSH_LOG(SSH_LOG_TRACE, "Pubkey RSA field NULL");
goto errout;
}
rsalen = mbedtls_pk_get_bitlen(pubkey->rsa) / 8;
if (len > rsalen) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature is too big: %lu > %lu",
(unsigned long) len,
(unsigned long) rsalen);
goto errout;
}
#ifdef DEBUG_CRYPTO
- SSH_LOG(SSH_LOG_WARN, "RSA signature len: %lu", (unsigned long)len);
+ SSH_LOG(SSH_LOG_TRACE, "RSA signature len: %lu", (unsigned long)len);
ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len);
#endif
@@ -932,7 +1344,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
int rc;
if (ssh_key_type_plain(pubkey->type) != type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Incompatible public key provided (%d) expecting (%d)",
type,
pubkey->type);
@@ -1017,7 +1429,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
}
if (rlen != 0) {
- SSH_LOG(SSH_LOG_WARN, "Signature has remaining bytes in inner "
+ SSH_LOG(SSH_LOG_TRACE, "Signature has remaining bytes in inner "
"sigblob: %lu",
(unsigned long)rlen);
ssh_signature_free(sig);
@@ -1035,7 +1447,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
}
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature type");
return NULL;
}
@@ -1051,6 +1463,7 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
mbedtls_md_type_t md = 0;
unsigned char *sig = NULL;
size_t slen;
+ size_t sig_size;
int ok;
switch (hash_type) {
@@ -1065,11 +1478,12 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
break;
case SSH_DIGEST_AUTO:
default:
- SSH_LOG(SSH_LOG_WARN, "Incompatible key algorithm");
+ SSH_LOG(SSH_LOG_TRACE, "Incompatible key algorithm");
return NULL;
}
- sig = malloc(mbedtls_pk_get_bitlen(privkey) / 8);
+ sig_size = mbedtls_pk_get_bitlen(privkey) / 8;
+ sig = malloc(sig_size);
if (sig == NULL) {
return NULL;
}
@@ -1079,6 +1493,9 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
digest,
dlen,
sig,
+#if MBEDTLS_VERSION_MAJOR > 2
+ sig_size,
+#endif
&slen,
mbedtls_ctr_drbg_random,
ssh_get_mbedtls_ctr_drbg_context());
@@ -1145,10 +1562,10 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
return NULL;
}
- rc = mbedtls_ecdsa_sign(&privkey->ecdsa->grp,
+ rc = mbedtls_ecdsa_sign(&privkey->ecdsa->MBEDTLS_PRIVATE(grp),
sig->ecdsa_sig.r,
sig->ecdsa_sig.s,
- &privkey->ecdsa->d,
+ &privkey->ecdsa->MBEDTLS_PRIVATE(d),
hash,
hlen,
mbedtls_ctr_drbg_random,
@@ -1351,8 +1768,9 @@ int pki_verify_data_signature(ssh_signature signature,
case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
- rc = mbedtls_ecdsa_verify(&pubkey->ecdsa->grp, hash, hlen,
- &pubkey->ecdsa->Q, signature->ecdsa_sig.r,
+ rc = mbedtls_ecdsa_verify(&pubkey->ecdsa->MBEDTLS_PRIVATE(grp), hash,
+ hlen, &pubkey->ecdsa->MBEDTLS_PRIVATE(Q),
+ signature->ecdsa_sig.r,
signature->ecdsa_sig.s);
if (rc != 0) {
char error_buf[100];
@@ -1455,18 +1873,19 @@ int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp)
goto fail;
}
- rc = mbedtls_ecp_copy(&keypair.Q, &Q);
+ rc = mbedtls_ecp_copy(&keypair.MBEDTLS_PRIVATE(Q), &Q);
if (rc != 0) {
goto fail;
}
- rc = mbedtls_ecp_group_copy(&keypair.grp, &group);
+ rc = mbedtls_ecp_group_copy(&keypair.MBEDTLS_PRIVATE(grp), &group);
if (rc != 0) {
goto fail;
}
- rc = mbedtls_mpi_read_binary(&keypair.d, ssh_string_data(exp),
- ssh_string_len(exp));
+ rc = mbedtls_mpi_read_binary(&keypair.MBEDTLS_PRIVATE(d),
+ ssh_string_data(exp),
+ ssh_string_len(exp));
if (rc != 0) {
goto fail;
}
@@ -1522,17 +1941,17 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
goto fail;
}
- rc = mbedtls_ecp_copy(&keypair.Q, &Q);
+ rc = mbedtls_ecp_copy(&keypair.MBEDTLS_PRIVATE(Q), &Q);
if (rc != 0) {
goto fail;
}
- rc = mbedtls_ecp_group_copy(&keypair.grp, &group);
+ rc = mbedtls_ecp_group_copy(&keypair.MBEDTLS_PRIVATE(grp), &group);
if (rc != 0) {
goto fail;
}
- mbedtls_mpi_init(&keypair.d);
+ mbedtls_mpi_init(&keypair.MBEDTLS_PRIVATE(d));
rc = mbedtls_ecdsa_from_keypair(key->ecdsa, &keypair);
if (rc != 0) {
@@ -1592,36 +2011,39 @@ int pki_key_generate_ecdsa(ssh_key key, int parameter)
return SSH_OK;
}
-int pki_privkey_build_dss(ssh_key key, ssh_string p, ssh_string q, ssh_string g,
- ssh_string pubkey, ssh_string privkey)
-{
- (void) key;
- (void) p;
- (void) q;
- (void) g;
- (void) pubkey;
- (void) privkey;
- return SSH_ERROR;
-}
-
-int pki_pubkey_build_dss(ssh_key key, ssh_string p, ssh_string q, ssh_string g,
- ssh_string pubkey)
+int ssh_key_size(ssh_key key)
{
- (void) key;
- (void) p;
- (void) q;
- (void) g;
- (void) pubkey;
- return SSH_ERROR;
-}
-
-int pki_key_generate_dss(ssh_key key, int parameter)
-{
- (void) key;
- (void) parameter;
- return SSH_ERROR;
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ return mbedtls_pk_get_bitlen(key->rsa);
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ return 256;
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ return 384;
+ case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ return 521;
+ case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys have fixed size */
+ return 255;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_UNKNOWN:
+ default:
+ return SSH_ERROR;
+ }
}
+#ifdef WITH_PKCS11_URI
int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type)
{
(void) uri_name;
@@ -1631,4 +2053,5 @@ int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type)
"mbedcrypto does not support PKCS #11");
return SSH_ERROR;
}
+#endif /* WITH_PKCS11_URI */
#endif /* HAVE_LIBMBEDCRYPTO */
diff --git a/src/poll.c b/src/poll.c
index 46206949..8f81c11c 100644
--- a/src/poll.c
+++ b/src/poll.c
@@ -44,14 +44,14 @@
#endif
/**
- * @defgroup libssh_poll The SSH poll functions.
+ * @defgroup libssh_poll The SSH poll functions
* @ingroup libssh
*
* Add a generic way to handle sockets asynchronously.
*
* It's based on poll objects, each of which store a socket, its events and a
* callback, which gets called whenever an event is set. The poll objects are
- * attached to a poll context, which should be allocated on per thread basis.
+ * attached to a poll context, which should be allocated on a per thread basis.
*
* Polling the poll context will poll all the attached poll objects and call
* their callbacks (handlers) if any of the socket events are set. This should
@@ -68,7 +68,7 @@ struct ssh_poll_handle_struct {
size_t idx;
} x;
short events;
- int lock;
+ uint32_t lock_cnt;
ssh_poll_callback cb;
void *cb_data;
};
@@ -84,15 +84,18 @@ struct ssh_poll_ctx_struct {
#ifdef HAVE_POLL
#include <poll.h>
-void ssh_poll_init(void) {
+void ssh_poll_init(void)
+{
return;
}
-void ssh_poll_cleanup(void) {
+void ssh_poll_cleanup(void)
+{
return;
}
-int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
+int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
+{
return poll((struct pollfd *) fds, nfds, timeout);
}
@@ -210,8 +213,8 @@ static short bsd_socket_compute_revents(int fd, short events)
* poll implementation.
*
* Keep in mind that select is terribly inefficient. The interface is simply not
- * meant to be used with maximum descriptor value greater, say, 32 or so. With
- * a value as high as 1024 on Linux you'll pay dearly in every single call.
+ * meant to be used with maximum descriptor value greater than, say, 32 or so.
+ * With a value as high as 1024 on Linux you'll pay dearly in every single call.
* poll() will be orders of magnitude faster.
*/
static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
@@ -246,19 +249,17 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
}
#endif
- if (fds[i].events & (POLLIN | POLLRDNORM)) {
- FD_SET (fds[i].fd, &readfds);
- }
+ // we use the readfds to get POLLHUP and POLLERR, which are provided even when not requested
+ FD_SET (fds[i].fd, &readfds);
+
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
FD_SET (fds[i].fd, &writefds);
}
if (fds[i].events & (POLLPRI | POLLRDBAND)) {
FD_SET (fds[i].fd, &exceptfds);
}
- if (fds[i].fd > max_fd &&
- (fds[i].events & (POLLIN | POLLOUT | POLLPRI |
- POLLRDNORM | POLLRDBAND |
- POLLWRNORM | POLLWRBAND))) {
+
+ if (fds[i].fd > max_fd) {
max_fd = fds[i].fd;
rc = 0;
}
@@ -286,7 +287,7 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
if (rc < 0) {
return -1;
}
- /* A timeout occured */
+ /* A timeout occurred */
if (rc == 0) {
return 0;
}
@@ -335,21 +336,24 @@ int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
/**
* @brief Allocate a new poll object, which could be used within a poll context.
*
- * @param fd Socket that will be polled.
- * @param events Poll events that will be monitored for the socket. i.e.
- * POLLIN, POLLPRI, POLLOUT
- * @param cb Function to be called if any of the events are set.
- * The prototype of cb is:
- * int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd,
- * int revents, void *userdata);
- * @param userdata Userdata to be passed to the callback function. NULL if
- * not needed.
+ * @param[in] fd Socket that will be polled.
+ * @param[in] events Poll events that will be monitored for the socket.
+ * i.e. POLLIN, POLLPRI, POLLOUT
+ * @param[in] cb Function to be called if any of the events are set.
+ * The prototype of cb is:
+ * int (*ssh_poll_callback)(ssh_poll_handle p,
+ * socket_t fd,
+ * int revents,
+ * void *userdata);
+ * @param[in] userdata Userdata to be passed to the callback function.
+ * NULL if not needed.
*
- * @return A new poll object, NULL on error
+ * @return A new poll object, NULL on error
*/
-ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb,
- void *userdata) {
+ssh_poll_handle
+ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, void *userdata)
+{
ssh_poll_handle p;
p = malloc(sizeof(struct ssh_poll_handle_struct));
@@ -373,12 +377,13 @@ ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb,
* @param p Pointer to an already allocated poll object.
*/
-void ssh_poll_free(ssh_poll_handle p) {
- if(p->ctx != NULL){
- ssh_poll_ctx_remove(p->ctx,p);
- p->ctx=NULL;
- }
- SAFE_FREE(p);
+void ssh_poll_free(ssh_poll_handle p)
+{
+ if (p->ctx != NULL) {
+ ssh_poll_ctx_remove(p->ctx, p);
+ p->ctx = NULL;
+ }
+ SAFE_FREE(p);
}
/**
@@ -388,8 +393,9 @@ void ssh_poll_free(ssh_poll_handle p) {
*
* @return Poll context or NULL if the poll object isn't attached.
*/
-ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) {
- return p->ctx;
+ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p)
+{
+ return p->ctx;
}
/**
@@ -399,22 +405,31 @@ ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) {
*
* @return Poll events.
*/
-short ssh_poll_get_events(ssh_poll_handle p) {
- return p->events;
+short ssh_poll_get_events(ssh_poll_handle p)
+{
+ return p->events;
}
/**
* @brief Set the events of a poll object. The events will also be propagated
- * to an associated poll context.
+ * to an associated poll context unless the fd is locked. In that case,
+ * only the POLLOUT can be set.
*
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
-void ssh_poll_set_events(ssh_poll_handle p, short events) {
- p->events = events;
- if (p->ctx != NULL && !p->lock) {
- p->ctx->pollfds[p->x.idx].events = events;
- }
+void ssh_poll_set_events(ssh_poll_handle p, short events)
+{
+ p->events = events;
+ if (p->ctx != NULL) {
+ if (p->lock_cnt == 0) {
+ p->ctx->pollfds[p->x.idx].events = events;
+ } else if (!(p->ctx->pollfds[p->x.idx].events & POLLOUT)) {
+ /* if locked, allow only setting POLLOUT to prevent recursive
+ * callbacks */
+ p->ctx->pollfds[p->x.idx].events = events & POLLOUT;
+ }
+ }
}
/**
@@ -424,12 +439,13 @@ void ssh_poll_set_events(ssh_poll_handle p, short events) {
* @param p Pointer to an already allocated poll object.
* @param fd New file descriptor.
*/
-void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) {
- if (p->ctx != NULL) {
- p->ctx->pollfds[p->x.idx].fd = fd;
- } else {
- p->x.fd = fd;
- }
+void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd)
+{
+ if (p->ctx != NULL) {
+ p->ctx->pollfds[p->x.idx].fd = fd;
+ } else {
+ p->x.fd = fd;
+ }
}
/**
@@ -439,8 +455,9 @@ void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) {
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
-void ssh_poll_add_events(ssh_poll_handle p, short events) {
- ssh_poll_set_events(p, ssh_poll_get_events(p) | events);
+void ssh_poll_add_events(ssh_poll_handle p, short events)
+{
+ ssh_poll_set_events(p, ssh_poll_get_events(p) | events);
}
/**
@@ -450,8 +467,9 @@ void ssh_poll_add_events(ssh_poll_handle p, short events) {
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
-void ssh_poll_remove_events(ssh_poll_handle p, short events) {
- ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events);
+void ssh_poll_remove_events(ssh_poll_handle p, short events)
+{
+ ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events);
}
/**
@@ -462,12 +480,13 @@ void ssh_poll_remove_events(ssh_poll_handle p, short events) {
* @return Raw socket.
*/
-socket_t ssh_poll_get_fd(ssh_poll_handle p) {
- if (p->ctx != NULL) {
- return p->ctx->pollfds[p->x.idx].fd;
- }
+socket_t ssh_poll_get_fd(ssh_poll_handle p)
+{
+ if (p->ctx != NULL) {
+ return p->ctx->pollfds[p->x.idx].fd;
+ }
- return p->x.fd;
+ return p->x.fd;
}
/**
* @brief Set the callback of a poll object.
@@ -477,11 +496,12 @@ socket_t ssh_poll_get_fd(ssh_poll_handle p) {
* @param userdata Userdata to be passed to the callback function. NULL if
* not needed.
*/
-void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata) {
- if (cb != NULL) {
- p->cb = cb;
- p->cb_data = userdata;
- }
+void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata)
+{
+ if (cb != NULL) {
+ p->cb = cb;
+ p->cb_data = userdata;
+ }
}
/**
@@ -495,7 +515,8 @@ void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userda
* for the next 5. Set it to 0 if you want to use the
* library's default value.
*/
-ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) {
+ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size)
+{
ssh_poll_ctx ctx;
ctx = malloc(sizeof(struct ssh_poll_ctx_struct));
@@ -518,25 +539,27 @@ ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) {
*
* @param ctx Pointer to an already allocated poll context.
*/
-void ssh_poll_ctx_free(ssh_poll_ctx ctx) {
- if (ctx->polls_allocated > 0) {
- while (ctx->polls_used > 0){
- ssh_poll_handle p = ctx->pollptrs[0];
- /*
- * The free function calls ssh_poll_ctx_remove() and decrements
- * ctx->polls_used
- */
- ssh_poll_free(p);
- }
+void ssh_poll_ctx_free(ssh_poll_ctx ctx)
+{
+ if (ctx->polls_allocated > 0) {
+ while (ctx->polls_used > 0){
+ ssh_poll_handle p = ctx->pollptrs[0];
+ /*
+ * The free function calls ssh_poll_ctx_remove() and decrements
+ * ctx->polls_used
+ */
+ ssh_poll_free(p);
+ }
- SAFE_FREE(ctx->pollptrs);
- SAFE_FREE(ctx->pollfds);
- }
+ SAFE_FREE(ctx->pollptrs);
+ SAFE_FREE(ctx->pollfds);
+ }
- SAFE_FREE(ctx);
+ SAFE_FREE(ctx);
}
-static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) {
+static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size)
+{
ssh_poll_handle *pollptrs;
ssh_pollfd_t *pollfds;
@@ -570,7 +593,8 @@ static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) {
*
* @return 0 on success, < 0 on error
*/
-int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) {
+int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p)
+{
socket_t fd;
if (p->ctx != NULL) {
@@ -604,7 +628,7 @@ int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) {
*/
int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s)
{
- ssh_poll_handle p;
+ ssh_poll_handle p = NULL;
int ret;
p = ssh_socket_get_poll_handle(s);
@@ -622,7 +646,8 @@ int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s)
* @param ctx Pointer to an already allocated poll context.
* @param p Pointer to an already allocated poll object.
*/
-void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
+void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p)
+{
size_t i;
i = p->x.idx;
@@ -648,7 +673,7 @@ void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
* @brief Poll all the sockets associated through a poll object with a
* poll context. If any of the events are set after the poll, the
* call back function of the socket will be called.
- * This function should be called once within the programs main loop.
+ * This function should be called once within the program's main loop.
*
* @param ctx Pointer to an already allocated poll context.
* @param timeout An upper limit on the time for which ssh_poll_ctx() will
@@ -657,7 +682,7 @@ void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
* the poll() function.
* @returns SSH_OK No error.
* SSH_ERROR Error happened during the poll.
- * SSH_AGAIN Timeout occured
+ * SSH_AGAIN Timeout occurred
*/
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
@@ -673,6 +698,15 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
return SSH_ERROR;
}
+ /* Allow only POLLOUT events on locked sockets as that means we are called
+ * recursively and we only want process the POLLOUT events here to flush
+ * output buffer */
+ for (i = 0; i < ctx->polls_used; i++) {
+ /* The lock allows only POLLOUT events: drop the rest */
+ if (ctx->pollptrs[i]->lock_cnt > 0) {
+ ctx->pollfds[i].events &= POLLOUT;
+ }
+ }
ssh_timestamp_init(&ts);
do {
int tm = ssh_timeout_update(&ts, timeout);
@@ -688,17 +722,24 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
used = ctx->polls_used;
for (i = 0; i < used && rc > 0; ) {
- if (!ctx->pollfds[i].revents || ctx->pollptrs[i]->lock) {
+ revents = ctx->pollfds[i].revents;
+ /* Do not pass any other events except for POLLOUT to callback when
+ * called recursively more than 2 times. On s390x the poll will be
+ * spammed with POLLHUP events causing infinite recursion when the user
+ * callback issues some write/flush/poll calls. */
+ if (ctx->pollptrs[i]->lock_cnt > 2) {
+ revents &= POLLOUT;
+ }
+ if (revents == 0) {
i++;
} else {
int ret;
p = ctx->pollptrs[i];
fd = ctx->pollfds[i].fd;
- revents = ctx->pollfds[i].revents;
/* avoid having any event caught during callback */
ctx->pollfds[i].events = 0;
- p->lock = 1;
+ p->lock_cnt++;
if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) {
if (ret == -2) {
return -1;
@@ -709,7 +750,7 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
} else {
ctx->pollfds[i].revents = 0;
ctx->pollfds[i].events = p->events;
- p->lock = 0;
+ p->lock_cnt--;
i++;
}
@@ -727,12 +768,13 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
* @param session SSH session
* @returns the default ssh_poll_ctx
*/
-ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session){
- if(session->default_poll_ctx != NULL)
- return session->default_poll_ctx;
- /* 2 is enough for the default one */
- session->default_poll_ctx = ssh_poll_ctx_new(2);
- return session->default_poll_ctx;
+ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session)
+{
+ if(session->default_poll_ctx != NULL)
+ return session->default_poll_ctx;
+ /* 2 is enough for the default one */
+ session->default_poll_ctx = ssh_poll_ctx_new(2);
+ return session->default_poll_ctx;
}
/* public event API */
@@ -754,10 +796,11 @@ struct ssh_event_struct {
* ssh_session objects and socket fd which are going to be polled at the
* same time as the event context. You would need a single event context
* per thread.
- *
+ *
* @return The ssh_event object on success, NULL on failure.
*/
-ssh_event ssh_event_new(void) {
+ssh_event ssh_event_new(void)
+{
ssh_event event;
event = malloc(sizeof(struct ssh_event_struct));
@@ -784,12 +827,14 @@ ssh_event ssh_event_new(void) {
return event;
}
-static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents,
- void *userdata) {
+static int
+ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents,
+ void *userdata)
+{
struct ssh_event_fd_wrapper *pw = (struct ssh_event_fd_wrapper *)userdata;
(void)p;
- if(pw->cb != NULL) {
+ if (pw->cb != NULL) {
return pw->cb(fd, revents, pw->userdata);
}
return 0;
@@ -812,11 +857,13 @@ static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int rev
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_add_fd(ssh_event event, socket_t fd, short events,
- ssh_event_callback cb, void *userdata) {
+int
+ssh_event_add_fd(ssh_event event, socket_t fd, short events,
+ ssh_event_callback cb, void *userdata)
+{
ssh_poll_handle p;
struct ssh_event_fd_wrapper *pw;
-
+
if(event == NULL || event->ctx == NULL || cb == NULL
|| fd == SSH_INVALID_SOCKET) {
return SSH_ERROR;
@@ -872,7 +919,7 @@ void ssh_event_remove_poll(ssh_event event, ssh_poll_handle p)
}
/**
- * @brief remove the poll handle from session and assign them to a event,
+ * @brief remove the poll handle from session and assign them to an event,
* when used in blocking mode.
*
* @param event The ssh_event object
@@ -881,7 +928,8 @@ void ssh_event_remove_poll(ssh_event event, ssh_poll_handle p)
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_add_session(ssh_event event, ssh_session session) {
+int ssh_event_add_session(ssh_event event, ssh_session session)
+{
ssh_poll_handle p;
#ifdef WITH_SERVER
struct ssh_iterator *iterator;
@@ -933,16 +981,19 @@ int ssh_event_add_session(ssh_event event, ssh_session session) {
*
* @return SSH_ERROR in case of error
*/
-int ssh_event_add_connector(ssh_event event, ssh_connector connector){
+int ssh_event_add_connector(ssh_event event, ssh_connector connector)
+{
return ssh_connector_set_event(connector, event);
}
/**
- * @brief Poll all the sockets and sessions associated through an event object.i
+ * @brief Poll all the sockets and sessions associated through an event object.
*
* If any of the events are set after the poll, the call back functions of the
* sessions or sockets will be called.
* This function should be called once within the programs main loop.
+ * In case of failure, the errno should be consulted to find more information
+ * about the failure set by underlying poll imlpementation.
*
* @param event The ssh_event object to poll.
*
@@ -951,13 +1002,15 @@ int ssh_event_add_connector(ssh_event event, ssh_connector connector){
* means an infinite timeout. This parameter is passed to
* the poll() function.
* @returns SSH_OK on success.
- * SSH_ERROR Error happened during the poll.
- * SSH_AGAIN Timeout occured
+ * SSH_ERROR Error happened during the poll. Check errno to get more
+ * details about why it failed.
+ * SSH_AGAIN Timeout occurred
*/
-int ssh_event_dopoll(ssh_event event, int timeout) {
+int ssh_event_dopoll(ssh_event event, int timeout)
+{
int rc;
- if(event == NULL || event->ctx == NULL) {
+ if (event == NULL || event->ctx == NULL) {
return SSH_ERROR;
}
rc = ssh_poll_ctx_dopoll(event->ctx, timeout);
@@ -973,7 +1026,8 @@ int ssh_event_dopoll(ssh_event event, int timeout) {
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_remove_fd(ssh_event event, socket_t fd) {
+int ssh_event_remove_fd(ssh_event event, socket_t fd)
+{
register size_t i, used;
int rc = SSH_ERROR;
@@ -1019,7 +1073,8 @@ int ssh_event_remove_fd(ssh_event event, socket_t fd) {
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_remove_session(ssh_event event, ssh_session session) {
+int ssh_event_remove_session(ssh_event event, ssh_session session)
+{
ssh_poll_handle p;
register size_t i, used;
int rc = SSH_ERROR;
@@ -1027,14 +1082,14 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
struct ssh_iterator *iterator;
#endif
- if(event == NULL || event->ctx == NULL || session == NULL) {
+ if (event == NULL || event->ctx == NULL || session == NULL) {
return SSH_ERROR;
}
used = event->ctx->polls_used;
- for(i = 0; i < used; i++) {
- p = event->ctx->pollptrs[i];
- if(p->session == session){
+ for (i = 0; i < used; i++) {
+ p = event->ctx->pollptrs[i];
+ if (p->session == session) {
/*
* ssh_poll_ctx_remove() decrements
* event->ctx->polls_used
@@ -1054,8 +1109,8 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
}
#ifdef WITH_SERVER
iterator = ssh_list_get_iterator(event->sessions);
- while(iterator != NULL) {
- if((ssh_session)iterator->data == session) {
+ while (iterator != NULL) {
+ if ((ssh_session)iterator->data == session) {
ssh_list_remove(event->sessions, iterator);
/* there should be only one instance of this session */
break;
@@ -1073,7 +1128,8 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
* @return SSH_OK on success
* @return SSH_ERROR on failure
*/
-int ssh_event_remove_connector(ssh_event event, ssh_connector connector){
+int ssh_event_remove_connector(ssh_event event, ssh_connector connector)
+{
(void)event;
return ssh_connector_remove_event(connector);
}
@@ -1091,13 +1147,13 @@ void ssh_event_free(ssh_event event)
size_t used, i;
ssh_poll_handle p;
- if(event == NULL) {
+ if (event == NULL) {
return;
}
if (event->ctx != NULL) {
used = event->ctx->polls_used;
- for(i = 0; i < used; i++) {
+ for (i = 0; i < used; i++) {
p = event->ctx->pollptrs[i];
if (p->session != NULL) {
ssh_poll_ctx_remove(event->ctx, p);
@@ -1110,7 +1166,7 @@ void ssh_event_free(ssh_event event)
ssh_poll_ctx_free(event->ctx);
}
#ifdef WITH_SERVER
- if(event->sessions != NULL) {
+ if (event->sessions != NULL) {
ssh_list_free(event->sessions);
}
#endif
diff --git a/src/scp.c b/src/scp.c
index 2e4b8620..40f45dbc 100644
--- a/src/scp.c
+++ b/src/scp.c
@@ -37,10 +37,14 @@
*
* SCP protocol over SSH functions
*
+ * @deprecated Please use SFTP instead
+ *
* @{
*/
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Create a new scp session.
*
* @param[in] session The SSH session to use.
@@ -62,7 +66,7 @@ ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location)
{
ssh_scp scp = NULL;
- if (session == NULL) {
+ if (session == NULL || location == NULL) {
goto error;
}
@@ -108,6 +112,8 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Initialize the scp channel.
*
* @param[in] scp The scp context to initialize.
@@ -140,7 +146,7 @@ int ssh_scp_init(ssh_scp scp)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Initializing scp session %s %son location '%s'",
+ SSH_LOG(SSH_LOG_DEBUG, "Initializing scp session %s %son location '%s'",
scp->mode == SSH_SCP_WRITE?"write":"read",
scp->recursive ? "recursive " : "",
scp->location);
@@ -230,6 +236,8 @@ int ssh_scp_init(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Close the scp channel.
*
* @param[in] scp The scp context to close.
@@ -277,6 +285,8 @@ int ssh_scp_close(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Free a scp context.
*
* @param[in] scp The context to free.
@@ -304,6 +314,8 @@ void ssh_scp_free(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Create a directory in a scp in sink mode.
*
* @param[in] scp The scp handle.
@@ -313,7 +325,7 @@ void ssh_scp_free(ssh_scp scp)
* @param[in] mode The UNIX permissions for the new directory, e.g. 0755.
*
* @returns SSH_OK if the directory has been created, SSH_ERROR if
- * an error occured.
+ * an error occurred.
*
* @see ssh_scp_leave_directory()
*/
@@ -364,7 +376,7 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"SCP pushing directory %s with permissions '%s'",
vis_encoded, perms);
@@ -399,10 +411,12 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Leave a directory.
*
* @returns SSH_OK if the directory has been left, SSH_ERROR if an
- * error occured.
+ * error occurred.
*
* @see ssh_scp_push_directory()
*/
@@ -436,6 +450,8 @@ int ssh_scp_leave_directory(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit
* size.
*
@@ -449,7 +465,7 @@ int ssh_scp_leave_directory(ssh_scp scp)
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
*
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
- * error occured.
+ * error occurred.
*
* @see ssh_scp_push_file()
*/
@@ -501,7 +517,7 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",
vis_encoded, size, perms);
@@ -540,6 +556,8 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Initialize the sending of a file to a scp in sink mode.
*
* @param[in] scp The scp handle.
@@ -552,7 +570,7 @@ error:
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
*
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
- * error occured.
+ * error occurred.
*/
int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
{
@@ -562,6 +580,8 @@ int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
/**
* @internal
*
+ * @deprecated Please use SFTP instead
+ *
* @brief Wait for a response of the scp server.
*
* @param[in] scp The scp handle.
@@ -569,7 +589,7 @@ int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
* @param[out] response A pointer where the response message must be copied if
* any. This pointer must then be free'd.
*
- * @returns The return code, SSH_ERROR a error occured.
+ * @returns The return code, SSH_ERROR a error occurred.
*/
int ssh_scp_response(ssh_scp scp, char **response)
{
@@ -628,6 +648,8 @@ int ssh_scp_response(ssh_scp scp, char **response)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Write into a remote scp file.
*
* @param[in] scp The scp handle.
@@ -637,7 +659,7 @@ int ssh_scp_response(ssh_scp scp, char **response)
* @param[in] len The number of bytes to write.
*
* @returns SSH_OK if the write was successful, SSH_ERROR an error
- * occured while writing.
+ * occurred while writing.
*/
int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
{
@@ -671,7 +693,6 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
scp->processed += w;
} else {
scp->state = SSH_SCP_ERROR;
- //return = channel_get_exit_status(scp->channel);
return SSH_ERROR;
}
@@ -702,6 +723,8 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Read a string on a channel, terminated by '\n'
*
* @param[in] scp The scp handle.
@@ -713,7 +736,7 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
* null-terminated.
*
* @returns SSH_OK if the string was read, SSH_ERROR if an error
- * occured while reading.
+ * occurred while reading.
*/
int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
{
@@ -748,6 +771,8 @@ int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Wait for a scp request (file, directory).
*
* @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending
@@ -800,7 +825,7 @@ int ssh_scp_pull_request(ssh_scp scp)
*p = '\0';
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SCP request: '%s'", buffer);
+ SSH_LOG(SSH_LOG_DEBUG, "Received SCP request: '%s'", buffer);
switch(buffer[0]) {
case 'C':
/* File */
@@ -859,7 +884,7 @@ int ssh_scp_pull_request(ssh_scp scp)
return SSH_ERROR;
}
- /* a parsing error occured */
+ /* a parsing error occurred */
error:
SAFE_FREE(name);
SAFE_FREE(mode);
@@ -869,6 +894,8 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Deny the transfer of a file or creation of a directory coming from the
* remote party.
*
@@ -915,6 +942,8 @@ int ssh_scp_deny_request(ssh_scp scp, const char *reason)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Accepts transfer of a file or creation of a directory coming from the
* remote party.
*
@@ -951,14 +980,18 @@ int ssh_scp_accept_request(ssh_scp scp)
return SSH_OK;
}
-/** @brief Read from a remote scp file
+/**
+ * @deprecated Please use SFTP instead
+ *
+ * @brief Read from a remote scp file
+ *
* @param[in] scp The scp handle.
*
* @param[in] buffer The destination buffer.
*
* @param[in] size The size of the buffer.
*
- * @returns The nNumber of bytes read, SSH_ERROR if an error occured
+ * @returns The number of bytes read, SSH_ERROR if an error occurred
* while reading.
*/
int ssh_scp_read(ssh_scp scp, void *buffer, size_t size)
@@ -1022,6 +1055,8 @@ int ssh_scp_read(ssh_scp scp, void *buffer, size_t size)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Get the name of the directory or file being pushed from the other
* party.
*
@@ -1038,6 +1073,8 @@ const char *ssh_scp_request_get_filename(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Get the permissions of the directory or file being pushed from the
* other party.
*
@@ -1052,7 +1089,10 @@ int ssh_scp_request_get_permissions(ssh_scp scp)
return scp->request_mode;
}
-/** @brief Get the size of the file being pushed from the other party.
+/**
+ * @deprecated Please use SFTP instead
+ *
+ * @brief Get the size of the file being pushed from the other party.
*
* @returns The numeric size of the file being read.
* @warning The real size may not fit in a 32 bits field and may
@@ -1067,7 +1107,10 @@ size_t ssh_scp_request_get_size(ssh_scp scp)
return (size_t)scp->filelen;
}
-/** @brief Get the size of the file being pushed from the other party.
+/**
+ * @deprecated Please use SFTP instead
+ *
+ * @brief Get the size of the file being pushed from the other party.
*
* @returns The numeric size of the file being read.
*/
@@ -1080,6 +1123,8 @@ uint64_t ssh_scp_request_get_size64(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Convert a scp text mode to an integer.
*
* @param[in] mode The mode to convert, e.g. "0644".
@@ -1093,6 +1138,8 @@ int ssh_scp_integer_mode(const char *mode)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Convert a unix mode into a scp string.
*
* @param[in] mode The mode to convert, e.g. 420 or 0644.
@@ -1108,6 +1155,8 @@ char *ssh_scp_string_mode(int mode)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Get the warning string from a scp handle.
*
* @param[in] scp The scp handle.
diff --git a/src/server.c b/src/server.c
index 841a1c42..28c3c015 100644
--- a/src/server.c
+++ b/src/server.c
@@ -92,7 +92,11 @@ int server_set_kex(ssh_session session)
size_t len;
int ok;
- ZERO_STRUCTP(server);
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (server->methods[0] != NULL) {
+ return SSH_OK;
+ }
ok = ssh_get_random(server->cookie, 16, 0);
if (!ok) {
@@ -113,15 +117,6 @@ int server_set_kex(ssh_session session)
",%s", session->srv.ecdsa_key->type_c);
}
#endif
-#ifdef HAVE_DSA
- if (session->srv.dsa_key != NULL) {
- len = strlen(hostkeys);
- keytype = ssh_key_type(session->srv.dsa_key);
-
- snprintf(hostkeys + len, sizeof(hostkeys) - len,
- ",%s", ssh_key_type_to_char(keytype));
- }
-#endif
if (session->srv.rsa_key != NULL) {
/* We support also the SHA2 variants */
len = strlen(hostkeys);
@@ -160,7 +155,8 @@ int server_set_kex(ssh_session session)
rc = ssh_options_set_algo(session,
SSH_HOSTKEYS,
- kept);
+ kept,
+ &session->opts.wanted_methods[SSH_HOSTKEYS]);
SAFE_FREE(kept);
if (rc < 0) {
return -1;
@@ -191,7 +187,13 @@ int server_set_kex(ssh_session session)
}
}
- return 0;
+ /* Do not append the extensions during rekey */
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
+ return SSH_OK;
+ }
+
+ rc = ssh_kex_append_extensions(session, server);
+ return rc;
}
int ssh_server_init_kex(ssh_session session) {
@@ -281,9 +283,6 @@ ssh_get_key_params(ssh_session session,
int rc;
switch(session->srv.hostkey) {
- case SSH_KEYTYPE_DSS:
- *privkey = session->srv.dsa_key;
- break;
case SSH_KEYTYPE_RSA:
*privkey = session->srv.rsa_key;
break;
@@ -335,117 +334,127 @@ ssh_get_key_params(ssh_session session,
* @brief A function to be called each time a step has been done in the
* connection.
*/
-static void ssh_server_connection_callback(ssh_session session){
+static void ssh_server_connection_callback(ssh_session session)
+{
int rc;
- switch(session->session_state){
- case SSH_SESSION_STATE_NONE:
- case SSH_SESSION_STATE_CONNECTING:
- case SSH_SESSION_STATE_SOCKET_CONNECTED:
- break;
- case SSH_SESSION_STATE_BANNER_RECEIVED:
- if (session->clientbanner == NULL) {
+ switch (session->session_state) {
+ case SSH_SESSION_STATE_NONE:
+ case SSH_SESSION_STATE_CONNECTING:
+ case SSH_SESSION_STATE_SOCKET_CONNECTED:
+ break;
+ case SSH_SESSION_STATE_BANNER_RECEIVED:
+ if (session->clientbanner == NULL) {
+ goto error;
+ }
+ set_status(session, 0.4f);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "SSH client banner: %s", session->clientbanner);
+
+ /* Here we analyze the different protocols the server allows. */
+ rc = ssh_analyze_banner(session, 1);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "No version of SSH protocol usable (banner: %s)",
+ session->clientbanner);
+ goto error;
+ }
+
+ /* from now, the packet layer is handling incoming packets */
+ ssh_packet_register_socket_callback(session, session->socket);
+
+ ssh_packet_set_default_callbacks(session);
+ set_status(session, 0.5f);
+ session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
+ rc = ssh_send_kex(session);
+ if (rc < 0) {
+ goto error;
+ }
+ break;
+ case SSH_SESSION_STATE_INITIAL_KEX:
+ /* TODO: This state should disappear in favor of get_key handle */
+ break;
+ case SSH_SESSION_STATE_KEXINIT_RECEIVED:
+ set_status(session, 0.6f);
+ if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
+ rc = server_set_kex(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- set_status(session, 0.4f);
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SSH client banner: %s", session->clientbanner);
-
- /* Here we analyze the different protocols the server allows. */
- rc = ssh_analyze_banner(session, 1);
+ /* We are in a rekeying, so we need to send the server kex */
+ rc = ssh_send_kex(session);
if (rc < 0) {
- ssh_set_error(session, SSH_FATAL,
- "No version of SSH protocol usable (banner: %s)",
- session->clientbanner);
goto error;
}
+ }
+ ssh_list_kex(&session->next_crypto->client_kex); // log client kex
+ rc = ssh_kex_select_methods(session);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = crypt_set_algorithms_server(session);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
+ set_status(session, 0.8f);
+ session->session_state = SSH_SESSION_STATE_DH;
+ break;
+ case SSH_SESSION_STATE_DH:
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
- /* from now, the packet layer is handling incoming packets */
- session->socket_callbacks.data=ssh_packet_socket_callback;
- ssh_packet_register_socket_callback(session, session->socket);
-
- ssh_packet_set_default_callbacks(session);
- set_status(session, 0.5f);
- session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
- if (ssh_send_kex(session, 1) < 0) {
- goto error;
- }
- break;
- case SSH_SESSION_STATE_INITIAL_KEX:
- /* TODO: This state should disappear in favor of get_key handle */
- break;
- case SSH_SESSION_STATE_KEXINIT_RECEIVED:
- set_status(session,0.6f);
- if(session->next_crypto->server_kex.methods[0]==NULL){
- if(server_set_kex(session) == SSH_ERROR)
- goto error;
- /* We are in a rekeying, so we need to send the server kex */
- if(ssh_send_kex(session, 1) < 0)
- goto error;
- }
- ssh_list_kex(&session->next_crypto->client_kex); // log client kex
- if (ssh_kex_select_methods(session) < 0) {
+ rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
+ if (rc != SSH_OK) {
goto error;
}
- if (crypt_set_algorithms_server(session) == SSH_ERROR)
- goto error;
- set_status(session,0.8f);
- session->session_state=SSH_SESSION_STATE_DH;
- break;
- case SSH_SESSION_STATE_DH:
- if(session->dh_handshake_state==DH_STATE_FINISHED){
-
- rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
- if (rc != SSH_OK) {
- goto error;
- }
+ /*
+ * If the client supports extension negotiation, we will send
+ * our supported extensions now. This is the first message after
+ * sending NEWKEYS message and after turning on crypto.
+ */
+ if (session->extensions & SSH_EXT_NEGOTIATION &&
+ session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
/*
- * If the client supports extension negotiation, we will send
- * our supported extensions now. This is the first message after
- * sending NEWKEYS message and after turning on crypto.
+ * Only send an SSH_MSG_EXT_INFO message the first time the
+ * client undergoes NEWKEYS. It is unexpected for this message
+ * to be sent upon rekey, and may cause clients to log error
+ * messages.
+ *
+ * The session_state can not be used for this purpose because it
+ * is re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey.
+ * So, use the connected flag which transitions from non-zero
+ * below.
+ *
+ * See also:
+ * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929
*/
- if (session->extensions & SSH_EXT_NEGOTIATION &&
- session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
-
- /*
- * Only send an SSH_MSG_EXT_INFO message the first time the client
- * undergoes NEWKEYS. It is unexpected for this message to be sent
- * upon rekey, and may cause clients to log error messages.
- *
- * The session_state can not be used for this purpose because it is
- * re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey. So,
- * use the connected flag which transitions from non-zero below.
- *
- * See also:
- * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929
- */
- if (session->connected == 0) {
- ssh_server_send_extensions(session);
- }
+ if (session->connected == 0) {
+ ssh_server_send_extensions(session);
}
+ }
- set_status(session,1.0f);
- session->connected = 1;
- session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
- session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
+ set_status(session, 1.0f);
+ session->connected = 1;
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATING;
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
- }
- break;
- case SSH_SESSION_STATE_AUTHENTICATING:
- break;
- case SSH_SESSION_STATE_ERROR:
- goto error;
- default:
- ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
+ }
+ break;
+ case SSH_SESSION_STATE_AUTHENTICATING:
+ break;
+ case SSH_SESSION_STATE_ERROR:
+ goto error;
+ default:
+ ssh_set_error(session, SSH_FATAL, "Invalid state %d",
+ session->session_state);
}
return;
error:
ssh_socket_close(session->socket);
session->alive = 0;
- session->session_state=SSH_SESSION_STATE_ERROR;
+ session->session_state = SSH_SESSION_STATE_ERROR;
}
/**
@@ -459,16 +468,17 @@ error:
* @param user is a pointer to session
* @returns Number of bytes processed, or zero if the banner is not complete.
*/
-static int callback_receive_banner(const void *data, size_t len, void *user) {
- char *buffer = (char *) data;
- ssh_session session = (ssh_session) user;
+static size_t callback_receive_banner(const void *data, size_t len, void *user)
+{
+ char *buffer = (char *)data;
+ ssh_session session = (ssh_session)user;
char *str = NULL;
size_t i;
- int ret=0;
+ size_t processed = 0;
for (i = 0; i < len; i++) {
#ifdef WITH_PCAP
- if(session->pcap_ctx && buffer[i] == '\n') {
+ if (session->pcap_ctx && buffer[i] == '\n') {
ssh_pcap_context_write(session->pcap_ctx,
SSH_PCAP_DIR_IN,
buffer,
@@ -477,33 +487,34 @@ static int callback_receive_banner(const void *data, size_t len, void *user) {
}
#endif
if (buffer[i] == '\r') {
- buffer[i]='\0';
+ buffer[i] = '\0';
}
if (buffer[i] == '\n') {
- buffer[i]='\0';
+ buffer[i] = '\0';
str = strdup(buffer);
/* number of bytes read */
- ret = i + 1;
+ processed = i + 1;
session->clientbanner = str;
session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED;
SSH_LOG(SSH_LOG_PACKET, "Received banner: %s", str);
session->ssh_connection_callback(session);
- return ret;
+ return processed;
}
- if(i > 127) {
+ if (i > 127) {
/* Too big banner */
session->session_state = SSH_SESSION_STATE_ERROR;
- ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner");
+ ssh_set_error(session, SSH_FATAL,
+ "Receiving banner: too large banner");
return 0;
}
}
- return ret;
+ return processed;
}
/* returns 0 until the key exchange is not finished */
@@ -524,11 +535,40 @@ void ssh_set_auth_methods(ssh_session session, int auth_methods)
session->auth.supported_methods = (uint32_t)auth_methods & 0x3fU;
}
+int ssh_send_issue_banner(ssh_session session, const ssh_string banner)
+{
+ int rc = SSH_ERROR;
+
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Sending a server issue banner");
+
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bSs",
+ SSH2_MSG_USERAUTH_BANNER,
+ banner,
+ "");
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+
+ rc = ssh_packet_send(session);
+ return rc;
+}
+
/* Do the banner and key exchange */
-int ssh_handle_key_exchange(ssh_session session) {
+int ssh_handle_key_exchange(ssh_session session)
+{
int rc;
- if (session->session_state != SSH_SESSION_STATE_NONE)
- goto pending;
+
+ if (session->session_state != SSH_SESSION_STATE_NONE) {
+ goto pending;
+ }
+
rc = ssh_send_banner(session, 1);
if (rc < 0) {
return SSH_ERROR;
@@ -539,27 +579,28 @@ int ssh_handle_key_exchange(ssh_session session) {
session->ssh_connection_callback = ssh_server_connection_callback;
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
ssh_socket_set_callbacks(session->socket,&session->socket_callbacks);
- session->socket_callbacks.data=callback_receive_banner;
- session->socket_callbacks.exception=ssh_socket_exception_callback;
- session->socket_callbacks.userdata=session;
+ session->socket_callbacks.data = callback_receive_banner;
+ session->socket_callbacks.exception = ssh_socket_exception_callback;
+ session->socket_callbacks.userdata = session;
rc = server_set_kex(session);
if (rc < 0) {
return SSH_ERROR;
}
- pending:
+pending:
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
- ssh_server_kex_termination,session);
+ ssh_server_kex_termination,session);
SSH_LOG(SSH_LOG_PACKET, "ssh_handle_key_exchange: current state : %d",
- session->session_state);
- if (rc != SSH_OK)
- return rc;
+ session->session_state);
+ if (rc != SSH_OK) {
+ return rc;
+ }
if (session->session_state == SSH_SESSION_STATE_ERROR ||
session->session_state == SSH_SESSION_STATE_DISCONNECTED) {
- return SSH_ERROR;
+ return SSH_ERROR;
}
- return SSH_OK;
+ return SSH_OK;
}
/* messages */
@@ -648,7 +689,7 @@ static int ssh_message_channel_request_reply_default(ssh_message msg) {
channel = msg->channel_request.channel->remote_channel;
SSH_LOG(SSH_LOG_PACKET,
- "Sending a default channel_request denied to channel %d", channel);
+ "Sending a default channel_request denied to channel %" PRIu32, channel);
rc = ssh_buffer_pack(msg->session->out_buffer,
"bd",
@@ -672,6 +713,13 @@ static int ssh_message_service_request_reply_default(ssh_message msg) {
return ssh_message_service_reply_success(msg);
}
+/**
+ * @brief Sends SERVICE_ACCEPT to the client
+ *
+ * @param msg The message to reply to
+ *
+ * @returns SSH_OK when success otherwise SSH_ERROR
+ */
int ssh_message_service_reply_success(ssh_message msg) {
ssh_session session;
int rc;
@@ -696,6 +744,15 @@ int ssh_message_service_reply_success(ssh_message msg) {
return rc;
}
+/**
+ * @brief Send a global request success message
+ *
+ * @param msg The message
+ *
+ * @param bound_port The remote bind port
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) {
int rc;
@@ -707,7 +764,7 @@ int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_por
goto error;
}
- if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
+ if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
&& msg->global_request.bind_port == 0) {
rc = ssh_buffer_pack(msg->session->out_buffer, "d", bound_port);
if (rc != SSH_OK) {
@@ -719,7 +776,7 @@ int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_por
return ssh_packet_send(msg->session);
}
- if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
+ if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
&& msg->global_request.bind_port == 0) {
SSH_LOG(SSH_LOG_PACKET,
"The client doesn't want to know the remote port!");
@@ -774,6 +831,13 @@ int ssh_message_reply_default(ssh_message msg) {
return -1;
}
+/**
+ * @brief Gets the service name from the service request message
+ *
+ * @param msg The service request message
+ *
+ * @returns the service name from the message
+ */
const char *ssh_message_service_service(ssh_message msg){
if (msg == NULL) {
return NULL;
@@ -805,7 +869,6 @@ ssh_key ssh_message_auth_pubkey(ssh_message msg) {
return msg->auth_request.pubkey;
}
-/* Get the publickey of an auth request */
ssh_public_key ssh_message_auth_publickey(ssh_message msg){
if (msg == NULL) {
return NULL;
@@ -821,6 +884,13 @@ enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){
return msg->auth_request.signature_state;
}
+/**
+ * @brief Check if the message is a keyboard-interactive response
+ *
+ * @param msg The message to check
+ *
+ * @returns 1 if the message is a response, otherwise 0
+ */
int ssh_message_auth_kbdint_is_response(ssh_message msg) {
if (msg == NULL) {
return -1;
@@ -830,6 +900,17 @@ int ssh_message_auth_kbdint_is_response(ssh_message msg) {
}
/* FIXME: methods should be unsigned */
+/**
+ * @brief Sets the supported authentication methods to a message
+ *
+ * @param msg The message
+ *
+ * @param methods Methods to set to the message.
+ * The supported methods are listed in ssh_set_auth_methods
+ * @see ssh_set_auth_methods
+ *
+ * @returns 0 on success, otherwise -1
+ */
int ssh_message_auth_set_methods(ssh_message msg, int methods) {
if (msg == NULL || msg->session == NULL) {
return -1;
@@ -884,9 +965,8 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
/* fill in the kbdint structure */
if (msg->session->kbdint == NULL) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a "
- "keyboard-interactive response but it "
- "seems we didn't send the request.");
+ SSH_LOG(SSH_LOG_DEBUG, "Warning: Got a keyboard-interactive response "
+ "but it seems we didn't send the request.");
msg->session->kbdint = ssh_kbdint_new();
if (msg->session->kbdint == NULL) {
@@ -950,6 +1030,17 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
return rc;
}
+/**
+ * @brief Sends SSH2_MSG_USERAUTH_SUCCESS or SSH2_MSG_USERAUTH_FAILURE message
+ * depending on the success of the authentication method
+ *
+ * @param session The session to reply to
+ *
+ * @param partial Denotes if the authentication process was partially completed
+ * (unsuccessful)
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_auth_reply_success(ssh_session session, int partial)
{
struct ssh_crypto_struct *crypto = NULL;
@@ -981,13 +1072,13 @@ int ssh_auth_reply_success(ssh_session session, int partial)
crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
if (crypto != NULL && crypto->delayed_compress_out) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Enabling delayed compression OUT");
+ SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT");
crypto->do_compress_out = 1;
}
crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
if (crypto != NULL && crypto->delayed_compress_in) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Enabling delayed compression IN");
+ SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN");
crypto->do_compress_in = 1;
}
return r;
@@ -999,7 +1090,17 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) {
return ssh_auth_reply_success(msg->session, partial);
}
-/* Answer OK to a pubkey auth request */
+/**
+ * @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
+ *
+ * @param msg The message
+ *
+ * @param algo The algorithm of the accepted public key
+ *
+ * @param pubkey The accepted public key
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) {
int rc;
if (msg == NULL) {
@@ -1020,12 +1121,19 @@ int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pu
return rc;
}
+/**
+ * @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
+ *
+ * @param msg The message
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) {
ssh_string algo;
ssh_string pubkey_blob = NULL;
int ret;
- algo = ssh_string_from_char(msg->auth_request.pubkey->type_c);
+ algo = ssh_string_from_char(msg->auth_request.sigtype);
if (algo == NULL) {
return SSH_ERROR;
}
@@ -1170,6 +1278,13 @@ int ssh_execute_message_callbacks(ssh_session session){
return SSH_OK;
}
+/**
+ * @brief Sends a keepalive message to the session
+ *
+ * @param session The session to send the message to
+ *
+ * @returns SSH_OK
+ */
int ssh_send_keepalive(ssh_session session)
{
/* Client denies the request, so the error code is not meaningful */
diff --git a/src/session.c b/src/session.c
index b5ffcbc2..279352b6 100644
--- a/src/session.c
+++ b/src/session.c
@@ -43,7 +43,7 @@
#define FIRST_CHANNEL 42 // why not ? it helps to find bugs.
/**
- * @defgroup libssh_session The SSH session functions.
+ * @defgroup libssh_session The SSH session functions
* @ingroup libssh
*
* Functions that manage a session.
@@ -97,36 +97,53 @@ ssh_session ssh_new(void)
ssh_set_blocking(session, 1);
session->maxchannel = FIRST_CHANNEL;
-#ifndef _WIN32
session->agent = ssh_agent_new(session);
if (session->agent == NULL) {
goto err;
}
-#endif /* _WIN32 */
/* OPTIONS */
session->opts.StrictHostKeyChecking = 1;
- session->opts.port = 0;
+ session->opts.port = 22;
session->opts.fd = -1;
session->opts.compressionlevel = 7;
session->opts.nodelay = 0;
+ session->opts.identities_only = false;
+ session->opts.control_master = SSH_CONTROL_MASTER_NO;
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.exp_flags = 0;
+
session->opts.identity = ssh_list_new();
if (session->opts.identity == NULL) {
goto err;
}
+ session->opts.identity_non_exp = ssh_list_new();
+ if (session->opts.identity_non_exp == NULL) {
+ goto err;
+ }
+
+ session->opts.certificate = ssh_list_new();
+ if (session->opts.certificate == NULL) {
+ goto err;
+ }
+ session->opts.certificate_non_exp = ssh_list_new();
+ if (session->opts.certificate_non_exp == NULL) {
+ goto err;
+ }
+ /* the default certificates are loaded automatically from the default
+ * identities later */
id = strdup("%d/id_ed25519");
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -136,7 +153,7 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -146,22 +163,11 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
-#ifdef HAVE_DSA
- id = strdup("%d/id_dsa");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-#endif
-
/* Explicitly initialize states */
session->session_state = SSH_SESSION_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
@@ -242,12 +248,8 @@ void ssh_free(ssh_session session)
crypto_free(session->current_crypto);
crypto_free(session->next_crypto);
-#ifndef _WIN32
ssh_agent_free(session->agent);
-#endif /* _WIN32 */
- ssh_key_free(session->srv.dsa_key);
- session->srv.dsa_key = NULL;
ssh_key_free(session->srv.rsa_key);
session->srv.rsa_key = NULL;
ssh_key_free(session->srv.ecdsa_key);
@@ -286,22 +288,56 @@ void ssh_free(ssh_session session)
ssh_list_free(session->opts.identity);
}
+ if (session->opts.identity_non_exp) {
+ char *id;
+
+ for (id = ssh_list_pop_head(char *, session->opts.identity_non_exp);
+ id != NULL;
+ id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
+ SAFE_FREE(id);
+ }
+ ssh_list_free(session->opts.identity_non_exp);
+ }
+
+ if (session->opts.certificate) {
+ char *cert = NULL;
+
+ for (cert = ssh_list_pop_head(char *, session->opts.certificate);
+ cert != NULL;
+ cert = ssh_list_pop_head(char *, session->opts.certificate)) {
+ SAFE_FREE(cert);
+ }
+ ssh_list_free(session->opts.certificate);
+ }
+
+ if (session->opts.certificate_non_exp) {
+ char *cert = NULL;
+
+ for (cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp);
+ cert != NULL;
+ cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) {
+ SAFE_FREE(cert);
+ }
+ ssh_list_free(session->opts.certificate_non_exp);
+ }
+
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
session->out_queue)) != NULL) {
SSH_BUFFER_FREE(b);
}
ssh_list_free(session->out_queue);
-#ifndef _WIN32
- ssh_agent_state_free (session->agent_state);
-#endif
+ ssh_agent_state_free(session->agent_state);
session->agent_state = NULL;
SAFE_FREE(session->auth.auto_state);
SAFE_FREE(session->serverbanner);
SAFE_FREE(session->clientbanner);
SAFE_FREE(session->banner);
+ SAFE_FREE(session->disconnect_message);
+ SAFE_FREE(session->peer_discon_msg);
+ SAFE_FREE(session->opts.agent_socket);
SAFE_FREE(session->opts.bindaddr);
SAFE_FREE(session->opts.custombanner);
SAFE_FREE(session->opts.moduli_file);
@@ -314,6 +350,7 @@ void ssh_free(ssh_session session)
SAFE_FREE(session->opts.gss_server_identity);
SAFE_FREE(session->opts.gss_client_identity);
SAFE_FREE(session->opts.pubkey_accepted_types);
+ SAFE_FREE(session->opts.control_path);
for (i = 0; i < SSH_KEX_METHODS; i++) {
if (session->opts.wanted_methods[i]) {
@@ -459,20 +496,36 @@ const char* ssh_get_hmac_out(ssh_session session) {
}
/**
+ * @internal
+ * @brief Close the connection socket if it is a socket created by us.
+ * Does not close the sockets provided by the user through options API.
+ */
+void
+ssh_session_socket_close(ssh_session session)
+{
+ if (session->opts.fd == SSH_INVALID_SOCKET) {
+ ssh_socket_close(session->socket);
+ }
+ session->alive = 0;
+ session->session_state = SSH_SESSION_STATE_ERROR;
+}
+
+/**
* @brief Disconnect impolitely from a remote host by closing the socket.
*
* Suitable if you forked and want to destroy this session.
*
* @param[in] session The SSH session to disconnect.
*/
-void ssh_silent_disconnect(ssh_session session) {
- if (session == NULL) {
- return;
- }
+void
+ssh_silent_disconnect(ssh_session session)
+{
+ if (session == NULL) {
+ return;
+ }
- ssh_socket_close(session->socket);
- session->alive = 0;
- ssh_disconnect(session);
+ ssh_session_socket_close(session);
+ ssh_disconnect(session);
}
/**
@@ -633,9 +686,10 @@ void ssh_set_fd_except(ssh_session session) {
*
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
-int ssh_handle_packets(ssh_session session, int timeout) {
- ssh_poll_handle spoll;
- ssh_poll_ctx ctx;
+int ssh_handle_packets(ssh_session session, int timeout)
+{
+ ssh_poll_handle spoll = NULL;
+ ssh_poll_ctx ctx = NULL;
int tm = timeout;
int rc;
@@ -644,20 +698,29 @@ int ssh_handle_packets(ssh_session session, int timeout) {
}
spoll = ssh_socket_get_poll_handle(session->socket);
+ if (spoll == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
ssh_poll_add_events(spoll, POLLIN);
ctx = ssh_poll_get_ctx(spoll);
- if (!ctx) {
+ if (ctx == NULL) {
ctx = ssh_poll_get_default_ctx(session);
+ if (ctx == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
ssh_poll_ctx_add(ctx, spoll);
}
if (timeout == SSH_TIMEOUT_USER) {
- if (ssh_is_blocking(session))
- tm = ssh_make_milliseconds(session->opts.timeout,
- session->opts.timeout_usec);
- else
- tm = 0;
+ if (ssh_is_blocking(session)) {
+ tm = ssh_make_milliseconds(session->opts.timeout,
+ session->opts.timeout_usec);
+ } else {
+ tm = 0;
+ }
}
rc = ssh_poll_ctx_dopoll(ctx, tm);
if (rc == SSH_ERROR) {
@@ -680,7 +743,7 @@ int ssh_handle_packets(ssh_session session, int timeout) {
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying
* SSH_TIMEOUT_INFINITE (-1) means an infinite timeout.
- * Specifying SSH_TIMEOUT_USER means to use the timeout
+ * Specifying SSH_TIMEOUT_USER means using the timeout
* specified in options. 0 means poll will return
* immediately.
* SSH_TIMEOUT_DEFAULT uses the session timeout if set or
@@ -694,13 +757,13 @@ int ssh_handle_packets(ssh_session session, int timeout) {
* SSH_ERROR otherwise.
*/
int ssh_handle_packets_termination(ssh_session session,
- long timeout,
+ int timeout,
ssh_termination_function fct,
void *user)
{
struct ssh_timestamp ts;
- long timeout_ms = SSH_TIMEOUT_INFINITE;
- long tm;
+ int timeout_ms = SSH_TIMEOUT_INFINITE;
+ int tm;
int ret = SSH_OK;
/* If a timeout has been provided, use it */
@@ -819,11 +882,11 @@ const char *ssh_get_disconnect_message(ssh_session session) {
if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Connection not closed yet");
- } else if(!session->discon_msg) {
+ } else if(!session->peer_discon_msg) {
ssh_set_error(session, SSH_FATAL,
"Connection correctly closed but no disconnect message");
} else {
- return session->discon_msg;
+ return session->peer_discon_msg;
}
return NULL;
@@ -857,7 +920,9 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){
if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) {
ssh_set_error(session, SSH_FATAL, "Socket error: disconnected");
} else {
- ssh_set_error(session, SSH_FATAL, "Socket error: %s", strerror(errno_code));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(session, SSH_FATAL, "Socket error: %s",
+ ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX));
}
session->ssh_connection_callback(session);
@@ -934,7 +999,7 @@ error:
/**
* @brief Set the session data counters.
*
- * This functions sets the counter structures to be used to calculate data
+ * This function sets the counter structures to be used to calculate data
* which comes in and goes out through the session at different levels.
*
* @code
@@ -977,8 +1042,8 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
{
ssh_key pubkey = NULL;
ssh_string pubkey_blob = NULL;
- MD5CTX ctx;
- unsigned char *h;
+ MD5CTX ctx = NULL;
+ unsigned char *h = NULL;
int rc;
if (session == NULL || hash == NULL) {
@@ -998,40 +1063,50 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
*hash = NULL;
if (session->current_crypto == NULL ||
session->current_crypto->server_pubkey == NULL) {
- ssh_set_error(session,SSH_FATAL,"No current cryptographic context");
+ ssh_set_error(session, SSH_FATAL, "No current cryptographic context");
+ return SSH_ERROR;
+ }
+
+ rc = ssh_get_server_publickey(session, &pubkey);
+ if (rc != SSH_OK) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
+ ssh_key_free(pubkey);
+ if (rc != SSH_OK) {
return SSH_ERROR;
}
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
if (h == NULL) {
+ SSH_STRING_FREE(pubkey_blob);
return SSH_ERROR;
}
ctx = md5_init();
if (ctx == NULL) {
+ SSH_STRING_FREE(pubkey_blob);
SAFE_FREE(h);
return SSH_ERROR;
}
- rc = ssh_get_server_publickey(session, &pubkey);
+ rc = md5_update(ctx,
+ ssh_string_data(pubkey_blob),
+ ssh_string_len(pubkey_blob));
if (rc != SSH_OK) {
- md5_final(h, ctx);
+ SSH_STRING_FREE(pubkey_blob);
+ md5_ctx_free(ctx);
SAFE_FREE(h);
- return SSH_ERROR;
+ return rc;
}
-
- rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
- ssh_key_free(pubkey);
+ SSH_STRING_FREE(pubkey_blob);
+ rc = md5_final(h, ctx);
if (rc != SSH_OK) {
- md5_final(h, ctx);
SAFE_FREE(h);
- return SSH_ERROR;
+ return rc;
}
- md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob));
- SSH_STRING_FREE(pubkey_blob);
- md5_final(h, ctx);
-
*hash = h;
return MD5_DIGEST_LEN;
@@ -1040,14 +1115,15 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
/**
* @brief Deallocate the hash obtained by ssh_get_pubkey_hash.
*
- * This is required under Microsoft platform as this library might use a
+ * This is required under Microsoft platform as this library might use a
* different C library than your software, hence a different heap.
*
* @param[in] hash The buffer to deallocate.
*
* @see ssh_get_pubkey_hash()
*/
-void ssh_clean_pubkey_hash(unsigned char **hash) {
+void ssh_clean_pubkey_hash(unsigned char **hash)
+{
SAFE_FREE(*hash);
}
@@ -1057,9 +1133,9 @@ void ssh_clean_pubkey_hash(unsigned char **hash) {
* @param[in] session The session to get the key from.
*
* @param[out] key A pointer to store the allocated key. You need to free
- * the key.
+ * the key using ssh_key_free().
*
- * @return SSH_OK on success, SSH_ERROR on errror.
+ * @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_key_free()
*/
@@ -1101,15 +1177,15 @@ int ssh_get_publickey(ssh_session session, ssh_key *key)
*
* @param[in] type The type of the hash you want.
*
- * @param[in] hash A pointer to store the allocated buffer. It can be
+ * @param[out] hash A pointer to store the allocated buffer. It can be
* freed using ssh_clean_pubkey_hash().
*
* @param[in] hlen The length of the hash.
*
- * @return 0 on success, -1 if an error occured.
+ * @return 0 on success, -1 if an error occurred.
*
* @warning It is very important that you verify at some moment that the hash
- * matches a known server. If you don't do it, cryptography wont help
+ * matches a known server. If you don't do it, cryptography won't help
* you at making things secure.
* OpenSSH uses SHA256 to print public key digests.
*
@@ -1124,7 +1200,7 @@ int ssh_get_publickey_hash(const ssh_key key,
size_t *hlen)
{
ssh_string blob;
- unsigned char *h;
+ unsigned char *h = NULL;
int rc;
rc = ssh_pki_export_pubkey_blob(key, &blob);
@@ -1150,8 +1226,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- sha1_final(h, ctx);
+ rc = sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ sha1_ctx_free(ctx);
+ goto out;
+ }
+ rc = sha1_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = SHA_DIGEST_LEN;
}
@@ -1173,8 +1258,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- sha256_final(h, ctx);
+ rc = sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ sha256_ctx_free(ctx);
+ goto out;
+ }
+ rc = sha256_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = SHA256_DIGEST_LEN;
}
@@ -1185,8 +1279,8 @@ int ssh_get_publickey_hash(const ssh_key key,
/* In FIPS mode, we cannot use MD5 */
if (ssh_fips_mode()) {
- SSH_LOG(SSH_LOG_WARN, "In FIPS mode MD5 is not allowed."
- "Try using SSH_PUBLICKEY_HASH_SHA256");
+ SSH_LOG(SSH_LOG_TRACE, "In FIPS mode MD5 is not allowed."
+ "Try using SSH_PUBLICKEY_HASH_SHA256");
rc = SSH_ERROR;
goto out;
}
@@ -1204,8 +1298,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- md5_final(h, ctx);
+ rc = md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ md5_ctx_free(ctx);
+ goto out;
+ }
+ rc = md5_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = MD5_DIGEST_LEN;
}
diff --git a/src/sftp.c b/src/sftp.c
index 1709ea68..2bfe04e4 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -56,21 +56,12 @@
#ifdef WITH_SFTP
-/* Buffer size maximum is 256M */
-#define SFTP_PACKET_SIZE_MAX 0x10000000
-
struct sftp_ext_struct {
uint32_t count;
char **name;
char **data;
};
-/* functions */
-static int sftp_enqueue(sftp_session session, sftp_message msg);
-static void sftp_message_free(sftp_message msg);
-static void sftp_set_error(sftp_session sftp, int errnum);
-static void status_msg_free(sftp_status_message status);
-
static sftp_ext sftp_ext_new(void) {
sftp_ext ext;
@@ -266,66 +257,38 @@ error:
return NULL;
}
-int sftp_server_init(sftp_session sftp){
- ssh_session session = sftp->session;
- sftp_packet packet = NULL;
- ssh_buffer reply = NULL;
- uint32_t version;
- int rc;
-
- packet = sftp_packet_read(sftp);
- if (packet == NULL) {
- return -1;
- }
-
- if (packet->type != SSH_FXP_INIT) {
- ssh_set_error(session, SSH_FATAL,
- "Packet read of type %d instead of SSH_FXP_INIT",
- packet->type);
-
- return -1;
- }
-
- SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT");
-
- ssh_buffer_get_u32(packet->payload, &version);
- version = ntohl(version);
- SSH_LOG(SSH_LOG_PACKET, "Client version: %d", version);
- sftp->client_version = (int)version;
-
- reply = ssh_buffer_new();
- if (reply == NULL) {
- ssh_set_error_oom(session);
- return -1;
- }
+/* @deprecated in favor of sftp_server_new() and callbacks based sftp server */
+int sftp_server_init(sftp_session sftp)
+{
+ ssh_session session = sftp->session;
+ sftp_client_message msg = NULL;
+ int rc;
- rc = ssh_buffer_pack(reply, "dssss",
- LIBSFTP_VERSION,
- "posix-rename@openssh.com",
- "1",
- "hardlink@openssh.com",
- "1");
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- SSH_BUFFER_FREE(reply);
- return -1;
- }
+ /* handles setting the sftp->client_version */
+ msg = sftp_get_client_message(sftp);
+ if (msg == NULL) {
+ return -1;
+ }
- if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) {
- SSH_BUFFER_FREE(reply);
- return -1;
- }
- SSH_BUFFER_FREE(reply);
+ if (msg->type != SSH_FXP_INIT) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Packet read of type %d instead of SSH_FXP_INIT",
+ msg->type);
+ return -1;
+ }
- SSH_LOG(SSH_LOG_PROTOCOL, "Server version sent");
+ SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT");
- if (version > LIBSFTP_VERSION) {
- sftp->version = LIBSFTP_VERSION;
- } else {
- sftp->version = (int)version;
- }
+ rc = sftp_reply_version(msg);
+ if (rc != SSH_OK) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to process the SSH_FXP_INIT message");
+ return -1;
+ }
- return 0;
+ return 0;
}
void sftp_server_free(sftp_session sftp)
@@ -353,6 +316,7 @@ void sftp_server_free(sftp_session sftp)
SAFE_FREE(sftp);
}
+
#endif /* WITH_SERVER */
void sftp_free(sftp_session sftp)
@@ -383,169 +347,62 @@ void sftp_free(sftp_session sftp)
SAFE_FREE(sftp->read_packet);
sftp_ext_free(sftp->ext);
+ sftp_limits_free(sftp->limits);
SAFE_FREE(sftp);
}
-ssize_t sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
-{
- uint8_t header[5] = {0};
- uint32_t payload_size;
- ssize_t size;
- int rc;
-
- /* Add size of type */
- payload_size = ssh_buffer_get_len(payload) + sizeof(uint8_t);
- PUSH_BE_U32(header, 0, payload_size);
- PUSH_BE_U8(header, 4, type);
-
- rc = ssh_buffer_prepend_data(payload, header, sizeof(header));
- if (rc < 0) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
-
- size = ssh_channel_write(sftp->channel,
- ssh_buffer_get(payload),
- ssh_buffer_get_len(payload));
- if (size < 0) {
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
-
- if ((uint32_t)size != ssh_buffer_get_len(payload)) {
- SSH_LOG(SSH_LOG_PACKET,
- "Had to write %d bytes, wrote only %zd",
- ssh_buffer_get_len(payload),
- size);
- }
-
- return size;
-}
-
-sftp_packet sftp_packet_read(sftp_session sftp)
+/* @internal
+ * Process the incoming data and copy them from the SSH packet buffer to the
+ * SFTP packet buffer.
+ * @returns number of decoded bytes.
+ */
+int
+sftp_decode_channel_data_to_packet(sftp_session sftp, void *data, uint32_t len)
{
- uint8_t tmpbuf[4];
- uint8_t *buffer = NULL;
sftp_packet packet = sftp->read_packet;
- uint32_t size;
int nread;
- bool is_eof;
- int rc;
-
- packet->sftp = sftp;
+ int payload_len;
+ unsigned int data_offset;
+ int to_read, rc;
- /*
- * If the packet has a payload, then just reinit the buffer, otherwise
- * allocate a new one.
- */
- if (packet->payload != NULL) {
- rc = ssh_buffer_reinit(packet->payload);
- if (rc != 0) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
- } else {
- packet->payload = ssh_buffer_new();
- if (packet->payload == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
+ if (packet->sftp == NULL) {
+ packet->sftp = sftp;
}
- nread = 0;
- do {
- int s;
-
- // read from channel until 4 bytes have been read or an error occurs
- s = ssh_channel_read(sftp->channel, tmpbuf + nread, 4 - nread, 0);
- if (s < 0) {
- goto error;
- } else if (s == 0) {
- is_eof = ssh_channel_is_eof(sftp->channel);
- if (is_eof) {
- ssh_set_error(sftp->session,
- SSH_FATAL,
- "Received EOF while reading sftp packet size");
- sftp_set_error(sftp, SSH_FX_EOF);
- goto error;
- }
- } else {
- nread += s;
- }
- } while (nread < 4);
-
- size = PULL_BE_U32(tmpbuf, 0);
- if (size == 0 || size > SFTP_PACKET_SIZE_MAX) {
- ssh_set_error(sftp->session, SSH_FATAL, "Invalid sftp packet size!");
- sftp_set_error(sftp, SSH_FX_FAILURE);
- goto error;
+ data_offset = sizeof(uint32_t) + sizeof(uint8_t);
+ /* not enough bytes to read */
+ if (len < data_offset) {
+ return SSH_ERROR;
}
- do {
- nread = ssh_channel_read(sftp->channel, tmpbuf, 1, 0);
- if (nread < 0) {
- goto error;
- } else if (nread == 0) {
- is_eof = ssh_channel_is_eof(sftp->channel);
- if (is_eof) {
- ssh_set_error(sftp->session,
- SSH_FATAL,
- "Received EOF while reading sftp packet type");
- sftp_set_error(sftp, SSH_FX_EOF);
- goto error;
- }
- }
- } while (nread < 1);
-
- packet->type = tmpbuf[0];
+ payload_len = PULL_BE_U32(data, 0);
+ packet->type = PULL_BE_U8(data, 4);
- /* Remove the packet type size */
- size -= sizeof(uint8_t);
-
- /* Allocate the receive buffer from payload */
- buffer = ssh_buffer_allocate(packet->payload, size);
- if (buffer == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- goto error;
+ /* We should check the legality of payload length */
+ if (payload_len + sizeof(uint32_t) > len || payload_len < 0) {
+ return SSH_ERROR;
}
- while (size > 0 && size < SFTP_PACKET_SIZE_MAX) {
- nread = ssh_channel_read(sftp->channel, buffer, size, 0);
- if (nread < 0) {
- /* TODO: check if there are cases where an error needs to be set here */
- goto error;
- }
- if (nread > 0) {
- buffer += nread;
- size -= nread;
- } else { /* nread == 0 */
- /* Retry the reading unless the remote was closed */
- is_eof = ssh_channel_is_eof(sftp->channel);
- if (is_eof) {
- ssh_set_error(sftp->session,
- SSH_REQUEST_DENIED,
- "Received EOF while reading sftp packet");
- sftp_set_error(sftp, SSH_FX_EOF);
- goto error;
- }
- }
+ to_read = payload_len - sizeof(uint8_t);
+ rc = ssh_buffer_add_data(packet->payload,
+ (void*)((uint8_t *)data + data_offset),
+ to_read);
+ if (rc != 0) {
+ return SSH_ERROR;
}
+ nread = ssh_buffer_get_len(packet->payload);
- return packet;
-error:
- ssh_buffer_reinit(packet->payload);
- return NULL;
-}
+ /* We should check if we copied the whole data */
+ if (nread != to_read) {
+ return SSH_ERROR;
+ }
-static void sftp_set_error(sftp_session sftp, int errnum) {
- if (sftp != NULL) {
- sftp->errnum = errnum;
- }
+ /*
+ * We should return how many bytes we decoded, including packet length header
+ * and the payload length.
+ */
+ return payload_len + sizeof(uint32_t);
}
/* Get the last sftp error */
@@ -557,199 +414,152 @@ int sftp_get_error(sftp_session sftp) {
return sftp->errnum;
}
-static void sftp_message_free(sftp_message msg)
-{
- if (msg == NULL) {
- return;
- }
-
- SSH_BUFFER_FREE(msg->payload);
- SAFE_FREE(msg);
-}
+static sftp_limits_t sftp_limits_use_extension(sftp_session sftp);
+static sftp_limits_t sftp_limits_use_default(sftp_session sftp);
-static sftp_message sftp_get_message(sftp_packet packet)
+/* Initialize the sftp session with the server. */
+int sftp_init(sftp_session sftp)
{
- sftp_session sftp = packet->sftp;
- sftp_message msg = NULL;
+ sftp_packet packet = NULL;
+ ssh_buffer buffer = NULL;
+ char *ext_name = NULL;
+ char *ext_data = NULL;
+ uint32_t version;
int rc;
- switch(packet->type) {
- case SSH_FXP_STATUS:
- case SSH_FXP_HANDLE:
- case SSH_FXP_DATA:
- case SSH_FXP_ATTRS:
- case SSH_FXP_NAME:
- case SSH_FXP_EXTENDED_REPLY:
- break;
- default:
- ssh_set_error(packet->sftp->session,
- SSH_FATAL,
- "Unknown packet type %d",
- packet->type);
- sftp_set_error(packet->sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- msg = calloc(1, sizeof(struct sftp_message_struct));
- if (msg == NULL) {
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
- sftp_set_error(packet->sftp, SSH_FX_FAILURE);
- return NULL;
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
}
- msg->sftp = packet->sftp;
- msg->packet_type = packet->type;
-
- /* Move the payload from the packet to the message */
- msg->payload = packet->payload;
- packet->payload = NULL;
-
- rc = ssh_buffer_unpack(msg->payload, "d", &msg->id);
+ rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION);
if (rc != SSH_OK) {
- ssh_set_error(packet->sftp->session, SSH_FATAL,
- "Invalid packet %d: no ID", packet->type);
- sftp_message_free(msg);
- sftp_set_error(packet->sftp, SSH_FX_FAILURE);
- return NULL;
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
}
- SSH_LOG(SSH_LOG_PACKET,
- "Packet with id %d type %d",
- msg->id,
- msg->packet_type);
-
- return msg;
-}
+ rc = sftp_packet_write(sftp, SSH_FXP_INIT, buffer);
+ if (rc == SSH_ERROR) {
+ SSH_BUFFER_FREE(buffer);
+ return -1;
+ }
-static int sftp_read_and_dispatch(sftp_session sftp)
-{
- sftp_packet packet = NULL;
- sftp_message msg = NULL;
+ SSH_BUFFER_FREE(buffer);
packet = sftp_packet_read(sftp);
if (packet == NULL) {
- /* something nasty happened reading the packet */
return -1;
}
- msg = sftp_get_message(packet);
- if (msg == NULL) {
+ if (packet->type != SSH_FXP_VERSION) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received a %d messages instead of SSH_FXP_VERSION",
+ packet->type);
return -1;
}
- if (sftp_enqueue(sftp, msg) < 0) {
- sftp_message_free(msg);
+ /* TODO: are we sure there are 4 bytes ready? */
+ rc = ssh_buffer_unpack(packet->payload, "d", &version);
+ if (rc != SSH_OK){
+ sftp_set_error(sftp, SSH_FX_FAILURE);
return -1;
}
- return 0;
-}
+ SSH_LOG(SSH_LOG_DEBUG,
+ "SFTP server version %" PRIu32,
+ version);
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
+ while (rc == SSH_OK) {
+ uint32_t count = sftp->ext->count;
+ char **tmp;
-void sftp_packet_free(sftp_packet packet)
-{
- if (packet == NULL) {
- return;
- }
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_data);
+ if (rc == SSH_ERROR) {
+ break;
+ }
- SSH_BUFFER_FREE(packet->payload);
- free(packet);
-}
+ SSH_LOG(SSH_LOG_DEBUG,
+ "SFTP server extension: %s, version: %s",
+ ext_name, ext_data);
-/* Initialize the sftp session with the server. */
-int sftp_init(sftp_session sftp) {
- sftp_packet packet = NULL;
- ssh_buffer buffer = NULL;
- char *ext_name = NULL;
- char *ext_data = NULL;
- uint32_t version;
- int rc;
+ count++;
+ tmp = realloc(sftp->ext->name, count * sizeof(char *));
+ if (tmp == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(ext_name);
+ SAFE_FREE(ext_data);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- buffer = ssh_buffer_new();
- if (buffer == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
+ tmp[count - 1] = ext_name;
+ sftp->ext->name = tmp;
- rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION);
- if (rc != SSH_OK) {
- ssh_set_error_oom(sftp->session);
- 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);
- return -1;
- }
- SSH_BUFFER_FREE(buffer);
+ tmp = realloc(sftp->ext->data, count * sizeof(char *));
+ if (tmp == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(ext_name);
+ SAFE_FREE(ext_data);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- packet = sftp_packet_read(sftp);
- if (packet == NULL) {
- return -1;
- }
+ tmp[count - 1] = ext_data;
+ sftp->ext->data = tmp;
- if (packet->type != SSH_FXP_VERSION) {
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received a %d messages instead of SSH_FXP_VERSION", packet->type);
- return -1;
- }
+ sftp->ext->count = count;
- /* TODO: are we sure there are 4 bytes ready? */
- rc = ssh_buffer_unpack(packet->payload, "d", &version);
- if (rc != SSH_OK){
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SFTP server version %d",
- version);
- rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
- while (rc == SSH_OK) {
- uint32_t count = sftp->ext->count;
- char **tmp;
-
- rc = ssh_buffer_unpack(packet->payload, "s", &ext_data);
- if (rc == SSH_ERROR) {
- break;
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SFTP server extension: %s, version: %s",
- ext_name, ext_data);
+ sftp->version = sftp->server_version = (int)version;
- count++;
- tmp = realloc(sftp->ext->name, count * sizeof(char *));
- if (tmp == NULL) {
- ssh_set_error_oom(sftp->session);
- SAFE_FREE(ext_name);
- SAFE_FREE(ext_data);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
- tmp[count - 1] = ext_name;
- sftp->ext->name = tmp;
+ /* Set the limits */
+ rc = sftp_extension_supported(sftp, "limits@openssh.com", "1");
+ if (rc == 1) {
+ /* Get the ssh and sftp errors */
+ const char *static_ssh_err_msg = ssh_get_error(sftp->session);
+ int ssh_err_code = ssh_get_error_code(sftp->session);
+ int sftp_err_code = sftp_get_error(sftp);
+ char *ssh_err_msg = strdup(static_ssh_err_msg);
+ if (ssh_err_msg == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- tmp = realloc(sftp->ext->data, count * sizeof(char *));
- if (tmp == NULL) {
- ssh_set_error_oom(sftp->session);
- SAFE_FREE(ext_name);
- SAFE_FREE(ext_data);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
+ sftp->limits = sftp_limits_use_extension(sftp);
+ if (sftp->limits == NULL) {
+ /* fallback and use the default limits on failure */
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to get the limits from a server claiming to "
+ "support the limits@openssh.com extension, falling back "
+ "and using the default limits");
+
+ /* Restore the sftp and ssh errors to their previous state */
+ ssh_set_error(sftp->session, ssh_err_code, "%s", ssh_err_msg);
+ sftp_set_error(sftp, sftp_err_code);
+ SAFE_FREE(ssh_err_msg);
+
+ sftp->limits = sftp_limits_use_default(sftp);
+ if (sftp->limits == NULL) {
+ return -1;
+ }
+ } else {
+ SAFE_FREE(ssh_err_msg);
+ }
+ } else {
+ sftp->limits = sftp_limits_use_default(sftp);
+ if (sftp->limits == NULL) {
+ return -1;
+ }
}
- tmp[count - 1] = ext_data;
- sftp->ext->data = tmp;
-
- sftp->ext->count = count;
-
- rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
- }
-
- sftp->version = sftp->server_version = (int)version;
-
- return 0;
+ return 0;
}
unsigned int sftp_extensions_get_count(sftp_session sftp) {
@@ -815,166 +625,6 @@ int sftp_extension_supported(sftp_session sftp, const char *name,
return 0;
}
-static sftp_request_queue request_queue_new(sftp_message msg) {
- sftp_request_queue queue = NULL;
-
- queue = calloc(1, sizeof(struct sftp_request_queue_struct));
- if (queue == NULL) {
- ssh_set_error_oom(msg->sftp->session);
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- queue->message = msg;
-
- return queue;
-}
-
-static void request_queue_free(sftp_request_queue queue) {
- if (queue == NULL) {
- return;
- }
-
- ZERO_STRUCTP(queue);
- SAFE_FREE(queue);
-}
-
-static int sftp_enqueue(sftp_session sftp, sftp_message msg) {
- sftp_request_queue queue = NULL;
- sftp_request_queue ptr;
-
- queue = request_queue_new(msg);
- if (queue == NULL) {
- return -1;
- }
-
- SSH_LOG(SSH_LOG_PACKET,
- "Queued msg id %d type %d",
- msg->id, msg->packet_type);
-
- if(sftp->queue == NULL) {
- sftp->queue = queue;
- } else {
- ptr = sftp->queue;
- while(ptr->next) {
- ptr=ptr->next; /* find end of linked list */
- }
- ptr->next = queue; /* add it on bottom */
- }
-
- return 0;
-}
-
-/*
- * Pulls of a message from the queue based on the ID.
- * Returns NULL if no message has been found.
- */
-static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){
- sftp_request_queue prev = NULL;
- sftp_request_queue queue;
- sftp_message msg;
-
- if(sftp->queue == NULL) {
- return NULL;
- }
-
- queue = sftp->queue;
- while (queue) {
- if(queue->message->id == id) {
- /* remove from queue */
- if (prev == NULL) {
- sftp->queue = queue->next;
- } else {
- prev->next = queue->next;
- }
- msg = queue->message;
- request_queue_free(queue);
- SSH_LOG(SSH_LOG_PACKET,
- "Dequeued msg id %d type %d",
- msg->id,
- msg->packet_type);
- return msg;
- }
- prev = queue;
- queue = queue->next;
- }
-
- return NULL;
-}
-
-/*
- * Assigns a new SFTP ID for new requests and assures there is no collision
- * between them.
- * Returns a new ID ready to use in a request
- */
-static inline uint32_t sftp_get_new_id(sftp_session session) {
- return ++session->id_counter;
-}
-
-static sftp_status_message parse_status_msg(sftp_message msg){
- sftp_status_message status;
- int rc;
-
- if (msg->packet_type != SSH_FXP_STATUS) {
- ssh_set_error(msg->sftp->session, SSH_FATAL,
- "Not a ssh_fxp_status message passed in!");
- sftp_set_error(msg->sftp, SSH_FX_BAD_MESSAGE);
- return NULL;
- }
-
- status = calloc(1, sizeof(struct sftp_status_message_struct));
- if (status == NULL) {
- ssh_set_error_oom(msg->sftp->session);
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- status->id = msg->id;
- rc = ssh_buffer_unpack(msg->payload, "d",
- &status->status);
- if (rc != SSH_OK){
- SAFE_FREE(status);
- ssh_set_error(msg->sftp->session, SSH_FATAL,
- "Invalid SSH_FXP_STATUS message");
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
- rc = ssh_buffer_unpack(msg->payload, "ss",
- &status->errormsg,
- &status->langmsg);
-
- if(rc != SSH_OK && msg->sftp->version >=3){
- /* These are mandatory from version 3 */
- SAFE_FREE(status);
- ssh_set_error(msg->sftp->session, SSH_FATAL,
- "Invalid SSH_FXP_STATUS message");
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
- if (status->errormsg == NULL)
- status->errormsg = strdup("No error message in packet");
- if (status->langmsg == NULL)
- status->langmsg = strdup("");
- if (status->errormsg == NULL || status->langmsg == NULL) {
- ssh_set_error_oom(msg->sftp->session);
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- status_msg_free(status);
- return NULL;
- }
-
- return status;
-}
-
-static void status_msg_free(sftp_status_message status){
- if (status == NULL) {
- return;
- }
-
- SAFE_FREE(status->errormsg);
- SAFE_FREE(status->langmsg);
- SAFE_FREE(status);
-}
-
static sftp_file parse_handle_msg(sftp_message msg){
sftp_file file;
@@ -1014,7 +664,7 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
sftp_file file = NULL;
sftp_dir dir = NULL;
sftp_status_message status;
- ssh_buffer payload;
+ ssh_buffer payload = NULL;
uint32_t id;
int rc;
@@ -1065,7 +715,7 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
}
sftp_set_error(sftp, status->status);
ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
- "SFTP server: %s", status->errormsg);
+ "SFTP server: %s", status->errormsg);
status_msg_free(status);
return NULL;
case SSH_FXP_HANDLE:
@@ -1092,452 +742,14 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
return dir;
default:
ssh_set_error(sftp->session, SSH_FATAL,
- "Received message %d during opendir!", msg->packet_type);
+ "Received message %d during opendir!",
+ msg->packet_type);
sftp_message_free(msg);
}
return NULL;
}
-/*
- * Parse the attributes from a payload from some messages. It is coded on
- * baselines from the protocol version 4.
- * This code is more or less dead but maybe we need it in future.
- */
-static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf,
- int expectnames) {
- sftp_attributes attr;
- ssh_string owner = NULL;
- ssh_string group = NULL;
- uint32_t flags = 0;
- int ok = 0;
-
- /* unused member variable */
- (void) expectnames;
-
- attr = calloc(1, sizeof(struct sftp_attributes_struct));
- if (attr == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- /* This isn't really a loop, but it is like a try..catch.. */
- do {
- if (ssh_buffer_get_u32(buf, &flags) != 4) {
- break;
- }
-
- flags = ntohl(flags);
- attr->flags = flags;
-
- if (flags & SSH_FILEXFER_ATTR_SIZE) {
- if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
- break;
- }
- attr->size = ntohll(attr->size);
- }
-
- if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
- owner = ssh_buffer_get_ssh_string(buf);
- if (owner == NULL) {
- break;
- }
- attr->owner = ssh_string_to_char(owner);
- SSH_STRING_FREE(owner);
- if (attr->owner == NULL) {
- break;
- }
-
- group = ssh_buffer_get_ssh_string(buf);
- if (group == NULL) {
- break;
- }
- attr->group = ssh_string_to_char(group);
- SSH_STRING_FREE(group);
- if (attr->group == NULL) {
- break;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
- break;
- }
- attr->permissions = ntohl(attr->permissions);
-
- /* FIXME on windows! */
- switch (attr->permissions & SSH_S_IFMT) {
- case SSH_S_IFSOCK:
- case SSH_S_IFBLK:
- case SSH_S_IFCHR:
- case SSH_S_IFIFO:
- attr->type = SSH_FILEXFER_TYPE_SPECIAL;
- break;
- case SSH_S_IFLNK:
- attr->type = SSH_FILEXFER_TYPE_SYMLINK;
- break;
- case SSH_S_IFREG:
- attr->type = SSH_FILEXFER_TYPE_REGULAR;
- break;
- case SSH_S_IFDIR:
- attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
- break;
- default:
- attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
- break;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
- if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
- break;
- }
- attr->atime64 = ntohll(attr->atime64);
-
- if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
- if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
- break;
- }
- attr->atime_nseconds = ntohl(attr->atime_nseconds);
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
- if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
- break;
- }
- attr->createtime = ntohll(attr->createtime);
-
- if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
- if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
- break;
- }
- attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
- if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
- break;
- }
- attr->mtime64 = ntohll(attr->mtime64);
-
- if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
- if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
- break;
- }
- attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_ACL) {
- if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
- break;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
- if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
- break;
- }
- attr->extended_count = ntohl(attr->extended_count);
-
- while(attr->extended_count &&
- (attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
- (attr->extended_data = ssh_buffer_get_ssh_string(buf))){
- attr->extended_count--;
- }
-
- if (attr->extended_count) {
- break;
- }
- }
- ok = 1;
- } while (0);
-
- if (ok == 0) {
- /* break issued somewhere */
- 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);
-
- ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
-
- return NULL;
- }
-
- return attr;
-}
-
-enum sftp_longname_field_e {
- SFTP_LONGNAME_PERM = 0,
- SFTP_LONGNAME_FIXME,
- SFTP_LONGNAME_OWNER,
- SFTP_LONGNAME_GROUP,
- SFTP_LONGNAME_SIZE,
- SFTP_LONGNAME_DATE,
- SFTP_LONGNAME_TIME,
- SFTP_LONGNAME_NAME,
-};
-
-static char *sftp_parse_longname(const char *longname,
- enum sftp_longname_field_e longname_field) {
- const char *p, *q;
- size_t len, field = 0;
-
- p = longname;
- /* Find the beginning of the field which is specified by sftp_longname_field_e. */
- while(field != longname_field) {
- if(isspace(*p)) {
- field++;
- p++;
- while(*p && isspace(*p)) {
- p++;
- }
- } else {
- p++;
- }
- }
-
- q = p;
- while (! isspace(*q)) {
- q++;
- }
-
- len = q - p;
-
- return strndup(p, len);
-}
-
-/* sftp version 0-3 code. It is different from the v4 */
-/* maybe a paste of the draft is better than the code */
-/*
- uint32 flags
- uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
- uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
- uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
- uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
- uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
- uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
- uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
- string extended_type
- string extended_data
- ... more extended data (extended_type - extended_data pairs),
- so that number of pairs equals extended_count */
-static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf,
- int expectname) {
- sftp_attributes attr;
- int rc;
-
- attr = calloc(1, sizeof(struct sftp_attributes_struct));
- if (attr == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- if (expectname) {
- rc = ssh_buffer_unpack(buf, "ss",
- &attr->name,
- &attr->longname);
- if (rc != SSH_OK){
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL, "Name: %s", attr->name);
-
- /* Set owner and group if we talk to openssh and have the longname */
- if (ssh_get_openssh_version(sftp->session)) {
- attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER);
- if (attr->owner == NULL) {
- goto error;
- }
-
- attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP);
- if (attr->group == NULL) {
- goto error;
- }
- }
- }
-
- rc = ssh_buffer_unpack(buf, "d", &attr->flags);
- if (rc != SSH_OK){
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Flags: %.8"PRIx32"\n", (uint32_t) attr->flags);
-
- if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
- rc = ssh_buffer_unpack(buf, "q", &attr->size);
- if(rc != SSH_OK) {
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Size: %"PRIu64"\n",
- (uint64_t) attr->size);
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
- rc = ssh_buffer_unpack(buf, "dd",
- &attr->uid,
- &attr->gid);
- if (rc != SSH_OK){
- goto error;
- }
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
- if (rc != SSH_OK){
- goto error;
- }
-
- switch (attr->permissions & SSH_S_IFMT) {
- case SSH_S_IFSOCK:
- case SSH_S_IFBLK:
- case SSH_S_IFCHR:
- case SSH_S_IFIFO:
- attr->type = SSH_FILEXFER_TYPE_SPECIAL;
- break;
- case SSH_S_IFLNK:
- attr->type = SSH_FILEXFER_TYPE_SYMLINK;
- break;
- case SSH_S_IFREG:
- attr->type = SSH_FILEXFER_TYPE_REGULAR;
- break;
- case SSH_S_IFDIR:
- attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
- break;
- default:
- attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
- break;
- }
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
- rc = ssh_buffer_unpack(buf, "dd",
- &attr->atime,
- &attr->mtime);
- if (rc != SSH_OK){
- goto error;
- }
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
- rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
- if (rc != SSH_OK){
- goto error;
- }
-
- if (attr->extended_count > 0){
- rc = ssh_buffer_unpack(buf, "ss",
- &attr->extended_type,
- &attr->extended_data);
- if (rc != SSH_OK){
- goto error;
- }
- attr->extended_count--;
- }
- /* just ignore the remaining extensions */
-
- while (attr->extended_count > 0){
- ssh_string tmp1,tmp2;
- rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
- if (rc != SSH_OK){
- goto error;
- }
- SAFE_FREE(tmp1);
- SAFE_FREE(tmp2);
- attr->extended_count--;
- }
- }
-
- return attr;
-
- error:
- SSH_STRING_FREE(attr->extended_type);
- SSH_STRING_FREE(attr->extended_data);
- SAFE_FREE(attr->name);
- SAFE_FREE(attr->longname);
- SAFE_FREE(attr->owner);
- SAFE_FREE(attr->group);
- SAFE_FREE(attr);
- ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
- sftp_set_error(sftp, SSH_FX_FAILURE);
-
- return NULL;
-}
-
-int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr)
-{
- uint32_t flags = (attr ? attr->flags : 0);
- int rc;
-
- flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
- SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
-
- rc = ssh_buffer_pack(buffer, "d", flags);
- if (rc != SSH_OK) {
- return -1;
- }
-
- if (attr != NULL) {
- if (flags & SSH_FILEXFER_ATTR_SIZE) {
- rc = ssh_buffer_pack(buffer, "q", attr->size);
- if (rc != SSH_OK) {
- return -1;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_UIDGID) {
- rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
- if (rc != SSH_OK) {
- return -1;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- rc = ssh_buffer_pack(buffer, "d", attr->permissions);
- if (rc != SSH_OK) {
- return -1;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
- rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
- if (rc != SSH_OK) {
- return -1;
- }
- }
- }
- return 0;
-}
-
-
-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);
- case 3:
- case 2:
- case 1:
- case 0:
- return sftp_parse_attr_3(session, buf, expectname);
- default:
- ssh_set_error(session->session, SSH_FATAL,
- "Version %d unsupported by client", session->server_version);
- return NULL;
- }
-
- return NULL;
-}
-
/* Get the version of the SFTP protocol supported by the server */
int sftp_server_version(sftp_session sftp) {
return sftp->server_version;
@@ -1581,7 +793,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
}
SSH_LOG(SSH_LOG_PACKET,
- "Sent a ssh_fxp_readdir with id %d", id);
+ "Sent a ssh_fxp_readdir with id %" PRIu32, id);
while (msg == NULL) {
if (sftp_read_and_dispatch(sftp) < 0) {
@@ -1609,7 +821,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
}
ssh_set_error(sftp->session, SSH_FATAL,
- "Unknown error status: %d", status->status);
+ "Unknown error status: %" PRIu32, status->status);
status_msg_free(status);
return NULL;
@@ -1638,7 +850,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
return NULL;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Count is %d", dir->count);
+ SSH_LOG(SSH_LOG_DEBUG, "Count is %" PRIu32, dir->count);
attr = sftp_parse_attr(sftp, dir->buffer, 1);
if (attr == NULL) {
@@ -1755,6 +967,10 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle)
int sftp_close(sftp_file file){
int err = SSH_NO_ERROR;
+ if (file == NULL) {
+ return err;
+ }
+
SAFE_FREE(file->name);
if (file->handle){
err = sftp_handle_close(file->sftp,file->handle);
@@ -1792,7 +1008,7 @@ sftp_file sftp_open(sftp_session sftp,
sftp_status_message status;
struct sftp_attributes_struct attr;
sftp_file handle;
- ssh_buffer buffer;
+ ssh_buffer buffer = NULL;
sftp_attributes stat_data;
uint32_t sftp_flags = 0;
uint32_t id;
@@ -1824,7 +1040,8 @@ sftp_file sftp_open(sftp_session sftp,
if ((flags & O_APPEND) == O_APPEND) {
sftp_flags |= SSH_FXF_APPEND;
}
- SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags);
+ SSH_LOG(SSH_LOG_PACKET, "Opening file %s with sftp flags %" PRIx32,
+ file, sftp_flags);
id = sftp_get_new_id(sftp);
rc = ssh_buffer_pack(buffer,
@@ -1862,62 +1079,66 @@ sftp_file sftp_open(sftp_session sftp,
}
switch (msg->packet_type) {
- case SSH_FXP_STATUS:
- status = parse_status_msg(msg);
- sftp_message_free(msg);
- if (status == NULL) {
- return NULL;
- }
- sftp_set_error(sftp, status->status);
- ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
- "SFTP server: %s", status->errormsg);
- status_msg_free(status);
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ handle = parse_handle_msg(msg);
+ if (handle == NULL) {
return NULL;
- case SSH_FXP_HANDLE:
- handle = parse_handle_msg(msg);
- if (handle == NULL) {
+ }
+ sftp_message_free(msg);
+ if ((flags & O_APPEND) == O_APPEND) {
+ stat_data = sftp_stat(sftp, file);
+ if (stat_data == NULL) {
+ sftp_close(handle);
return NULL;
}
- sftp_message_free(msg);
- if ((flags & O_APPEND) == O_APPEND) {
- stat_data = sftp_stat(sftp, file);
- if (stat_data == NULL) {
- sftp_close(handle);
- return NULL;
- }
- if ((stat_data->flags & SSH_FILEXFER_ATTR_SIZE) != SSH_FILEXFER_ATTR_SIZE) {
- ssh_set_error(sftp->session,
- SSH_FATAL,
- "Cannot open in append mode. Unknown file size.");
- sftp_close(handle);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- handle->offset = stat_data->size;
+ if ((stat_data->flags & SSH_FILEXFER_ATTR_SIZE) != SSH_FILEXFER_ATTR_SIZE) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Cannot open in append mode. Unknown file size.");
+ sftp_attributes_free(stat_data);
+ sftp_close(handle);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
}
- return handle;
- default:
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received message %d during open!", msg->packet_type);
- sftp_message_free(msg);
- sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+
+ handle->offset = stat_data->size;
+ sftp_attributes_free(stat_data);
+ }
+ return handle;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during open!", msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
}
return NULL;
}
-void sftp_file_set_nonblocking(sftp_file handle){
- handle->nonblocking=1;
+void sftp_file_set_nonblocking(sftp_file handle)
+{
+ handle->nonblocking = 1;
}
-void sftp_file_set_blocking(sftp_file handle){
- handle->nonblocking=0;
+void sftp_file_set_blocking(sftp_file handle)
+{
+ handle->nonblocking = 0;
}
/* Read from a file using an opened sftp file handle. */
ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
- sftp_session sftp = handle->sftp;
+ sftp_session sftp;
sftp_message msg = NULL;
sftp_status_message status;
ssh_string datastring;
@@ -1926,10 +1147,27 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
uint32_t id;
int rc;
+ if (handle == NULL) {
+ return -1;
+ }
+ sftp = handle->sftp;
+
if (handle->eof) {
return 0;
}
+ /*
+ * limit the reads to the maximum specified in Section 3 of
+ * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
+ * or to the values provided by the limits@openssh.com extension.
+ *
+ * TODO: We should iterate over the blocks rather than writing less than
+ * requested to provide less surprises to the calling applications.
+ */
+ if (count > sftp->limits->max_read_length) {
+ count = sftp->limits->max_read_length;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2125,7 +1363,7 @@ int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){
if (ssh_string_len(datastring) > size) {
ssh_set_error(sftp->session, SSH_FATAL,
"Received a too big DATA packet from sftp server: "
- "%zu and asked for %u",
+ "%zu and asked for %" PRIu32,
ssh_string_len(datastring), size);
SSH_STRING_FREE(datastring);
return SSH_ERROR;
@@ -2147,7 +1385,7 @@ int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){
}
ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
- sftp_session sftp = file->sftp;
+ sftp_session sftp;
sftp_message msg = NULL;
sftp_status_message status;
ssh_buffer buffer;
@@ -2156,6 +1394,11 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
size_t packetlen;
int rc;
+ if (file == NULL) {
+ return -1;
+ }
+ sftp = file->sftp;
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2165,6 +1408,18 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
id = sftp_get_new_id(file->sftp);
+ /*
+ * limit the writes to the maximum specified in Section 3 of
+ * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
+ * or to the values provided by the limits@openssh.com extension.
+ *
+ * TODO: We should iterate over the blocks rather than writing less than
+ * requested to provide less surprises to the calling applications.
+ */
+ if (count > sftp->limits->max_write_length) {
+ count = sftp->limits->max_write_length;
+ }
+
rc = ssh_buffer_pack(buffer,
"dSqdP",
id,
@@ -2178,8 +1433,8 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
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);
+ packetlen=ssh_buffer_get_len(buffer);
SSH_BUFFER_FREE(buffer);
if (len < 0) {
return -1;
@@ -2514,85 +1769,115 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode)
}
/* code written by nick */
-int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
- sftp_status_message status = NULL;
- sftp_message msg = NULL;
- ssh_buffer buffer;
- uint32_t id;
- int rc;
+int sftp_rename(sftp_session sftp, const char *original, const char *newname)
+{
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ const char *extension_name = "posix-rename@openssh.com";
+ int request_type;
+ int rc;
- buffer = ssh_buffer_new();
- if (buffer == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- id = sftp_get_new_id(sftp);
+ id = sftp_get_new_id(sftp);
- rc = ssh_buffer_pack(buffer,
- "dss",
- id,
- original,
- newname);
- if (rc != SSH_OK) {
- ssh_set_error_oom(sftp->session);
- SSH_BUFFER_FREE(buffer);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
+ /*
+ * posix-rename@openssh.com extension will be used
+ * if it is supported by sftp
+ */
+ if (sftp_extension_supported(sftp,
+ extension_name,
+ "1")) {
+ rc = ssh_buffer_pack(buffer,
+ "dsss",
+ id,
+ extension_name,
+ original,
+ newname);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- if (sftp->version >= 4){
- /* POSIX rename atomically replaces newpath, we should do the same
- * only available on >=v4 */
- ssh_buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE);
- }
+ request_type = SSH_FXP_EXTENDED;
+ } else {
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ original,
+ newname);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) {
- SSH_BUFFER_FREE(buffer);
- return -1;
- }
- SSH_BUFFER_FREE(buffer);
+ if (sftp->version >= 4) {
+ /*
+ * POSIX rename atomically replaces newpath,
+ * we should do the same only available on >=v4
+ */
+ ssh_buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE);
+ }
- while (msg == NULL) {
- if (sftp_read_and_dispatch(sftp) < 0) {
- return -1;
+ request_type = SSH_FXP_RENAME;
}
- msg = sftp_dequeue(sftp, id);
- }
- /* By specification, this command only returns SSH_FXP_STATUS */
- if (msg->packet_type == SSH_FXP_STATUS) {
- status = parse_status_msg(msg);
- sftp_message_free(msg);
- if (status == NULL) {
- return -1;
+ rc = sftp_packet_write(sftp, request_type, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc < 0) {
+ return -1;
}
- sftp_set_error(sftp, status->status);
- switch (status->status) {
- case SSH_FX_OK:
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * Status should be SSH_FX_OK if the command was successful,
+ * if it didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
status_msg_free(status);
- return 0;
- default:
- break;
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to rename",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
}
- /*
- * Status should be SSH_FX_OK if the command was successful, if it didn't,
- * then there was an error
- */
- ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
- "SFTP server: %s", status->errormsg);
- status_msg_free(status);
- return -1;
- } else {
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received message %d when attempting to rename",
- msg->packet_type);
- sftp_message_free(msg);
- sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
- }
- return -1;
+ return -1;
}
/* Code written by Nick */
@@ -2720,7 +2005,8 @@ int sftp_utimes(sftp_session sftp, const char *file,
return sftp_setstat(sftp, file, &attr);
}
-int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
+int sftp_symlink(sftp_session sftp, const char *target, const char *dest)
+{
sftp_status_message status = NULL;
sftp_message msg = NULL;
ssh_buffer buffer;
@@ -2744,7 +2030,10 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
id = sftp_get_new_id(sftp);
- /* TODO check for version number if they ever fix it. */
+ /* The OpenSSH sftp server has order of the arguments reversed, see the
+ * section "4.1 sftp: Reversal of arguments to SSH_FXP_SYMLINK' in
+ * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
+ * for more information */
if (ssh_get_openssh_version(sftp->session)) {
rc = ssh_buffer_pack(buffer,
"dss",
@@ -2904,6 +2193,94 @@ char *sftp_readlink(sftp_session sftp, const char *path)
return NULL;
}
+int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath)
+{
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ const char *extension_name = "hardlink@openssh.com";
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ int rc;
+
+ if (sftp == NULL) {
+ return -1;
+ }
+
+ if (oldpath == NULL || newpath == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dsss",
+ id,
+ extension_name,
+ oldpath,
+ newpath);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc < 0) {
+ return -1;
+ }
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * Status should be SSH_FX_OK if the command was successful,
+ * if it didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to create hardlink",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ }
+
+ return -1;
+}
+
static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) {
sftp_statvfs_t statvfs;
int rc;
@@ -3204,6 +2581,183 @@ void sftp_statvfs_free(sftp_statvfs_t statvfs) {
SAFE_FREE(statvfs);
}
+static sftp_limits_t sftp_limits_new(void)
+{
+ return calloc(1, sizeof(struct sftp_limits_struct));
+}
+
+static sftp_limits_t sftp_parse_limits(sftp_session sftp, ssh_buffer buf)
+{
+ sftp_limits_t limits = NULL;
+ int rc;
+
+ limits = sftp_limits_new();
+ if (limits == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ rc = ssh_buffer_unpack(buf, "qqqq",
+ &limits->max_packet_length, /** maximum number of bytes in a single sftp packet */
+ &limits->max_read_length, /** maximum length in a SSH_FXP_READ packet */
+ &limits->max_write_length, /** maximum length in a SSH_FXP_WRITE packet */
+ &limits->max_open_handles /** maximum number of active handles allowed by server */
+ );
+ if (rc != SSH_OK) {
+ SAFE_FREE(limits);
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid limits structure");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ return limits;
+}
+
+static sftp_limits_t sftp_limits_use_extension(sftp_session sftp)
+{
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL)
+ return NULL;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "ds",
+ id,
+ "limits@openssh.com");
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ 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);
+ if (rc < 0) {
+ return NULL;
+ }
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
+ sftp_limits_t limits = sftp_parse_limits(sftp, msg->payload);
+ sftp_message_free(msg);
+ if (limits == NULL) {
+ return NULL;
+ }
+
+ return limits;
+ } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session,
+ SSH_REQUEST_DENIED,
+ "SFTP server: %s",
+ status->errormsg);
+ status_msg_free(status);
+ } else { /* this shouldn't happen */
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received message %d when attempting to get limits",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ }
+
+ return NULL;
+}
+
+static sftp_limits_t sftp_limits_use_default(sftp_session sftp)
+{
+ sftp_limits_t limits = NULL;
+
+ if (sftp == NULL) {
+ return NULL;
+ }
+
+ limits = sftp_limits_new();
+ if (limits == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ limits->max_packet_length = 34000;
+ limits->max_read_length = 32768;
+ limits->max_write_length = 32768;
+
+ /*
+ * For max-open-handles field openssh says :
+ * If the server doesn't enforce a specific limit, then the field may
+ * be set to 0. This implies the server relies on the OS to enforce
+ * limits (e.g. available memory or file handles), and such limits
+ * might be dynamic. The client SHOULD take care to not try to exceed
+ * reasonable limits.
+ */
+ limits->max_open_handles = 0;
+
+ return limits;
+}
+
+sftp_limits_t sftp_limits(sftp_session sftp)
+{
+ sftp_limits_t limits = NULL;
+
+ if (sftp == NULL) {
+ return NULL;
+ }
+
+ if (sftp->limits == NULL) {
+ ssh_set_error(sftp, SSH_FATAL,
+ "Uninitialized sftp session, "
+ "sftp_init() was not called or failed");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ limits = sftp_limits_new();
+ if (limits == NULL) {
+ ssh_set_error_oom(sftp);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ memcpy(limits, sftp->limits, sizeof(struct sftp_limits_struct));
+ return limits;
+}
+
+void sftp_limits_free(sftp_limits_t limits)
+{
+ if (limits == NULL) {
+ return;
+ }
+
+ SAFE_FREE(limits);
+}
+
/* another code written by Nick */
char *sftp_canonicalize_path(sftp_session sftp, const char *path)
{
@@ -3449,4 +3003,98 @@ sftp_attributes sftp_fstat(sftp_file file)
return NULL;
}
+char *sftp_expand_path(sftp_session sftp, const char *path)
+{
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL) {
+ return NULL;
+ }
+
+ if (path == NULL) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "NULL received as an argument instead of the path to expand");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ "expand-path@openssh.com",
+ path);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ 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);
+ if (rc < 0) {
+ return NULL;
+ }
+
+ while (msg == NULL) {
+ rc = sftp_read_and_dispatch(sftp);
+ if (rc < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_NAME) {
+ uint32_t ignored = 0;
+ char *cname = NULL;
+
+ rc = ssh_buffer_unpack(msg->payload,
+ "ds",
+ &ignored,
+ &cname);
+ sftp_message_free(msg);
+ if (rc != SSH_OK) {
+ ssh_set_error(sftp->session,
+ SSH_ERROR,
+ "Failed to parse expanded path");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ return cname;
+ } else if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to expand path",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ }
+
+ return NULL;
+}
+
#endif /* WITH_SFTP */
diff --git a/src/sftp_aio.c b/src/sftp_aio.c
new file mode 100644
index 00000000..c1c54561
--- /dev/null
+++ b/src/sftp_aio.c
@@ -0,0 +1,500 @@
+/*
+ * sftp_aio.c - Secure FTP functions for asynchronous i/o
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2005-2008 by Aris Adamantiadis
+ * Copyright (c) 2008-2018 by Andreas Schneider <asn@cryptomilk.org>
+ * Copyright (c) 2023 by Eshan Kelkar <eshankelkar@galorithm.com>
+ *
+ * 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/sftp.h"
+#include "libssh/sftp_priv.h"
+#include "libssh/buffer.h"
+#include "libssh/session.h"
+
+#ifdef WITH_SFTP
+
+struct sftp_aio_struct {
+ sftp_file file;
+ uint32_t id;
+ size_t len;
+};
+
+static sftp_aio sftp_aio_new(void)
+{
+ sftp_aio aio = NULL;
+ aio = calloc(1, sizeof(struct sftp_aio_struct));
+ return aio;
+}
+
+void sftp_aio_free(sftp_aio aio)
+{
+ SAFE_FREE(aio);
+}
+
+ssize_t sftp_aio_begin_read(sftp_file file, size_t len, sftp_aio *aio)
+{
+ sftp_session sftp = NULL;
+ ssh_buffer buffer = NULL;
+ sftp_aio aio_handle = NULL;
+ uint32_t id;
+ int rc;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (len == 0) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, 0 passed as the number of "
+ "bytes to read");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ /* Apply a cap on the length a user is allowed to read */
+ if (len > sftp->limits->max_read_length) {
+ len = sftp->limits->max_read_length;
+ }
+
+ if (aio == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed instead of a pointer to "
+ "a location to store an sftp aio handle");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dSqd",
+ id,
+ file->handle,
+ file->offset,
+ len);
+
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle = sftp_aio_new();
+ if (aio_handle == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle->file = file;
+ aio_handle->id = id;
+ aio_handle->len = len;
+
+ rc = sftp_packet_write(sftp, SSH_FXP_READ, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc == SSH_ERROR) {
+ SFTP_AIO_FREE(aio_handle);
+ return SSH_ERROR;
+ }
+
+ /* Assume we read len bytes from the file */
+ file->offset += len;
+ *aio = aio_handle;
+ return len;
+}
+
+ssize_t sftp_aio_wait_read(sftp_aio *aio,
+ void *buf,
+ size_t buf_size)
+{
+ sftp_file file = NULL;
+ size_t bytes_requested;
+ sftp_session sftp = NULL;
+ sftp_message msg = NULL;
+ sftp_status_message status = NULL;
+ uint32_t string_len, host_len;
+ int rc, err;
+
+ /*
+ * This function releases the memory of the structure
+ * that (*aio) points to in all cases except when the
+ * return value is SSH_AGAIN.
+ *
+ * If the return value is SSH_AGAIN, the user should call this
+ * function again to get the response for the request corresponding
+ * to the structure that (*aio) points to, hence we don't release the
+ * structure's memory when SSH_AGAIN is returned.
+ */
+
+ if (aio == NULL || *aio == NULL) {
+ return SSH_ERROR;
+ }
+
+ file = (*aio)->file;
+ bytes_requested = (*aio)->len;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (bytes_requested == 0) {
+ /* should never happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid sftp aio, len for requested i/o is 0");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ if (buf == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed "
+ "instead of a buffer's address");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ if (buf_size < bytes_requested) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Buffer size (%zu, passed by the caller) is "
+ "smaller than the number of bytes requested "
+ "to read (%zu, as per the supplied sftp aio)",
+ buf_size, bytes_requested);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ /* handle an existing request */
+ while (msg == NULL) {
+ if (file->nonblocking) {
+ if (ssh_channel_poll(sftp->channel, 0) == 0) {
+ /* we cannot block */
+ return SSH_AGAIN;
+ }
+ }
+
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ msg = sftp_dequeue(sftp, (*aio)->id);
+ }
+
+ /*
+ * Release memory for the structure that (*aio) points to
+ * as all further points of return are for success or
+ * failure.
+ */
+ SFTP_AIO_FREE(*aio);
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp_set_error(sftp, status->status);
+ if (status->status != SSH_FX_EOF) {
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server : %s", status->errormsg);
+ err = SSH_ERROR;
+ } else {
+ file->eof = 1;
+ /* Update the offset correctly */
+ file->offset = file->offset - bytes_requested;
+ err = SSH_OK;
+ }
+
+ status_msg_free(status);
+ return err;
+
+ case SSH_FXP_DATA:
+ rc = ssh_buffer_get_u32(msg->payload, &string_len);
+ if (rc == 0) {
+ /* Insufficient data in the buffer */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received invalid DATA packet from sftp server");
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ host_len = ntohl(string_len);
+ if (host_len > buf_size) {
+ /*
+ * This should never happen, as according to the
+ * SFTP protocol the server reads bytes less than
+ * or equal to the number of bytes requested to read.
+ *
+ * And we have checked before that the buffer size is
+ * greater than or equal to the number of bytes requested
+ * to read, hence code of this if block should never
+ * get executed.
+ */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "DATA packet (%u bytes) received from sftp server "
+ "cannot fit into the supplied buffer (%zu bytes)",
+ host_len, buf_size);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ string_len = ssh_buffer_get_data(msg->payload, buf, host_len);
+ if (string_len != host_len) {
+ /* should never happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received invalid DATA packet from sftp server");
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ /* Update the offset with the correct value */
+ file->offset = file->offset - (bytes_requested - string_len);
+ sftp_message_free(msg);
+ return string_len;
+
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during read!", msg->packet_type);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ return SSH_ERROR; /* not reached */
+}
+
+ssize_t sftp_aio_begin_write(sftp_file file,
+ const void *buf,
+ size_t len,
+ sftp_aio *aio)
+{
+ sftp_session sftp = NULL;
+ ssh_buffer buffer = NULL;
+ sftp_aio aio_handle = NULL;
+ uint32_t id;
+ int rc;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (buf == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed instead "
+ "of a buffer's address");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ if (len == 0) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, 0 passed as the number "
+ "of bytes to write");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ /* Apply a cap on the length a user is allowed to write */
+ if (len > sftp->limits->max_write_length) {
+ len = sftp->limits->max_write_length;
+ }
+
+ if (aio == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed instead of a pointer to "
+ "a location to store an sftp aio handle");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ id = sftp_get_new_id(sftp);
+ rc = ssh_buffer_pack(buffer,
+ "dSqdP",
+ id,
+ file->handle,
+ file->offset,
+ len, /* len of datastring */
+ len, buf);
+
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle = sftp_aio_new();
+ if (aio_handle == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle->file = file;
+ aio_handle->id = id;
+ aio_handle->len = len;
+
+ rc = sftp_packet_write(sftp, SSH_FXP_WRITE, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc == SSH_ERROR) {
+ SFTP_AIO_FREE(aio_handle);
+ return SSH_ERROR;
+ }
+
+ /* Assume we wrote len bytes to the file */
+ file->offset += len;
+ *aio = aio_handle;
+ return len;
+}
+
+ssize_t sftp_aio_wait_write(sftp_aio *aio)
+{
+ sftp_file file = NULL;
+ size_t bytes_requested;
+
+ sftp_session sftp = NULL;
+ sftp_message msg = NULL;
+ sftp_status_message status = NULL;
+
+ /*
+ * This function releases the memory of the structure
+ * that (*aio) points to in all cases except when the
+ * return value is SSH_AGAIN.
+ *
+ * If the return value is SSH_AGAIN, the user should call this
+ * function again to get the response for the request corresponding
+ * to the structure that (*aio) points to, hence we don't release the
+ * structure's memory when SSH_AGAIN is returned.
+ */
+
+ if (aio == NULL || *aio == NULL) {
+ return SSH_ERROR;
+ }
+
+ file = (*aio)->file;
+ bytes_requested = (*aio)->len;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (bytes_requested == 0) {
+ /* This should never happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid sftp aio, len for requested i/o is 0");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ while (msg == NULL) {
+ if (file->nonblocking) {
+ if (ssh_channel_poll(sftp->channel, 0) == 0) {
+ /* we cannot block */
+ return SSH_AGAIN;
+ }
+ }
+
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ msg = sftp_dequeue(sftp, (*aio)->id);
+ }
+
+ /*
+ * Release memory for the structure that (*aio) points to
+ * as all further points of return are for success or
+ * failure.
+ */
+ SFTP_AIO_FREE(*aio);
+
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp_set_error(sftp, status->status);
+ if (status->status == SSH_FX_OK) {
+ status_msg_free(status);
+ return bytes_requested;
+ }
+
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return SSH_ERROR;
+ }
+
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during write!",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ return SSH_ERROR;
+}
+
+#endif /* WITH_SFTP */
diff --git a/src/sftp_common.c b/src/sftp_common.c
new file mode 100644
index 00000000..005fa9da
--- /dev/null
+++ b/src/sftp_common.c
@@ -0,0 +1,913 @@
+/*
+ * sftp_common.c - Secure FTP functions which are private and are used
+ * internally by other sftp api functions spread across
+ * various source files.
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2005-2008 by Aris Adamantiadis
+ * Copyright (c) 2008-2018 by Andreas Schneider <asn@cryptomilk.org>
+ *
+ * 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 <ctype.h>
+
+#include "libssh/sftp.h"
+#include "libssh/sftp_priv.h"
+#include "libssh/buffer.h"
+#include "libssh/session.h"
+#include "libssh/bytearray.h"
+
+#ifdef WITH_SFTP
+
+/* Buffer size maximum is 256M */
+#define SFTP_PACKET_SIZE_MAX 0x10000000
+
+sftp_packet sftp_packet_read(sftp_session sftp)
+{
+ uint8_t tmpbuf[4];
+ uint8_t *buffer = NULL;
+ sftp_packet packet = sftp->read_packet;
+ uint32_t size;
+ int nread;
+ bool is_eof;
+ int rc;
+
+ packet->sftp = sftp;
+
+ /*
+ * If the packet has a payload, then just reinit the buffer, otherwise
+ * allocate a new one.
+ */
+ if (packet->payload != NULL) {
+ rc = ssh_buffer_reinit(packet->payload);
+ if (rc != 0) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+ } else {
+ packet->payload = ssh_buffer_new();
+ if (packet->payload == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+ }
+
+ nread = 0;
+ do {
+ int s;
+
+ /* read from channel until 4 bytes have been read or an error occurs */
+ s = ssh_channel_read(sftp->channel, tmpbuf + nread, 4 - nread, 0);
+ if (s < 0) {
+ goto error;
+ } else if (s == 0) {
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received EOF while reading sftp packet size");
+ sftp_set_error(sftp, SSH_FX_EOF);
+ goto error;
+ }
+ } else {
+ nread += s;
+ }
+ } while (nread < 4);
+
+ size = PULL_BE_U32(tmpbuf, 0);
+ if (size == 0 || size > SFTP_PACKET_SIZE_MAX) {
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid sftp packet size!");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ goto error;
+ }
+
+ do {
+ nread = ssh_channel_read(sftp->channel, tmpbuf, 1, 0);
+ if (nread < 0) {
+ goto error;
+ } else if (nread == 0) {
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received EOF while reading sftp packet type");
+ sftp_set_error(sftp, SSH_FX_EOF);
+ goto error;
+ }
+ }
+ } while (nread < 1);
+
+ packet->type = tmpbuf[0];
+
+ /* Remove the packet type size */
+ size -= sizeof(uint8_t);
+
+ /* Allocate the receive buffer from payload */
+ buffer = ssh_buffer_allocate(packet->payload, size);
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ goto error;
+ }
+ while (size > 0 && size < SFTP_PACKET_SIZE_MAX) {
+ nread = ssh_channel_read(sftp->channel, buffer, size, 0);
+ if (nread < 0) {
+ /* TODO: check if there are cases where an error needs to be set here */
+ goto error;
+ }
+
+ if (nread > 0) {
+ buffer += nread;
+ size -= nread;
+ } else { /* nread == 0 */
+ /* Retry the reading unless the remote was closed */
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ ssh_set_error(sftp->session,
+ SSH_REQUEST_DENIED,
+ "Received EOF while reading sftp packet");
+ sftp_set_error(sftp, SSH_FX_EOF);
+ goto error;
+ }
+ }
+ }
+
+ return packet;
+error:
+ ssh_buffer_reinit(packet->payload);
+ return NULL;
+}
+
+int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
+{
+ uint8_t header[5] = {0};
+ uint32_t payload_size;
+ int size;
+ int rc;
+
+ /* Add size of type */
+ payload_size = ssh_buffer_get_len(payload) + sizeof(uint8_t);
+ PUSH_BE_U32(header, 0, payload_size);
+ PUSH_BE_U8(header, 4, type);
+
+ rc = ssh_buffer_prepend_data(payload, header, sizeof(header));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ size = ssh_channel_write(sftp->channel,
+ ssh_buffer_get(payload),
+ ssh_buffer_get_len(payload));
+ if (size < 0) {
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ if ((uint32_t)size != ssh_buffer_get_len(payload)) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Had to write %" PRIu32 " bytes, wrote only %d",
+ ssh_buffer_get_len(payload),
+ size);
+ }
+
+ return size;
+}
+
+void sftp_packet_free(sftp_packet packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ SSH_BUFFER_FREE(packet->payload);
+ free(packet);
+}
+
+int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr)
+{
+ uint32_t flags = (attr ? attr->flags : 0);
+ int rc;
+
+ flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
+ SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+
+ rc = ssh_buffer_pack(buffer, "d", flags);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
+ if (attr != NULL) {
+ if (flags & SSH_FILEXFER_ATTR_SIZE) {
+ rc = ssh_buffer_pack(buffer, "q", attr->size);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_UIDGID) {
+ rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ rc = ssh_buffer_pack(buffer, "d", attr->permissions);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Parse the attributes from a payload from some messages. It is coded on
+ * baselines from the protocol version 4.
+ * This code is more or less dead but maybe we will need it in the future.
+ */
+static sftp_attributes sftp_parse_attr_4(sftp_session sftp,
+ ssh_buffer buf,
+ int expectnames)
+{
+ sftp_attributes attr = NULL;
+ ssh_string owner = NULL;
+ ssh_string group = NULL;
+ uint32_t flags = 0;
+ int ok = 0;
+
+ /* unused member variable */
+ (void) expectnames;
+
+ attr = calloc(1, sizeof(struct sftp_attributes_struct));
+ if (attr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ /* This isn't really a loop, but it is like a try..catch.. */
+ do {
+ if (ssh_buffer_get_u32(buf, &flags) != 4) {
+ break;
+ }
+
+ flags = ntohl(flags);
+ attr->flags = flags;
+
+ if (flags & SSH_FILEXFER_ATTR_SIZE) {
+ if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
+ break;
+ }
+ attr->size = ntohll(attr->size);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
+ owner = ssh_buffer_get_ssh_string(buf);
+ if (owner == NULL) {
+ break;
+ }
+ attr->owner = ssh_string_to_char(owner);
+ SSH_STRING_FREE(owner);
+ if (attr->owner == NULL) {
+ break;
+ }
+
+ group = ssh_buffer_get_ssh_string(buf);
+ if (group == NULL) {
+ break;
+ }
+ attr->group = ssh_string_to_char(group);
+ SSH_STRING_FREE(group);
+ if (attr->group == NULL) {
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
+ break;
+ }
+ attr->permissions = ntohl(attr->permissions);
+
+ /* FIXME on windows! */
+ switch (attr->permissions & SSH_S_IFMT) {
+ case SSH_S_IFSOCK:
+ case SSH_S_IFBLK:
+ case SSH_S_IFCHR:
+ case SSH_S_IFIFO:
+ attr->type = SSH_FILEXFER_TYPE_SPECIAL;
+ break;
+ case SSH_S_IFLNK:
+ attr->type = SSH_FILEXFER_TYPE_SYMLINK;
+ break;
+ case SSH_S_IFREG:
+ attr->type = SSH_FILEXFER_TYPE_REGULAR;
+ break;
+ case SSH_S_IFDIR:
+ attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
+ break;
+ default:
+ attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
+ if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
+ break;
+ }
+ attr->atime64 = ntohll(attr->atime64);
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
+ break;
+ }
+ attr->atime_nseconds = ntohl(attr->atime_nseconds);
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
+ if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
+ break;
+ }
+ attr->createtime = ntohll(attr->createtime);
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
+ break;
+ }
+ attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
+ if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
+ break;
+ }
+ attr->mtime64 = ntohll(attr->mtime64);
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
+ break;
+ }
+ attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACL) {
+ if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
+ break;
+ }
+ attr->extended_count = ntohl(attr->extended_count);
+
+ while (attr->extended_count &&
+ (attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
+ (attr->extended_data = ssh_buffer_get_ssh_string(buf))) {
+ attr->extended_count--;
+ }
+
+ if (attr->extended_count) {
+ break;
+ }
+ }
+ ok = 1;
+ } while (0);
+
+ if (ok == 0) {
+ /* break issued somewhere */
+ 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);
+
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
+
+ return NULL;
+ }
+
+ return attr;
+}
+
+enum sftp_longname_field_e {
+ SFTP_LONGNAME_PERM = 0,
+ SFTP_LONGNAME_FIXME,
+ SFTP_LONGNAME_OWNER,
+ SFTP_LONGNAME_GROUP,
+ SFTP_LONGNAME_SIZE,
+ SFTP_LONGNAME_DATE,
+ SFTP_LONGNAME_TIME,
+ SFTP_LONGNAME_NAME,
+};
+
+static char * sftp_parse_longname(const char *longname,
+ enum sftp_longname_field_e longname_field)
+{
+ const char *p, *q;
+ size_t len, field = 0;
+
+ p = longname;
+ /*
+ * Find the beginning of the field which is specified
+ * by sftp_longname_field_e.
+ */
+ while (field != longname_field) {
+ if (isspace(*p)) {
+ field++;
+ p++;
+ while (*p && isspace(*p)) {
+ p++;
+ }
+ } else {
+ p++;
+ }
+ }
+
+ q = p;
+ while (! isspace(*q)) {
+ q++;
+ }
+
+ len = q - p;
+
+ return strndup(p, len);
+}
+
+/* sftp version 0-3 code. It is different from the v4 */
+/* maybe a paste of the draft is better than the code */
+/*
+ uint32 flags
+ uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ string extended_type
+ string extended_data
+ ... more extended data (extended_type - extended_data pairs),
+ so that number of pairs equals extended_count */
+static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
+ ssh_buffer buf,
+ int expectname)
+{
+ sftp_attributes attr;
+ int rc;
+
+ attr = calloc(1, sizeof(struct sftp_attributes_struct));
+ if (attr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ if (expectname) {
+ rc = ssh_buffer_unpack(buf, "ss",
+ &attr->name,
+ &attr->longname);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Name: %s", attr->name);
+
+ /* Set owner and group if we talk to openssh and have the longname */
+ if (ssh_get_openssh_version(sftp->session)) {
+ attr->owner = sftp_parse_longname(attr->longname,
+ SFTP_LONGNAME_OWNER);
+ if (attr->owner == NULL) {
+ goto error;
+ }
+
+ attr->group = sftp_parse_longname(attr->longname,
+ SFTP_LONGNAME_GROUP);
+ if (attr->group == NULL) {
+ goto error;
+ }
+ }
+ }
+
+ rc = ssh_buffer_unpack(buf, "d", &attr->flags);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Flags: %.8" PRIx32 "\n", attr->flags);
+
+ if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
+ rc = ssh_buffer_unpack(buf, "q", &attr->size);
+ if(rc != SSH_OK) {
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Size: %" PRIu64 "\n",
+ (uint64_t) attr->size);
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
+ rc = ssh_buffer_unpack(buf, "dd",
+ &attr->uid,
+ &attr->gid);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ switch (attr->permissions & SSH_S_IFMT) {
+ case SSH_S_IFSOCK:
+ case SSH_S_IFBLK:
+ case SSH_S_IFCHR:
+ case SSH_S_IFIFO:
+ attr->type = SSH_FILEXFER_TYPE_SPECIAL;
+ break;
+ case SSH_S_IFLNK:
+ attr->type = SSH_FILEXFER_TYPE_SYMLINK;
+ break;
+ case SSH_S_IFREG:
+ attr->type = SSH_FILEXFER_TYPE_REGULAR;
+ break;
+ case SSH_S_IFDIR:
+ attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
+ break;
+ default:
+ attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ rc = ssh_buffer_unpack(buf, "dd",
+ &attr->atime,
+ &attr->mtime);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ if (attr->extended_count > 0) {
+ rc = ssh_buffer_unpack(buf, "ss",
+ &attr->extended_type,
+ &attr->extended_data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ attr->extended_count--;
+ }
+ /* just ignore the remaining extensions */
+
+ while (attr->extended_count > 0) {
+ ssh_string tmp1,tmp2;
+ rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SAFE_FREE(tmp1);
+ SAFE_FREE(tmp2);
+ attr->extended_count--;
+ }
+ }
+
+ return attr;
+
+error:
+ SSH_STRING_FREE(attr->extended_type);
+ SSH_STRING_FREE(attr->extended_data);
+ SAFE_FREE(attr->name);
+ SAFE_FREE(attr->longname);
+ SAFE_FREE(attr->owner);
+ SAFE_FREE(attr->group);
+ SAFE_FREE(attr);
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+
+ return NULL;
+}
+
+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);
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ return sftp_parse_attr_3(session, buf, expectname);
+ default:
+ ssh_set_error(session->session, SSH_FATAL,
+ "Version %d unsupported by client",
+ session->server_version);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+void sftp_set_error(sftp_session sftp, int errnum)
+{
+ if (sftp != NULL) {
+ sftp->errnum = errnum;
+ }
+}
+
+void sftp_message_free(sftp_message msg)
+{
+ if (msg == NULL) {
+ return;
+ }
+
+ SSH_BUFFER_FREE(msg->payload);
+ SAFE_FREE(msg);
+}
+
+static sftp_request_queue request_queue_new(sftp_message msg)
+{
+ sftp_request_queue queue = NULL;
+
+ queue = calloc(1, sizeof(struct sftp_request_queue_struct));
+ if (queue == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ queue->message = msg;
+
+ return queue;
+}
+
+static void request_queue_free(sftp_request_queue queue)
+{
+ if (queue == NULL) {
+ return;
+ }
+
+ ZERO_STRUCTP(queue);
+ SAFE_FREE(queue);
+}
+
+static int sftp_enqueue(sftp_session sftp, sftp_message msg)
+{
+ sftp_request_queue queue = NULL;
+ sftp_request_queue ptr;
+
+ queue = request_queue_new(msg);
+ if (queue == NULL) {
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Queued msg id %" PRIu32 " type %d",
+ msg->id, msg->packet_type);
+
+ if(sftp->queue == NULL) {
+ sftp->queue = queue;
+ } else {
+ ptr = sftp->queue;
+ while(ptr->next) {
+ ptr=ptr->next; /* find end of linked list */
+ }
+ ptr->next = queue; /* add it on bottom */
+ }
+
+ return 0;
+}
+
+/*
+ * Pulls a message from the queue based on the ID.
+ * Returns NULL if no message has been found.
+ */
+sftp_message sftp_dequeue(sftp_session sftp, uint32_t id)
+{
+ sftp_request_queue prev = NULL;
+ sftp_request_queue queue;
+ sftp_message msg;
+
+ if(sftp->queue == NULL) {
+ return NULL;
+ }
+
+ queue = sftp->queue;
+ while (queue) {
+ if (queue->message->id == id) {
+ /* remove from queue */
+ if (prev == NULL) {
+ sftp->queue = queue->next;
+ } else {
+ prev->next = queue->next;
+ }
+ msg = queue->message;
+ request_queue_free(queue);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Dequeued msg id %" PRIu32 " type %d",
+ msg->id,
+ msg->packet_type);
+ return msg;
+ }
+ prev = queue;
+ queue = queue->next;
+ }
+
+ return NULL;
+}
+
+static sftp_message sftp_get_message(sftp_packet packet)
+{
+ sftp_session sftp = packet->sftp;
+ sftp_message msg = NULL;
+ int rc;
+
+ switch (packet->type) {
+ case SSH_FXP_STATUS:
+ case SSH_FXP_HANDLE:
+ case SSH_FXP_DATA:
+ case SSH_FXP_ATTRS:
+ case SSH_FXP_NAME:
+ case SSH_FXP_EXTENDED_REPLY:
+ break;
+ default:
+ ssh_set_error(packet->sftp->session,
+ SSH_FATAL,
+ "Unknown packet type %d",
+ packet->type);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ msg = calloc(1, sizeof(struct sftp_message_struct));
+ if (msg == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ msg->sftp = packet->sftp;
+ msg->packet_type = packet->type;
+
+ /* Move the payload from the packet to the message */
+ msg->payload = packet->payload;
+ packet->payload = NULL;
+
+ rc = ssh_buffer_unpack(msg->payload, "d", &msg->id);
+ if (rc != SSH_OK) {
+ ssh_set_error(packet->sftp->session, SSH_FATAL,
+ "Invalid packet %d: no ID", packet->type);
+ sftp_message_free(msg);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Packet with id %" PRIu32 " type %d",
+ msg->id,
+ msg->packet_type);
+
+ return msg;
+}
+
+int sftp_read_and_dispatch(sftp_session sftp)
+{
+ sftp_packet packet = NULL;
+ sftp_message msg = NULL;
+
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
+ /* something nasty happened reading the packet */
+ return -1;
+ }
+
+ msg = sftp_get_message(packet);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ if (sftp_enqueue(sftp, msg) < 0) {
+ sftp_message_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+sftp_status_message parse_status_msg(sftp_message msg)
+{
+ sftp_status_message status = NULL;
+ int rc;
+
+ if (msg->packet_type != SSH_FXP_STATUS) {
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Not a ssh_fxp_status message passed in!");
+ sftp_set_error(msg->sftp, SSH_FX_BAD_MESSAGE);
+ return NULL;
+ }
+
+ status = calloc(1, sizeof(struct sftp_status_message_struct));
+ if (status == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ status->id = msg->id;
+ rc = ssh_buffer_unpack(msg->payload, "d",
+ &status->status);
+ if (rc != SSH_OK) {
+ SAFE_FREE(status);
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_STATUS message");
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ rc = ssh_buffer_unpack(msg->payload, "ss",
+ &status->errormsg,
+ &status->langmsg);
+
+ if (rc != SSH_OK && msg->sftp->version >= 3) {
+ /* These are mandatory from version 3 */
+ SAFE_FREE(status);
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_STATUS message");
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ if (status->errormsg == NULL)
+ status->errormsg = strdup("No error message in packet");
+
+ if (status->langmsg == NULL)
+ status->langmsg = strdup("");
+
+ if (status->errormsg == NULL || status->langmsg == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ status_msg_free(status);
+ return NULL;
+ }
+
+ return status;
+}
+
+void status_msg_free(sftp_status_message status)
+{
+ if (status == NULL) {
+ return;
+ }
+
+ SAFE_FREE(status->errormsg);
+ SAFE_FREE(status->langmsg);
+ SAFE_FREE(status);
+}
+
+#endif /* WITH_SFTP */
diff --git a/src/sftpserver.c b/src/sftpserver.c
index 9117f155..7d8070b1 100644
--- a/src/sftpserver.c
+++ b/src/sftpserver.c
@@ -3,7 +3,8 @@
*
* This file is part of the SSH Library
*
- * Copyright (c) 2005 by Aris Adamantiadis
+ * Copyright (c) 2005 Aris Adamantiadis
+ * Copyright (c) 2022 Zeyu Sheng <shengzeyu19_98@163.com>
*
* 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
@@ -23,16 +24,23 @@
#include "config.h"
-#include <stdio.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <dirent.h>
+#include <sys/statvfs.h>
#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <time.h>
#include "libssh/libssh.h"
#include "libssh/sftp.h"
#include "libssh/sftp_priv.h"
+#include "libssh/sftpserver.h"
#include "libssh/ssh2.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
@@ -40,518 +48,1720 @@
#define SFTP_HANDLES 256
-sftp_client_message sftp_get_client_message(sftp_session sftp) {
- ssh_session session = sftp->session;
- sftp_packet packet;
- sftp_client_message msg;
- ssh_buffer payload;
- int rc;
+#define MAX_ENTRIES_NUM_IN_PACKET 50
+#define MAX_LONG_NAME_LEN 350
- msg = malloc(sizeof (struct sftp_client_message_struct));
- if (msg == NULL) {
- ssh_set_error_oom(session);
- return NULL;
- }
- ZERO_STRUCTP(msg);
+static sftp_client_message
+sftp_make_client_message(sftp_session sftp, sftp_packet packet)
+{
+ ssh_session session = sftp->session;
+ sftp_client_message msg = NULL;
+ ssh_buffer payload = NULL;
+ int rc;
+ int version;
+
+ msg = calloc(1, sizeof(struct sftp_client_message_struct));
+ if (msg == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+
+ payload = packet->payload;
+ msg->type = packet->type;
+ msg->sftp = sftp;
+
+ /* take a copy of the whole packet */
+ msg->complete_message = ssh_buffer_new();
+ if (msg->complete_message == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- packet = sftp_packet_read(sftp);
- if (packet == NULL) {
- ssh_set_error_oom(session);
+ rc = ssh_buffer_add_data(msg->complete_message,
+ ssh_buffer_get(payload),
+ ssh_buffer_get_len(payload));
+ if (rc < 0) {
+ goto error;
+ }
+
+ if (msg->type != SSH_FXP_INIT) {
+ rc = ssh_buffer_get_u32(payload, &msg->id);
+ if (rc != sizeof(uint32_t)) {
+ goto error;
+ }
+ }
+
+ switch (msg->type) {
+ case SSH_FXP_INIT:
+ rc = ssh_buffer_unpack(payload,
+ "d",
+ &version);
+ if (rc != SSH_OK) {
+ printf("unpack init failed!\n");
+ goto error;
+ }
+ version = ntohl(version);
+ sftp->client_version = version;
+ break;
+ case SSH_FXP_CLOSE:
+ case SSH_FXP_READDIR:
+ msg->handle = ssh_buffer_get_ssh_string(payload);
+ if (msg->handle == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_READ:
+ rc = ssh_buffer_unpack(payload,
+ "Sqd",
+ &msg->handle,
+ &msg->offset,
+ &msg->len);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_WRITE:
+ rc = ssh_buffer_unpack(payload,
+ "SqS",
+ &msg->handle,
+ &msg->offset,
+ &msg->data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_REMOVE:
+ case SSH_FXP_RMDIR:
+ case SSH_FXP_OPENDIR:
+ case SSH_FXP_READLINK:
+ case SSH_FXP_REALPATH:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_RENAME:
+ case SSH_FXP_SYMLINK:
+ rc = ssh_buffer_unpack(payload,
+ "sS",
+ &msg->filename,
+ &msg->data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_MKDIR:
+ case SSH_FXP_SETSTAT:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ msg->attr = sftp_parse_attr(sftp, payload, 0);
+ if (msg->attr == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_FSETSTAT:
+ msg->handle = ssh_buffer_get_ssh_string(payload);
+ if (msg->handle == NULL) {
+ goto error;
+ }
+ msg->attr = sftp_parse_attr(sftp, payload, 0);
+ if (msg->attr == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_LSTAT:
+ case SSH_FXP_STAT:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ if (sftp->version > 3) {
+ ssh_buffer_unpack(payload, "d", &msg->flags);
+ }
+ break;
+ case SSH_FXP_OPEN:
+ rc = ssh_buffer_unpack(payload,
+ "sd",
+ &msg->filename,
+ &msg->flags);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ msg->attr = sftp_parse_attr(sftp, payload, 0);
+ if (msg->attr == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_FSTAT:
+ rc = ssh_buffer_unpack(payload,
+ "S",
+ &msg->handle);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_EXTENDED:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->submessage);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ if (strcmp(msg->submessage, "hardlink@openssh.com") == 0 ||
+ strcmp(msg->submessage, "posix-rename@openssh.com") == 0) {
+ rc = ssh_buffer_unpack(payload,
+ "sS",
+ &msg->filename,
+ &msg->data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ } else if (strcmp(msg->submessage, "statvfs@openssh.com") == 0 ){
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
+ break;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received unhandled sftp message %d", msg->type);
+ goto error;
+ }
+
+ return msg;
+
+error:
sftp_client_message_free(msg);
return NULL;
- }
-
- payload = packet->payload;
- msg->type = packet->type;
- msg->sftp = sftp;
+}
- /* take a copy of the whole packet */
- msg->complete_message = ssh_buffer_new();
- if (msg->complete_message == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
+sftp_client_message sftp_get_client_message(sftp_session sftp)
+{
+ ssh_session session = sftp->session;
+ sftp_packet packet;
- rc = ssh_buffer_add_data(msg->complete_message,
- ssh_buffer_get(payload),
- ssh_buffer_get_len(payload));
- if (rc < 0) {
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
ssh_set_error_oom(session);
- sftp_client_message_free(msg);
return NULL;
- }
+ }
+ return sftp_make_client_message(sftp, packet);
+}
- ssh_buffer_get_u32(payload, &msg->id);
+/**
+ * @brief Get the client message from a sftp packet.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @return The pointer to the generated sftp client message.
+ */
+static sftp_client_message
+sftp_get_client_message_from_packet(sftp_session sftp)
+{
+ sftp_packet packet = NULL;
- switch(msg->type) {
- case SSH_FXP_CLOSE:
- case SSH_FXP_READDIR:
- msg->handle = ssh_buffer_get_ssh_string(payload);
- if (msg->handle == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_READ:
- rc = ssh_buffer_unpack(payload,
- "Sqd",
- &msg->handle,
- &msg->offset,
- &msg->len);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_WRITE:
- rc = ssh_buffer_unpack(payload,
- "SqS",
- &msg->handle,
- &msg->offset,
- &msg->data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_REMOVE:
- case SSH_FXP_RMDIR:
- case SSH_FXP_OPENDIR:
- case SSH_FXP_READLINK:
- case SSH_FXP_REALPATH:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->filename);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_RENAME:
- case SSH_FXP_SYMLINK:
- rc = ssh_buffer_unpack(payload,
- "sS",
- &msg->filename,
- &msg->data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_MKDIR:
- case SSH_FXP_SETSTAT:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->filename);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- msg->attr = sftp_parse_attr(sftp, payload, 0);
- if (msg->attr == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_FSETSTAT:
- msg->handle = ssh_buffer_get_ssh_string(payload);
- if (msg->handle == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- msg->attr = sftp_parse_attr(sftp, payload, 0);
- if (msg->attr == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_LSTAT:
- case SSH_FXP_STAT:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->filename);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- if(sftp->version > 3) {
- ssh_buffer_unpack(payload, "d", &msg->flags);
- }
- break;
- case SSH_FXP_OPEN:
- rc = ssh_buffer_unpack(payload,
- "sd",
- &msg->filename,
- &msg->flags);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- msg->attr = sftp_parse_attr(sftp, payload, 0);
- if (msg->attr == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_FSTAT:
- rc = ssh_buffer_unpack(payload,
- "S",
- &msg->handle);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_EXTENDED:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->submessage);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
+ packet = sftp->read_packet;
+ if (packet == NULL) {
return NULL;
- }
-
- if (strcmp(msg->submessage, "hardlink@openssh.com") == 0 ||
- strcmp(msg->submessage, "posix-rename@openssh.com") == 0) {
- rc = ssh_buffer_unpack(payload,
- "sS",
- &msg->filename,
- &msg->data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- }
- break;
- default:
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received unhandled sftp message %d", msg->type);
- sftp_client_message_free(msg);
- return NULL;
- }
-
- return msg;
+ }
+ return sftp_make_client_message(sftp, packet);
}
-/* Send an sftp client message. Can be used in cas of proxying */
-int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){
- return sftp_packet_write(sftp, msg->type, msg->complete_message);
+/* Send an sftp client message. Can be used in case of proxying */
+int sftp_send_client_message(sftp_session sftp, sftp_client_message msg)
+{
+ return sftp_packet_write(sftp, msg->type, msg->complete_message);
}
-uint8_t sftp_client_message_get_type(sftp_client_message msg){
- return msg->type;
+uint8_t sftp_client_message_get_type(sftp_client_message msg)
+{
+ return msg->type;
}
-const char *sftp_client_message_get_filename(sftp_client_message msg){
- return msg->filename;
+const char *sftp_client_message_get_filename(sftp_client_message msg)
+{
+ return msg->filename;
}
-void sftp_client_message_set_filename(sftp_client_message msg, const char *newname){
- free(msg->filename);
- msg->filename = strdup(newname);
+void
+sftp_client_message_set_filename(sftp_client_message msg, const char *newname)
+{
+ free(msg->filename);
+ msg->filename = strdup(newname);
}
-const char *sftp_client_message_get_data(sftp_client_message msg){
- if (msg->str_data == NULL)
- msg->str_data = ssh_string_to_char(msg->data);
- return msg->str_data;
+const char *sftp_client_message_get_data(sftp_client_message msg)
+{
+ if (msg->str_data == NULL)
+ msg->str_data = ssh_string_to_char(msg->data);
+ return msg->str_data;
}
-uint32_t sftp_client_message_get_flags(sftp_client_message msg){
- return msg->flags;
+uint32_t sftp_client_message_get_flags(sftp_client_message msg)
+{
+ return msg->flags;
}
-const char *sftp_client_message_get_submessage(sftp_client_message msg){
- return msg->submessage;
+const char *sftp_client_message_get_submessage(sftp_client_message msg)
+{
+ return msg->submessage;
}
-void sftp_client_message_free(sftp_client_message msg) {
- if (msg == NULL) {
- return;
- }
+void sftp_client_message_free(sftp_client_message msg)
+{
+ if (msg == NULL) {
+ return;
+ }
- SAFE_FREE(msg->filename);
- SAFE_FREE(msg->submessage);
- SSH_STRING_FREE(msg->data);
- SSH_STRING_FREE(msg->handle);
- sftp_attributes_free(msg->attr);
- SSH_BUFFER_FREE(msg->complete_message);
- SAFE_FREE(msg->str_data);
- ZERO_STRUCTP(msg);
- SAFE_FREE(msg);
+ SAFE_FREE(msg->filename);
+ SAFE_FREE(msg->submessage);
+ SSH_STRING_FREE(msg->data);
+ SSH_STRING_FREE(msg->handle);
+ sftp_attributes_free(msg->attr);
+ SSH_BUFFER_FREE(msg->complete_message);
+ SAFE_FREE(msg->str_data);
+ ZERO_STRUCTP(msg);
+ SAFE_FREE(msg);
}
-int sftp_reply_name(sftp_client_message msg, const char *name,
- sftp_attributes attr) {
- ssh_buffer out;
- ssh_string file;
+int
+sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
+{
+ ssh_buffer out;
+ ssh_string file;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- file = ssh_string_from_char(name);
- if (file == NULL) {
- SSH_BUFFER_FREE(out);
- return -1;
- }
-
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, htonl(1)) < 0 ||
- ssh_buffer_add_ssh_string(out, file) < 0 ||
- 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) {
+ file = ssh_string_from_char(name);
+ if (file == NULL) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending name %s", ssh_string_get_char(file));
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, htonl(1)) < 0 ||
+ ssh_buffer_add_ssh_string(out, file) < 0 ||
+ 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);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
SSH_STRING_FREE(file);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- SSH_STRING_FREE(file);
- return 0;
+ return 0;
}
-int sftp_reply_handle(sftp_client_message msg, ssh_string handle){
- ssh_buffer out;
+int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- 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_log_hexdump("Sending handle:",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(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);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- return 0;
+ return 0;
}
-int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) {
- ssh_buffer out;
+int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- 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_LOG(SSH_LOG_PROTOCOL, "Sending 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);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- return 0;
+ return 0;
}
-int sftp_reply_names_add(sftp_client_message msg, const char *file,
- const char *longname, sftp_attributes attr) {
- ssh_string name;
+int
+sftp_reply_names_add(sftp_client_message msg, const char *file,
+ const char *longname, sftp_attributes attr)
+{
+ ssh_string name;
- name = ssh_string_from_char(file);
- if (name == NULL) {
- return -1;
- }
+ name = ssh_string_from_char(file);
+ if (name == NULL) {
+ return -1;
+ }
- if (msg->attrbuf == NULL) {
- msg->attrbuf = ssh_buffer_new();
if (msg->attrbuf == NULL) {
- SSH_STRING_FREE(name);
- return -1;
+ msg->attrbuf = ssh_buffer_new();
+ if (msg->attrbuf == NULL) {
+ SSH_STRING_FREE(name);
+ return -1;
+ }
}
- }
- if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0) {
- SSH_STRING_FREE(name);
- return -1;
- }
+ if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0) {
+ SSH_STRING_FREE(name);
+ return -1;
+ }
- 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);
- return -1;
- }
- SSH_STRING_FREE(name);
- msg->attr_num++;
+ 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);
+ return -1;
+ }
+ SSH_STRING_FREE(name);
+ msg->attr_num++;
- return 0;
+ return 0;
}
-int sftp_reply_names(sftp_client_message msg) {
- ssh_buffer out;
+int sftp_reply_names(sftp_client_message msg)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- SSH_BUFFER_FREE(msg->attrbuf);
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ SSH_BUFFER_FREE(msg->attrbuf);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d names", msg->attr_num);
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
+ 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);
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
- 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);
- return -1;
- }
-
- SSH_BUFFER_FREE(out);
- SSH_BUFFER_FREE(msg->attrbuf);
- msg->attr_num = 0;
- msg->attrbuf = NULL;
+ msg->attr_num = 0;
+ msg->attrbuf = NULL;
- return 0;
+ return 0;
}
-int sftp_reply_status(sftp_client_message msg, uint32_t status,
- const char *message) {
- ssh_buffer out;
- ssh_string s;
+int
+sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
+{
+ ssh_buffer out;
+ ssh_string s;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- s = ssh_string_from_char(message ? message : "");
- if (s == NULL) {
- SSH_BUFFER_FREE(out);
- return -1;
- }
+ s = ssh_string_from_char(message ? message : "");
+ if (s == NULL) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending status %d, message: %s", status,
+ ssh_string_get_char(s));
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, htonl(status)) < 0 ||
+ 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);
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, htonl(status)) < 0 ||
- 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);
- return -1;
- }
-
- SSH_BUFFER_FREE(out);
- SSH_STRING_FREE(s);
- return 0;
+ return 0;
}
-int sftp_reply_data(sftp_client_message msg, const void *data, int len) {
- ssh_buffer out;
+int sftp_reply_data(sftp_client_message msg, const void *data, int len)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- 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_LOG(SSH_LOG_PROTOCOL, "Sending data, length: %d", len);
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ 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);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- return 0;
+ return 0;
+}
+
+/**
+ * @brief Handle the statvfs request, return information the mounted file system.
+ *
+ * @param msg The sftp client message.
+ *
+ * @param st The statvfs state of target file.
+ *
+ * @return 0 on success, < 0 on error with ssh and sftp error set.
+ *
+ * @see sftp_get_error()
+ */
+static int
+sftp_reply_statvfs(sftp_client_message msg, sftp_statvfs_t st)
+{
+ int ret = 0;
+ ssh_buffer out;
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending statvfs reply");
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_bsize)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_frsize)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_blocks)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_bfree)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_bavail)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_files)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_ffree)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_favail)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_fsid)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_flag)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_namemax)) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_EXTENDED_REPLY, out) < 0) {
+ ret = -1;
+ }
+ SSH_BUFFER_FREE(out);
+
+ return ret;
+}
+
+int sftp_reply_version(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_session session = sftp->session;
+ int version;
+ ssh_buffer reply;
+ int rc;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending version packet");
+
+ version = sftp->client_version;
+ reply = ssh_buffer_new();
+ if (reply == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+
+ rc = ssh_buffer_pack(reply, "dssssss",
+ LIBSFTP_VERSION,
+ "posix-rename@openssh.com",
+ "1",
+ "hardlink@openssh.com",
+ "1",
+ "statvfs@openssh.com",
+ "2");
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ SSH_BUFFER_FREE(reply);
+ return -1;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_VERSION, reply);
+ if (rc < 0) {
+ SSH_BUFFER_FREE(reply);
+ return -1;
+ }
+ SSH_BUFFER_FREE(reply);
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Server version sent");
+
+ if (version > LIBSFTP_VERSION) {
+ sftp->version = LIBSFTP_VERSION;
+ } else {
+ sftp->version = version;
+ }
+
+ return SSH_OK;
}
+
/*
* This function will return you a new handle to give the client.
* the function accepts an info that can be retrieved later with
* the handle. Care is given that a corrupted handle won't give a
* valid info (or worse).
*/
-ssh_string sftp_handle_alloc(sftp_session sftp, void *info) {
- ssh_string ret;
- uint32_t val;
- uint32_t i;
+ssh_string sftp_handle_alloc(sftp_session sftp, void *info)
+{
+ ssh_string ret;
+ uint32_t val;
+ uint32_t i;
- if (sftp->handles == NULL) {
- sftp->handles = calloc(SFTP_HANDLES, sizeof(void *));
if (sftp->handles == NULL) {
- return NULL;
+ sftp->handles = calloc(SFTP_HANDLES, sizeof(void *));
+ if (sftp->handles == NULL) {
+ return NULL;
+ }
}
- }
- for (i = 0; i < SFTP_HANDLES; i++) {
- if (sftp->handles[i] == NULL) {
- break;
+ for (i = 0; i < SFTP_HANDLES; i++) {
+ if (sftp->handles[i] == NULL) {
+ break;
+ }
}
- }
- if (i == SFTP_HANDLES) {
- return NULL; /* no handle available */
- }
+ if (i == SFTP_HANDLES) {
+ return NULL; /* no handle available */
+ }
- val = i;
- ret = ssh_string_new(4);
- if (ret == NULL) {
- return NULL;
- }
+ val = i;
+ ret = ssh_string_new(4);
+ if (ret == NULL) {
+ return NULL;
+ }
- memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
- sftp->handles[i] = info;
+ memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
+ sftp->handles[i] = info;
- return ret;
+ return ret;
}
-void *sftp_handle(sftp_session sftp, ssh_string handle){
- uint32_t val;
+void *sftp_handle(sftp_session sftp, ssh_string handle)
+{
+ uint32_t val;
- if (sftp->handles == NULL) {
- return NULL;
- }
+ if (sftp->handles == NULL) {
+ return NULL;
+ }
- if (ssh_string_len(handle) != sizeof(uint32_t)) {
- return NULL;
- }
+ if (ssh_string_len(handle) != sizeof(uint32_t)) {
+ return NULL;
+ }
- memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
+ memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
- if (val > SFTP_HANDLES) {
- return NULL;
- }
+ if (val > SFTP_HANDLES) {
+ return NULL;
+ }
+
+ return sftp->handles[val];
+}
+
+void sftp_handle_remove(sftp_session sftp, void *handle)
+{
+ int i;
+
+ for (i = 0; i < SFTP_HANDLES; i++) {
+ if (sftp->handles[i] == handle) {
+ sftp->handles[i] = NULL;
+ break;
+ }
+ }
+}
+
+/* Default SFTP handlers */
+
+static const char *
+ssh_str_error(int u_errno)
+{
+ switch (u_errno) {
+ case SSH_FX_NO_SUCH_FILE:
+ return "No such file";
+ case SSH_FX_PERMISSION_DENIED:
+ return "Permission denied";
+ case SSH_FX_BAD_MESSAGE:
+ return "Bad message";
+ case SSH_FX_OP_UNSUPPORTED:
+ return "Operation not supported";
+ default:
+ return "Operation failed";
+ }
+}
+
+static int
+unix_errno_to_ssh_stat(int u_errno)
+{
+ int ret = SSH_OK;
+ switch (u_errno) {
+ case 0:
+ break;
+ case ENOENT:
+ case ENOTDIR:
+ case EBADF:
+ case ELOOP:
+ ret = SSH_FX_NO_SUCH_FILE;
+ break;
+ case EPERM:
+ case EACCES:
+ case EFAULT:
+ ret = SSH_FX_PERMISSION_DENIED;
+ break;
+ case ENAMETOOLONG:
+ case EINVAL:
+ ret = SSH_FX_BAD_MESSAGE;
+ break;
+ case ENOSYS:
+ ret = SSH_FX_OP_UNSUPPORTED;
+ break;
+ default:
+ ret = SSH_FX_FAILURE;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+stat_to_filexfer_attrib(const struct stat *z_st, struct sftp_attributes_struct *z_attr)
+{
+ z_attr->flags = 0 | (uint32_t)SSH_FILEXFER_ATTR_SIZE;
+ z_attr->size = z_st->st_size;
+
+ z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_UIDGID;
+ z_attr->uid = z_st->st_uid;
+ z_attr->gid = z_st->st_gid;
- return sftp->handles[val];
+ z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS;
+ z_attr->permissions = z_st->st_mode;
+
+ z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_ACMODTIME;
+ z_attr->atime = z_st->st_atime;
+ z_attr->mtime = z_st->st_mtime;
+}
+
+static void
+clear_filexfer_attrib(struct sftp_attributes_struct *z_attr)
+{
+ z_attr->flags = 0;
+ z_attr->size = 0;
+ z_attr->uid = 0;
+ z_attr->gid = 0;
+ z_attr->permissions = 0;
+ z_attr->atime = 0;
+ z_attr->mtime = 0;
+}
+
+#ifndef _WIN32
+/* internal */
+enum sftp_handle_type
+{
+ SFTP_NULL_HANDLE,
+ SFTP_DIR_HANDLE,
+ SFTP_FILE_HANDLE
+};
+
+struct sftp_handle
+{
+ enum sftp_handle_type type;
+ int fd;
+ DIR *dirp;
+ char *name;
+};
+
+SSH_SFTP_CALLBACK(process_unsupposed);
+SSH_SFTP_CALLBACK(process_open);
+SSH_SFTP_CALLBACK(process_read);
+SSH_SFTP_CALLBACK(process_write);
+SSH_SFTP_CALLBACK(process_close);
+SSH_SFTP_CALLBACK(process_opendir);
+SSH_SFTP_CALLBACK(process_readdir);
+SSH_SFTP_CALLBACK(process_rmdir);
+SSH_SFTP_CALLBACK(process_realpath);
+SSH_SFTP_CALLBACK(process_mkdir);
+SSH_SFTP_CALLBACK(process_lstat);
+SSH_SFTP_CALLBACK(process_stat);
+SSH_SFTP_CALLBACK(process_readlink);
+SSH_SFTP_CALLBACK(process_symlink);
+SSH_SFTP_CALLBACK(process_remove);
+SSH_SFTP_CALLBACK(process_extended_statvfs);
+
+const struct sftp_message_handler message_handlers[] = {
+ {"open", NULL, SSH_FXP_OPEN, process_open},
+ {"close", NULL, SSH_FXP_CLOSE, process_close},
+ {"read", NULL, SSH_FXP_READ, process_read},
+ {"write", NULL, SSH_FXP_WRITE, process_write},
+ {"lstat", NULL, SSH_FXP_LSTAT, process_lstat},
+ {"fstat", NULL, SSH_FXP_FSTAT, process_unsupposed},
+ {"setstat", NULL, SSH_FXP_SETSTAT, process_unsupposed},
+ {"fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed},
+ {"opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
+ {"readdir", NULL, SSH_FXP_READDIR, process_readdir},
+ {"remove", NULL, SSH_FXP_REMOVE, process_remove},
+ {"mkdir", NULL, SSH_FXP_MKDIR, process_mkdir},
+ {"rmdir", NULL, SSH_FXP_RMDIR, process_rmdir},
+ {"realpath", NULL, SSH_FXP_REALPATH, process_realpath},
+ {"stat", NULL, SSH_FXP_STAT, process_stat},
+ {"rename", NULL, SSH_FXP_RENAME, process_unsupposed},
+ {"readlink", NULL, SSH_FXP_READLINK, process_readlink},
+ {"symlink", NULL, SSH_FXP_SYMLINK, process_symlink},
+ {"init", NULL, SSH_FXP_INIT, sftp_reply_version},
+ {NULL, NULL, 0, NULL},
+};
+
+const struct sftp_message_handler extended_handlers[] = {
+ /* here are some extended type handlers */
+ {"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
+ {NULL, NULL, 0, NULL},
+};
+
+static int
+process_open(sftp_client_message client_msg)
+{
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ uint32_t msg_flag = sftp_client_message_get_flags(client_msg);
+ uint32_t mode = client_msg->attr->permissions;
+ ssh_string handle_s = NULL;
+ struct sftp_handle *h = NULL;
+ int file_flag;
+ int fd = -1;
+ int status;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing open: filename %s, mode=0%o" PRIu32,
+ filename, mode);
+
+ if (((msg_flag & (uint32_t)SSH_FXF_READ) == SSH_FXF_READ) &&
+ ((msg_flag & (uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE)) {
+ file_flag = O_RDWR; // file must exist
+ if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
+ file_flag |= O_CREAT;
+ } else if ((msg_flag & (uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE) {
+ file_flag = O_WRONLY;
+ if ((msg_flag & (uint32_t)SSH_FXF_APPEND) == SSH_FXF_APPEND)
+ file_flag |= O_APPEND;
+ if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
+ file_flag |= O_CREAT;
+ } else if ((msg_flag & (uint32_t)SSH_FXF_READ) == SSH_FXF_READ) {
+ file_flag = O_RDONLY;
+ } else {
+ SSH_LOG(SSH_LOG_PROTOCOL, "undefined message flag: %" PRIu32, msg_flag);
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, "Flag error");
+ return SSH_ERROR;
+ }
+
+ fd = open(filename, file_flag, mode);
+ if (fd == -1) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "error open file with error: %s",
+ strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, "Write error");
+ return SSH_ERROR;
+ }
+
+ h = calloc(1, sizeof (struct sftp_handle));
+ if (h == NULL) {
+ close(fd);
+ SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE,
+ "Failed to allocate new handle");
+ return SSH_ERROR;
+ }
+ h->fd = fd;
+ h->type = SFTP_FILE_HANDLE;
+ handle_s = sftp_handle_alloc(client_msg->sftp, h);
+ if (handle_s != NULL) {
+ sftp_reply_handle(client_msg, handle_s);
+ ssh_string_free(handle_s);
+ } else {
+ close(fd);
+ SSH_LOG(SSH_LOG_PROTOCOL, "Failed to allocate handle");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE,
+ "Failed to allocate handle");
+ }
+
+ return SSH_OK;
+}
+
+static int
+process_read(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ ssize_t allreadn = 0;
+ int fd = -1;
+ char *buffer = NULL;
+ int rv;
+
+ ssh_log_hexdump("Processing read: handle:",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, handle);
+ if (h->type == SFTP_FILE_HANDLE) {
+ fd = h->fd;
+ }
+
+ if (fd < 0) {
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "invalid fd (%d) received from handle", fd);
+ return SSH_ERROR;
+ }
+ rv = lseek(fd, client_msg->offset, SEEK_SET);
+ if (rv == -1) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL,
+ "error seeking file fd: %d at offset: %" PRIu64,
+ fd, client_msg->offset);
+ return SSH_ERROR;
+ }
+
+ buffer = malloc(client_msg->len);
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "Failed to allocate memory for read data");
+ return SSH_ERROR;
+ }
+ do {
+ ssize_t readn = read(fd, buffer + allreadn, client_msg->len - allreadn);
+ if (readn < 0) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "read file error!");
+ free(buffer);
+ return SSH_ERROR;
+ } else if (readn == 0) {
+ /* no more data to read, EOF ? */
+ break;
+ }
+ allreadn += readn;
+ } while (allreadn < (ssize_t)client_msg->len);
+
+ if (allreadn > 0) {
+ sftp_reply_data(client_msg, buffer, allreadn);
+ } else {
+ sftp_reply_status(client_msg, SSH_FX_EOF, NULL);
+ }
+
+ free(buffer);
+ return SSH_OK;
+}
+
+static int
+process_write(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ ssize_t written = 0;
+ int fd = -1;
+ const char *msg_data = NULL;
+ uint32_t len;
+ int rv;
+
+ ssh_log_hexdump("Processing write: handle",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, handle);
+ if (h->type == SFTP_FILE_HANDLE) {
+ fd = h->fd;
+ }
+ if (fd < 0) {
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "write file fd error!");
+ return SSH_ERROR;
+ }
+
+ msg_data = ssh_string_get_char(client_msg->data);
+ len = ssh_string_len(client_msg->data);
+
+ rv = lseek(fd, client_msg->offset, SEEK_SET);
+ if (rv == -1) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file at offset: %" PRIu64,
+ client_msg->offset);
+ }
+ do {
+ rv = write(fd, msg_data + written, len - written);
+ if (rv < 0) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error");
+ SSH_LOG(SSH_LOG_PROTOCOL, "file write error!");
+ return SSH_ERROR;
+ }
+ written += rv;
+ } while (written < (int)len);
+
+ sftp_reply_status(client_msg, SSH_FX_OK, NULL);
+
+ return SSH_OK;
+}
+
+static int
+process_close(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ int ret;
+
+ ssh_log_hexdump("Processing close: handle:",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, handle);
+ if (h->type == SFTP_FILE_HANDLE) {
+ int fd = h->fd;
+ close(fd);
+ ret = SSH_OK;
+ } else if (h->type == SFTP_DIR_HANDLE) {
+ DIR *dir = h->dirp;
+ closedir(dir);
+ ret = SSH_OK;
+ } else {
+ ret = SSH_ERROR;
+ }
+ SAFE_FREE(h->name);
+ sftp_handle_remove(sftp, h);
+ SAFE_FREE(h);
+
+ if (ret == SSH_OK) {
+ sftp_reply_status(client_msg, SSH_FX_OK, NULL);
+ } else {
+ SSH_LOG(SSH_LOG_PROTOCOL, "closing file failed");
+ sftp_reply_status(client_msg, SSH_FX_BAD_MESSAGE, "Invalid handle");
+ }
+
+ return SSH_OK;
+}
+
+static int
+process_opendir(sftp_client_message client_msg)
+{
+ DIR *dir = NULL;
+ const char *dir_name = sftp_client_message_get_filename(client_msg);
+ ssh_string handle_s = NULL;
+ struct sftp_handle *h = NULL;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing opendir %s", dir_name);
+
+ dir = opendir(dir_name);
+ if (dir == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "No such directory");
+ return SSH_ERROR;
+ }
+
+ h = calloc(1, sizeof (struct sftp_handle));
+ if (h == NULL) {
+ closedir(dir);
+ SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE,
+ "Failed to allocate new handle");
+ return SSH_ERROR;
+ }
+ h->dirp = dir;
+ h->name = strdup(dir_name);
+ h->type = SFTP_DIR_HANDLE;
+ handle_s = sftp_handle_alloc(client_msg->sftp, h);
+
+ if (handle_s != NULL) {
+ sftp_reply_handle(client_msg, handle_s);
+ ssh_string_free(handle_s);
+ } else {
+ closedir(dir);
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
+ }
+
+ return SSH_OK;
+}
+
+static int
+readdir_long_name(char *z_file_name, struct stat *z_st, char *z_long_name)
+{
+ char tmpbuf[MAX_LONG_NAME_LEN];
+ char time[50];
+ char *ptr = z_long_name;
+ int mode = z_st->st_mode;
+
+ *ptr = '\0';
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ *ptr++ = 'd';
+ break;
+ default:
+ *ptr++ = '-';
+ break;
+ }
+
+ /* user */
+ if (mode & 0400)
+ *ptr++ = 'r';
+ else
+ *ptr++ = '-';
+
+ if (mode & 0200)
+ *ptr++ = 'w';
+ else
+ *ptr++ = '-';
+
+ if (mode & 0100) {
+ if (mode & S_ISUID)
+ *ptr++ = 's';
+ else
+ *ptr++ = 'x';
+ } else
+ *ptr++ = '-';
+
+ /* group */
+ if (mode & 040)
+ *ptr++ = 'r';
+ else
+ *ptr++ = '-';
+ if (mode & 020)
+ *ptr++ = 'w';
+ else
+ *ptr++ = '-';
+ if (mode & 010)
+ *ptr++ = 'x';
+ else
+ *ptr++ = '-';
+
+ /* other */
+ if (mode & 04)
+ *ptr++ = 'r';
+ else
+ *ptr++ = '-';
+ if (mode & 02)
+ *ptr++ = 'w';
+ else
+ *ptr++ = '-';
+ if (mode & 01)
+ *ptr++ = 'x';
+ else
+ *ptr++ = '-';
+
+ *ptr++ = ' ';
+ *ptr = '\0';
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "%3d %d %d %d", (int)z_st->st_nlink,
+ (int)z_st->st_uid, (int)z_st->st_gid, (int)z_st->st_size);
+ strcat(z_long_name, tmpbuf);
+
+ ctime_r(&z_st->st_mtime, time);
+ if ((ptr = strchr(time, '\n'))) {
+ *ptr = '\0';
+ }
+ snprintf(tmpbuf, sizeof(tmpbuf), " %s %s", time + 4, z_file_name);
+ strcat(z_long_name, tmpbuf);
+
+ return SSH_OK;
+}
+
+static int
+process_readdir(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ int ret = SSH_OK;
+ int entries = 0;
+ struct dirent *dentry = NULL;
+ DIR *dir = NULL;
+ char long_path[PATH_MAX];
+ int srclen;
+ const char *handle_name = NULL;
+
+ ssh_log_hexdump("Processing readdir: handle",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, client_msg->handle);
+ if (h->type == SFTP_DIR_HANDLE) {
+ dir = h->dirp;
+ handle_name = h->name;
+ }
+ if (dir == NULL) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "got wrong handle from msg");
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+
+ if (handle_name == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+
+ srclen = strlen(handle_name);
+ if (srclen + 2 >= PATH_MAX) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "handle string length exceed max length!");
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+
+ for (int i = 0; i < MAX_ENTRIES_NUM_IN_PACKET; i++) {
+ dentry = readdir(dir);
+
+ if (dentry != NULL) {
+ struct sftp_attributes_struct attr;
+ struct stat st;
+ char long_name[MAX_LONG_NAME_LEN];
+
+ if (strlen(dentry->d_name) + srclen + 1 >= PATH_MAX) {
+ SSH_LOG(SSH_LOG_PROTOCOL,
+ "handle string length exceed max length!");
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+ snprintf(long_path, PATH_MAX, "%s/%s", handle_name, dentry->d_name);
+
+ if (lstat(long_path, &st) == 0) {
+ stat_to_filexfer_attrib(&st, &attr);
+ } else {
+ clear_filexfer_attrib(&attr);
+ }
+
+ if (readdir_long_name(dentry->d_name, &st, long_name) == 0) {
+ sftp_reply_names_add(client_msg, dentry->d_name, long_name, &attr);
+ } else {
+ printf("readdir long name error\n");
+ }
+
+ entries++;
+ } else {
+ break;
+ }
+ }
+
+ if (entries > 0) {
+ ret = sftp_reply_names(client_msg);
+ } else {
+ sftp_reply_status(client_msg, SSH_FX_EOF, NULL);
+ }
+
+ return ret;
+}
+
+static int
+process_mkdir(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ uint32_t msg_flags = client_msg->attr->flags;
+ uint32_t permission = client_msg->attr->permissions;
+ uint32_t mode = (msg_flags & (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS)
+ ? permission & (uint32_t)07777 : 0777;
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing mkdir %s, mode=0%o" PRIu32,
+ filename, mode);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = mkdir(filename, mode);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "failed to mkdir: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ ret = SSH_ERROR;
+ }
+
+ sftp_reply_status(client_msg, status, NULL);
+
+ return ret;
+}
+
+static int
+process_rmdir(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing rmdir %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = rmdir(filename);
+ if (rv < 0) {
+ status = unix_errno_to_ssh_stat(errno);
+ ret = SSH_ERROR;
+ }
+
+ sftp_reply_status(client_msg, status, NULL);
+
+ return ret;
}
-void sftp_handle_remove(sftp_session sftp, void *handle) {
- int i;
+static int
+process_realpath(sftp_client_message client_msg)
+{
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ char *path = NULL;
- for (i = 0; i < SFTP_HANDLES; i++) {
- if (sftp->handles[i] == handle) {
- sftp->handles[i] = NULL;
- break;
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing realpath %s", filename);
+
+ if (filename[0] == '\0') {
+ path = realpath(".", NULL);
+ } else {
+ path = realpath(filename, NULL);
+ }
+ if (path == NULL) {
+ int saved_errno = errno;
+ int status = unix_errno_to_ssh_stat(saved_errno);
+ const char *err_msg = ssh_str_error(status);
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "realpath failed: %s", strerror(saved_errno));
+ sftp_reply_status(client_msg, status, err_msg);
+ return SSH_ERROR;
+ }
+ sftp_reply_name(client_msg, path, NULL);
+ free(path);
+ return SSH_OK;
+}
+
+static int
+process_lstat(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ struct sftp_attributes_struct attr;
+ struct stat st;
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing lstat %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = lstat(filename, &st);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "lstat failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, NULL);
+ ret = SSH_ERROR;
+ } else {
+ stat_to_filexfer_attrib(&st, &attr);
+ sftp_reply_attr(client_msg, &attr);
+ }
+
+ return ret;
+}
+
+static int
+process_stat(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ struct sftp_attributes_struct attr;
+ struct stat st;
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing stat %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = stat(filename, &st);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "lstat failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, NULL);
+ ret = SSH_ERROR;
+ } else {
+ stat_to_filexfer_attrib(&st, &attr);
+ sftp_reply_attr(client_msg, &attr);
+ }
+
+ return ret;
+}
+
+static int
+process_readlink(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ char buf[PATH_MAX];
+ int len = -1;
+ const char *err_msg;
+ int status = SSH_FX_OK;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing readlink %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ len = readlink(filename, buf, sizeof(buf) - 1);
+ if (len < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "readlink failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ err_msg = ssh_str_error(status);
+ sftp_reply_status(client_msg, status, err_msg);
+ ret = SSH_ERROR;
+ } else {
+ buf[len] = '\0';
+ sftp_reply_name(client_msg, buf, NULL);
+ }
+
+ return ret;
+}
+
+/* Note, that this function is using reversed order of the arguments than the
+ * OpenSSH sftp server as they have the arguments switched. See
+ * section "4.1 sftp: Reversal of arguments to SSH_FXP_SYMLINK' in
+ * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
+ * for more information */
+static int
+process_symlink(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *destpath = sftp_client_message_get_filename(client_msg);
+ const char *srcpath = ssh_string_get_char(client_msg->data);
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing symlink: src=%s dest=%s",
+ srcpath, destpath);
+
+ if (srcpath == NULL || destpath == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = symlink(srcpath, destpath);
+ if (rv < 0) {
+ int saved_errno = errno;
+ status = unix_errno_to_ssh_stat(saved_errno);
+ SSH_LOG(SSH_LOG_PROTOCOL, "symlink failed: %s", strerror(saved_errno));
+ sftp_reply_status(client_msg, status, "Write error");
+ ret = SSH_ERROR;
+ } else {
+ sftp_reply_status(client_msg, SSH_FX_OK, "write success");
+ }
+
+ return ret;
+}
+
+static int
+process_remove(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ int rv;
+ int status = SSH_FX_OK;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing remove: %s", filename);
+
+ rv = unlink(filename);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "unlink failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ ret = SSH_ERROR;
+ }
+
+ sftp_reply_status(client_msg, status, NULL);
+
+ return ret;
+}
+
+static int
+process_unsupposed(sftp_client_message client_msg)
+{
+ sftp_reply_status(client_msg, SSH_FX_OP_UNSUPPORTED,
+ "Operation not supported");
+ SSH_LOG(SSH_LOG_PROTOCOL, "Message type %d not implemented",
+ sftp_client_message_get_type(client_msg));
+ return SSH_OK;
+}
+
+static int
+process_extended_statvfs(sftp_client_message client_msg)
+{
+ const char *path = sftp_client_message_get_filename(client_msg);
+ sftp_statvfs_t sftp_statvfs;
+ struct statvfs st;
+ uint64_t flag;
+ int status;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing extended statvfs: %s", path);
+
+ rv = statvfs(path, &st);
+ if (rv != 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "statvfs failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, NULL);
+ return SSH_ERROR;
+ }
+
+ sftp_statvfs = calloc(1, sizeof(struct sftp_statvfs_struct));
+ if (sftp_statvfs == NULL) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "Failed to allocate statvfs structure");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ return SSH_ERROR;
+ }
+ flag = (st.f_flag & ST_RDONLY) ? SSH_FXE_STATVFS_ST_RDONLY : 0;
+ flag |= (st.f_flag & ST_NOSUID) ? SSH_FXE_STATVFS_ST_NOSUID : 0;
+
+ sftp_statvfs->f_bsize = st.f_bsize;
+ sftp_statvfs->f_frsize = st.f_frsize;
+ sftp_statvfs->f_blocks = st.f_blocks;
+ sftp_statvfs->f_bfree = st.f_bfree;
+ sftp_statvfs->f_bavail = st.f_bavail;
+ sftp_statvfs->f_files = st.f_files;
+ sftp_statvfs->f_ffree = st.f_ffree;
+ sftp_statvfs->f_favail = st.f_favail;
+ sftp_statvfs->f_fsid = st.f_fsid;
+ sftp_statvfs->f_flag = flag;
+ sftp_statvfs->f_namemax = st.f_namemax;
+
+ rv = sftp_reply_statvfs(client_msg, sftp_statvfs);
+ free(sftp_statvfs);
+ if (rv == 0) {
+ return SSH_OK;
+ }
+ return SSH_ERROR;
+}
+
+static int
+process_extended(sftp_client_message sftp_msg)
+{
+ int status = SSH_ERROR;
+ const char *subtype = sftp_msg->submessage;
+ sftp_server_message_callback handler = NULL;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing extended message: %s", subtype);
+
+ for (int i = 0; extended_handlers[i].cb != NULL; i++) {
+ if (strcmp(subtype, extended_handlers[i].extended_name) == 0) {
+ handler = extended_handlers[i].cb;
+ break;
+ }
+ }
+ if (handler != NULL) {
+ status = handler(sftp_msg);
+ return status;
+ }
+
+ sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED,
+ "Extended Operation not supported");
+ SSH_LOG(SSH_LOG_PROTOCOL, "Extended Message type %s not implemented",
+ subtype);
+ return SSH_OK;
+}
+
+static int
+dispatch_sftp_request(sftp_client_message sftp_msg)
+{
+ int status = SSH_ERROR;
+ sftp_server_message_callback handler = NULL;
+ uint8_t type = sftp_client_message_get_type(sftp_msg);
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing request type: %u", type);
+
+ for (int i = 0; message_handlers[i].cb != NULL; i++) {
+ if (type == message_handlers[i].type) {
+ handler = message_handlers[i].cb;
+ break;
+ }
+ }
+
+ if (handler != NULL) {
+ status = handler(sftp_msg);
+ } else {
+ sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED,
+ "Operation not supported");
+ SSH_LOG(SSH_LOG_PROTOCOL, "Message type %u not implemented", type);
+ return SSH_OK;
+ }
+
+ return status;
+}
+
+static int
+process_client_message(sftp_client_message client_msg)
+{
+ int status = SSH_OK;
+ if (client_msg == NULL) {
+ return SSH_ERROR;
+ }
+
+ switch (client_msg->type) {
+ case SSH_FXP_EXTENDED:
+ status = process_extended(client_msg);
+ break;
+ default:
+ status = dispatch_sftp_request(client_msg);
+ }
+
+ if (status != SSH_OK)
+ SSH_LOG(SSH_LOG_PROTOCOL,
+ "error occurred during processing client message!");
+
+ return status;
+}
+
+/**
+ * @brief Default subsystem request handler for SFTP subsystem
+ *
+ * @param[in] session The ssh session
+ * @param[in] channel The existing ssh channel
+ * @param[in] subsystem The subsystem name. Only "sftp" is handled
+ * @param[out] userdata The pointer to sftp_session which will get the
+ * resulting SFTP session
+ *
+ * @return SSH_OK when the SFTP server was successfully initialized, SSH_ERROR
+ * otherwise.
+ */
+int
+sftp_channel_default_subsystem_request(ssh_session session,
+ ssh_channel channel,
+ const char *subsystem,
+ void *userdata)
+{
+ if (strcmp(subsystem, "sftp") == 0) {
+ sftp_session *sftp = (sftp_session *)userdata;
+
+ /* initialize sftp session and file handler */
+ *sftp = sftp_server_new(session, channel);
+ if (*sftp == NULL) {
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+ }
+ return SSH_ERROR;
+}
+
+/**
+ * @brief Default data callback for sftp server
+ *
+ * @param[in] session The ssh session
+ * @param[in] channel The ssh channel with SFTP session opened
+ * @param[in] data The data to be processed.
+ * @param[in] len The length of input data to be processed
+ * @param[in] is_stderr Unused channel flag for stderr flagging
+ * @param[in] userdata The pointer to sftp_session
+ *
+ * @return number of bytes processed, -1 when error occurs.
+ */
+int
+sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
+ UNUSED_PARAM(ssh_channel channel),
+ void *data,
+ uint32_t len,
+ UNUSED_PARAM(int is_stderr),
+ void *userdata)
+{
+ sftp_session *sftpp = (sftp_session *)userdata;
+ sftp_session sftp = NULL;
+ sftp_client_message msg;
+ int decode_len;
+ int rc;
+
+ if (sftpp == NULL) {
+ SSH_LOG(SSH_LOG_WARNING, "NULL userdata passed to callback");
+ return -1;
}
- }
+ sftp = *sftpp;
+
+ decode_len = sftp_decode_channel_data_to_packet(sftp, data, len);
+ if (decode_len == -1)
+ return -1;
+
+ msg = sftp_get_client_message_from_packet(sftp);
+ rc = process_client_message(msg);
+ sftp_client_message_free(msg);
+ if (rc != SSH_OK)
+ SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
+
+ return decode_len;
}
+#else
+/* Not available on Windows for now */
+int
+sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
+ UNUSED_PARAM(ssh_channel channel),
+ UNUSED_PARAM(void *data),
+ UNUSED_PARAM(uint32_t len),
+ UNUSED_PARAM(int is_stderr),
+ UNUSED_PARAM(void *userdata))
+{
+ return -1;
+}
+
+int
+sftp_channel_default_subsystem_request(UNUSED_PARAM(ssh_session session),
+ UNUSED_PARAM(ssh_channel channel),
+ UNUSED_PARAM(const char *subsystem),
+ UNUSED_PARAM(void *userdata))
+{
+ return SSH_ERROR;
+}
+#endif
diff --git a/src/socket.c b/src/socket.c
index bc18f382..9dc4cbdd 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -28,17 +28,15 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
-#if _MSC_VER >= 1400
-#include <io.h>
-#undef open
-#define open _open
-#undef close
-#define close _close
-#undef read
-#define read _read
-#undef write
-#define write _write
-#endif /* _MSC_VER */
+#ifndef UNIX_PATH_MAX
+ /* Inlining the key portions of afunix.h in Windows 10 SDK;
+ * that header isn't available in the mingw environment. */
+#define UNIX_PATH_MAX 108
+struct sockaddr_un {
+ ADDRESS_FAMILY sun_family;
+ char sun_path[UNIX_PATH_MAX];
+};
+#endif
#else /* _WIN32 */
#include <fcntl.h>
#include <sys/types.h>
@@ -56,8 +54,6 @@
#include "libssh/session.h"
/**
- * @internal
- *
* @defgroup libssh_socket The SSH socket functions.
* @ingroup libssh
*
@@ -214,6 +210,15 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
s->callbacks = callbacks;
}
+void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p)
+{
+ s->state = SSH_SOCKET_CONNECTED;
+ /* POLLOUT is the event to wait for in a nonblocking connect */
+ if (p != NULL) {
+ ssh_poll_set_events(p, POLLIN | POLLOUT);
+ }
+}
+
/**
* @brief SSH poll callback. This callback will be used when an event
* caught on the socket.
@@ -221,7 +226,7 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
* @param p Poll object this callback belongs to.
* @param fd The raw socket.
* @param revents The current poll events on the socket.
- * @param userdata Userdata to be passed to the callback function,
+ * @param v_s Userdata to be passed to the callback function,
* in this case the socket object.
*
* @return 0 on success, < 0 when the poll object has been removed
@@ -243,7 +248,8 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
if (!ssh_socket_is_open(s)) {
return -1;
}
- SSH_LOG(SSH_LOG_TRACE, "Poll callback on socket %d (%s%s%s), out buffer %d",fd,
+ SSH_LOG(SSH_LOG_TRACE,
+ "Poll callback on socket %d (%s%s%s), out buffer %" PRIu32, fd,
(revents & POLLIN) ? "POLLIN ":"",
(revents & POLLOUT) ? "POLLOUT ":"",
(revents & POLLERR) ? "POLLERR":"",
@@ -314,12 +320,13 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
/* Call the callback */
if (s->callbacks != NULL && s->callbacks->data != NULL) {
+ size_t processed;
do {
- nread = s->callbacks->data(ssh_buffer_get(s->in_buffer),
- ssh_buffer_get_len(s->in_buffer),
- s->callbacks->userdata);
- ssh_buffer_pass_bytes(s->in_buffer, nread);
- } while ((nread > 0) && (s->state == SSH_SOCKET_CONNECTED));
+ processed = s->callbacks->data(ssh_buffer_get(s->in_buffer),
+ ssh_buffer_get_len(s->in_buffer),
+ s->callbacks->userdata);
+ ssh_buffer_pass_bytes(s->in_buffer, processed);
+ } while ((processed > 0) && (s->state == SSH_SOCKET_CONNECTED));
/* p may have been freed, so don't use it
* anymore in this function */
@@ -336,10 +343,7 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
/* First, POLLOUT is a sign we may be connected */
if (s->state == SSH_SOCKET_CONNECTING) {
SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
- s->state = SSH_SOCKET_CONNECTED;
- if (p != NULL) {
- ssh_poll_set_events(p, POLLOUT | POLLIN);
- }
+ ssh_socket_set_connected(s, p);
rc = ssh_socket_set_blocking(ssh_socket_get_fd(s));
if (rc < 0) {
@@ -367,7 +371,7 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
ssh_socket_nonblocking_flush(s);
} else if (s->callbacks != NULL && s->callbacks->controlflow != NULL) {
/* Otherwise advertise the upper level that write can be done */
- SSH_LOG(SSH_LOG_TRACE,"sending control flow event");
+ SSH_LOG(SSH_LOG_TRACE, "sending control flow event");
s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
s->callbacks->userdata);
}
@@ -392,7 +396,7 @@ ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
if (s->poll_handle) {
return s->poll_handle;
}
- s->poll_handle = ssh_poll_new(s->fd,0,ssh_socket_pollcallback,s);
+ s->poll_handle = ssh_poll_new(s->fd, 0, ssh_socket_pollcallback, s);
return s->poll_handle;
}
@@ -410,10 +414,10 @@ void ssh_socket_free(ssh_socket s)
SAFE_FREE(s);
}
-#ifndef _WIN32
int ssh_socket_unix(ssh_socket s, const char *path)
{
struct sockaddr_un sunaddr;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
socket_t fd;
sunaddr.sun_family = AF_UNIX;
snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
@@ -422,28 +426,30 @@ int ssh_socket_unix(ssh_socket s, const char *path)
if (fd == SSH_INVALID_SOCKET) {
ssh_set_error(s->session, SSH_FATAL,
"Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
+#ifndef _WIN32
if (fcntl(fd, F_SETFD, 1) == -1) {
ssh_set_error(s->session, SSH_FATAL,
"Error from fcntl(fd, F_SETFD, 1): %s",
- strerror(errno));
- close(fd);
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
return -1;
}
+#endif
if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
- ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
- strerror(errno));
- close(fd);
+ ssh_set_error(s->session, SSH_FATAL, "Error from connect(%s): %s",
+ path,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
return -1;
}
ssh_socket_set_fd(s,fd);
return 0;
}
-#endif
/** \internal
* \brief closes a socket
@@ -477,12 +483,14 @@ void ssh_socket_close(ssh_socket s)
kill(pid, SIGTERM);
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
- SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s", strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ SSH_LOG(SSH_LOG_TRACE, "waitpid failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return;
}
}
if (!WIFEXITED(status)) {
- SSH_LOG(SSH_LOG_WARN, "Proxy command exitted abnormally");
+ SSH_LOG(SSH_LOG_TRACE, "Proxy command exited abnormally");
return;
}
SSH_LOG(SSH_LOG_TRACE, "Proxy command returned %d", WEXITSTATUS(status));
@@ -495,22 +503,28 @@ void ssh_socket_close(ssh_socket s)
* @brief sets the file descriptor of the socket.
* @param[out] s ssh_socket to update
* @param[in] fd file descriptor to set
- * @warning this function updates boths the input and output
+ * @warning this function updates both the input and output
* file descriptors
*/
void ssh_socket_set_fd(ssh_socket s, socket_t fd)
{
+ ssh_poll_handle h = NULL;
+
s->fd = fd;
if (s->poll_handle) {
ssh_poll_set_fd(s->poll_handle,fd);
} else {
s->state = SSH_SOCKET_CONNECTING;
+ h = ssh_socket_get_poll_handle(s);
+ if (h == NULL) {
+ return;
+ }
/* POLLOUT is the event to wait for in a nonblocking connect */
- ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLOUT);
+ ssh_poll_set_events(h, POLLOUT);
#ifdef _WIN32
- ssh_poll_add_events(ssh_socket_get_poll_handle(s), POLLWRNORM);
+ ssh_poll_add_events(h, POLLWRNORM);
#endif
}
}
@@ -544,9 +558,9 @@ static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
return -1;
}
if (s->fd_is_socket) {
- rc = recv(s->fd,buffer, len, 0);
+ rc = recv(s->fd, buffer, len, 0);
} else {
- rc = read(s->fd,buffer, len);
+ rc = read(s->fd, buffer, len);
}
#ifdef _WIN32
s->last_errno = WSAGetLastError();
@@ -557,6 +571,8 @@ static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
if (rc < 0) {
s->data_except = 1;
+ } else {
+ SSH_LOG(SSH_LOG_TRACE, "read %zd", rc);
}
return rc;
@@ -594,12 +610,13 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
/* Reactive the POLLOUT detector in the poll multiplexer system */
if (s->poll_handle) {
SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket");
- ssh_poll_set_events(s->poll_handle,ssh_poll_get_events(s->poll_handle) | POLLOUT);
+ ssh_poll_add_events(s->poll_handle, POLLOUT);
}
if (w < 0) {
s->data_except = 1;
}
+ SSH_LOG(SSH_LOG_TRACE, "wrote %zd", w);
return w;
}
@@ -638,7 +655,7 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
* \returns SSH_OK, or SSH_ERROR
* \warning has no effect on socket before a flush
*/
-int ssh_socket_write(ssh_socket s, const void *buffer, int len)
+int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
{
if (len > 0) {
if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
@@ -668,11 +685,12 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
s->last_errno,
s->callbacks->userdata);
} else {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(session,
SSH_FATAL,
"Writing packet: error on socket (or connection "
"closed): %s",
- strerror(s->last_errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
}
return SSH_ERROR;
@@ -701,11 +719,12 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
s->last_errno,
s->callbacks->userdata);
} else {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(session,
SSH_FATAL,
"Writing packet: error on socket (or connection "
"closed): %s",
- strerror(s->last_errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
}
return SSH_ERROR;
@@ -720,6 +739,8 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
/* Is there some data pending? */
len = ssh_buffer_get_len(s->out_buffer);
if (s->poll_handle && len > 0) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "did not send all the data, queuing pollout event");
/* force the poll system to catch pollout events */
ssh_poll_add_events(s->poll_handle, POLLOUT);
@@ -829,7 +850,7 @@ int ssh_socket_set_blocking(socket_t fd)
/**
* @internal
* @brief Launches a socket connection
- * If a the socket connected callback has been defined and
+ * If the socket connected callback has been defined and
* a poll object exists, this call will be non blocking.
* @param s socket to connect.
* @param host hostname or ip address to connect to.
@@ -837,8 +858,6 @@ int ssh_socket_set_blocking(socket_t fd)
* @param bind_addr address to bind to, or NULL for default.
* @returns SSH_OK socket is being connected.
* @returns SSH_ERROR error while connecting to remote host.
- * @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,
@@ -846,19 +865,19 @@ int ssh_socket_connect(ssh_socket s,
const char *bind_addr)
{
socket_t fd;
-
+
if (s->state != SSH_SOCKET_NONE) {
ssh_set_error(s->session, SSH_FATAL,
"ssh_socket_connect called on socket not unconnected");
return SSH_ERROR;
}
fd = ssh_connect_host_nonblocking(s->session, host, bind_addr, port);
- SSH_LOG(SSH_LOG_PROTOCOL, "Nonblocking connection socket: %d", fd);
+ SSH_LOG(SSH_LOG_DEBUG, "Nonblocking connection socket: %d", fd);
if (fd == SSH_INVALID_SOCKET) {
return SSH_ERROR;
}
ssh_socket_set_fd(s,fd);
-
+
return SSH_OK;
}
@@ -876,20 +895,29 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
const char *shell = NULL;
const char *args[] = {NULL/*shell*/, "-c", command, NULL};
int devnull;
+ int rc;
/* Prepare /dev/null socket for the stderr redirection */
devnull = open("/dev/null", O_WRONLY);
if (devnull == -1) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to open /dev/null");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to open /dev/null");
exit(1);
}
- /* By default, use the current users shell */
+ /*
+ * By default, use the current users shell. This could fail with some
+ * shells like zsh or dash ...
+ */
shell = getenv("SHELL");
if (shell == NULL || shell[0] == '\0') {
- /* Fall back to bash. There are issues with dash or
- * whatever people tend to link to /bin/sh */
- shell = "/bin/bash";
+ /* Fall back to the /bin/sh only if the bash is not available. But there are
+ * issues with dash or whatever people tend to link to /bin/sh */
+ rc = access("/bin/bash", 0);
+ if (rc != 0) {
+ shell = "/bin/sh";
+ } else {
+ shell = "/bin/bash";
+ }
}
args[0] = shell;
@@ -900,7 +928,13 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
dup2(devnull, STDERR_FILENO);
close(in);
close(out);
- execv(args[0], (char * const *)args);
+ rc = execv(args[0], (char * const *)args);
+ if (rc < 0) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+
+ SSH_LOG(SSH_LOG_WARN, "Failed to execute command %s: %s",
+ command, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ }
exit(1);
}
@@ -918,6 +952,7 @@ int
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
{
socket_t pair[2];
+ ssh_poll_handle h = NULL;
int pid;
int rc;
@@ -930,7 +965,7 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Executing proxycommand '%s'", command);
+ SSH_LOG(SSH_LOG_DEBUG, "Executing proxycommand '%s'", command);
pid = fork();
if (pid == 0) {
ssh_execute_command(command, pair[0], pair[0]);
@@ -938,12 +973,14 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
}
s->proxy_pid = pid;
close(pair[0]);
- SSH_LOG(SSH_LOG_PROTOCOL, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
+ SSH_LOG(SSH_LOG_DEBUG, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
ssh_socket_set_fd(s, pair[1]);
- s->state=SSH_SOCKET_CONNECTED;
- s->fd_is_socket=0;
- /* POLLOUT is the event to wait for in a nonblocking connect */
- ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLIN | POLLOUT);
+ s->fd_is_socket = 0;
+ h = ssh_socket_get_poll_handle(s);
+ if (h == NULL) {
+ return SSH_ERROR;
+ }
+ ssh_socket_set_connected(s, h);
if (s->callbacks && s->callbacks->connected) {
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata);
}
diff --git a/src/string.c b/src/string.c
index acd3cf48..44403487 100644
--- a/src/string.c
+++ b/src/string.c
@@ -103,7 +103,7 @@ int ssh_string_fill(struct ssh_string_struct *s, const void *data, size_t len) {
* @return The newly allocated string, NULL on error with errno
* set.
*
- * @note The nul byte is not copied nor counted in the ouput string.
+ * @note The null byte is not copied nor counted in the output string.
*/
struct ssh_string_struct *ssh_string_from_char(const char *what) {
struct ssh_string_struct *ptr;
@@ -129,7 +129,7 @@ struct ssh_string_struct *ssh_string_from_char(const char *what) {
/**
* @brief Return the size of a SSH string.
*
- * @param[in] s The the input SSH string.
+ * @param[in] s The input SSH string.
*
* @return The size of the content of the string, 0 on error.
*/
@@ -149,7 +149,7 @@ size_t ssh_string_len(struct ssh_string_struct *s) {
}
/**
- * @brief Get the the string as a C nul-terminated string.
+ * @brief Get the string as a C null-terminated string.
*
* This is only available as long as the SSH string exists.
*
@@ -168,7 +168,7 @@ const char *ssh_string_get_char(struct ssh_string_struct *s)
}
/**
- * @brief Convert a SSH string to a C nul-terminated string.
+ * @brief Convert a SSH string to a C null-terminated string.
*
* @param[in] s The SSH input string.
*
diff --git a/src/threads.c b/src/threads.c
index 792b976d..7660b972 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -20,7 +20,7 @@
*/
/**
- * @defgroup libssh_threads The SSH threading functions.
+ * @defgroup libssh_threads The SSH threading functions
* @ingroup libssh
*
* Threading with libssh
diff --git a/src/threads/libcrypto.c b/src/threads/libcrypto.c
index dac840d3..18951b6a 100644
--- a/src/threads/libcrypto.c
+++ b/src/threads/libcrypto.c
@@ -24,8 +24,6 @@
#include "libssh/threads.h"
#include <libssh/callbacks.h>
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000)
-
int crypto_thread_init(struct ssh_threads_callbacks_struct *cb)
{
(void) cb;
@@ -36,86 +34,3 @@ void crypto_thread_finalize(void)
{
return;
}
-
-#else
-
-static struct ssh_threads_callbacks_struct *user_callbacks = NULL;
-
-static void **libcrypto_mutexes;
-
-void libcrypto_lock_callback(int mode, int i, const char *file, int line);
-
-void libcrypto_lock_callback(int mode, int i, const char *file, int line)
-{
- (void)file;
- (void)line;
-
- if (mode & CRYPTO_LOCK) {
- user_callbacks->mutex_lock(&libcrypto_mutexes[i]);
- } else {
- user_callbacks->mutex_unlock(&libcrypto_mutexes[i]);
- }
-}
-
-static void libcrypto_THREADID_callback(CRYPTO_THREADID *id)
-{
- unsigned long thread_id = (*user_callbacks->thread_id)();
-
- CRYPTO_THREADID_set_numeric(id, thread_id);
-}
-
-int crypto_thread_init(struct ssh_threads_callbacks_struct *cb)
-{
- int n = CRYPTO_num_locks();
- int cmp;
- int i;
-
- if (cb == NULL) {
- return SSH_OK;
- }
-
- if (user_callbacks != NULL) {
- crypto_thread_finalize();
- }
-
- user_callbacks = cb;
-
- cmp = strcmp(user_callbacks->type, "threads_noop");
- if (cmp == 0) {
- return SSH_OK;
- }
-
- libcrypto_mutexes = calloc(n, sizeof(void *));
- if (libcrypto_mutexes == NULL) {
- return SSH_ERROR;
- }
-
- for (i = 0; i < n; ++i){
- user_callbacks->mutex_init(&libcrypto_mutexes[i]);
- }
-
- CRYPTO_THREADID_set_callback(libcrypto_THREADID_callback);
- CRYPTO_set_locking_callback(libcrypto_lock_callback);
-
- return SSH_OK;
-}
-
-void crypto_thread_finalize(void)
-{
- int n = CRYPTO_num_locks();
- int i;
-
- if (libcrypto_mutexes == NULL) {
- return;
- }
-
- CRYPTO_THREADID_set_callback(NULL);
- CRYPTO_set_locking_callback(NULL);
-
- for (i = 0; i < n; ++i) {
- user_callbacks->mutex_destroy(&libcrypto_mutexes[i]);
- }
- SAFE_FREE(libcrypto_mutexes);
-}
-
-#endif
diff --git a/src/token.c b/src/token.c
index 0924d3bd..6ce2b64c 100644
--- a/src/token.c
+++ b/src/token.c
@@ -87,7 +87,7 @@ struct ssh_tokens_st *ssh_tokenize(const char *chain, char separator)
return NULL;
}
- tokens->buffer= strdup(chain);
+ tokens->buffer = strdup(chain);
if (tokens->buffer == NULL) {
goto error;
}
@@ -376,6 +376,7 @@ char *ssh_append_without_duplicates(const char *list,
{
size_t concat_len = 0;
char *ret = NULL, *concat = NULL;
+ int rc = 0;
if (list != NULL) {
concat_len = strlen(list);
@@ -396,12 +397,144 @@ char *ssh_append_without_duplicates(const char *list,
return NULL;
}
+ rc = snprintf(concat, concat_len, "%s%s%s",
+ list == NULL ? "" : list,
+ list == NULL ? "" : ",",
+ appended_list == NULL ? "" : appended_list);
+ if (rc < 0) {
+ SAFE_FREE(concat);
+ return NULL;
+ }
+
+ ret = ssh_remove_duplicates(concat);
+
+ SAFE_FREE(concat);
+
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Given two strings containing lists of tokens, return a newly
+ * allocated string containing the elements of the first list without the
+ * elements of the second list. The order of the elements will be preserved.
+ *
+ * @param[in] list The first list
+ * @param[in] remove_list The list to be removed
+ *
+ * @return A newly allocated copy list containing elements of the
+ * list without the elements of remove_list; NULL in case of error.
+ */
+char *ssh_remove_all_matching(const char *list,
+ const char *remove_list)
+{
+ struct ssh_tokens_st *l_tok = NULL, *r_tok = NULL;
+ int i, j, cmp;
+ char *ret = NULL;
+ size_t len, pos = 0;
+ bool exclude;
+
+ if (list == NULL) {
+ return NULL;
+ }
+ if (remove_list == NULL) {
+ return strdup (list);
+ }
+
+ l_tok = ssh_tokenize(list, ',');
+ if (l_tok == NULL) {
+ goto out;
+ }
+
+ r_tok = ssh_tokenize(remove_list, ',');
+ if (r_tok == NULL) {
+ goto out;
+ }
+
+ ret = calloc(1, strlen(list) + 1);
+ if (ret == NULL) {
+ goto out;
+ }
+
+ for (i = 0; l_tok->tokens[i]; i++) {
+ exclude = false;
+ for (j = 0; r_tok->tokens[j]; j++) {
+ cmp = strcmp(l_tok->tokens[i], r_tok->tokens[j]);
+ if (cmp == 0) {
+ exclude = true;
+ break;
+ }
+ }
+ if (exclude == false) {
+ if (pos != 0) {
+ ret[pos] = ',';
+ pos++;
+ }
+
+ len = strlen(l_tok->tokens[i]);
+ memcpy(&ret[pos], l_tok->tokens[i], len);
+ pos += len;
+ }
+ }
+
+ if (ret[0] == '\0') {
+ SAFE_FREE(ret);
+ }
+
+out:
+ ssh_tokens_free(l_tok);
+ ssh_tokens_free(r_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 prefixed at
+ * the beginning of the second list, without duplicates.
+ *
+ * @param[in] list The first list
+ * @param[in] prefixed_list The list to use as a prefix
+ *
+ * @return A newly allocated list containing all the elements
+ * of the list prefixed with the elements of the prefixed_list without
+ * duplicates; NULL in case of error.
+ */
+char *ssh_prefix_without_duplicates(const char *list,
+ const char *prefixed_list)
+{
+ size_t concat_len = 0;
+ char *ret = NULL, *concat = NULL;
+ int rc = 0;
+
if (list != NULL) {
- strcpy(concat, list);
- strncat(concat, ",", concat_len - strlen(concat) - 1);
+ concat_len = strlen(list);
}
- if (appended_list != NULL) {
- strncat(concat, appended_list, concat_len - strlen(concat) - 1);
+
+ if (prefixed_list != NULL) {
+ concat_len += strlen(prefixed_list);
+ }
+
+ if (concat_len == 0) {
+ return NULL;
+ }
+
+ /* Add room for ending '\0' and for middle ',' */
+ concat_len += 2;
+ concat = calloc(concat_len, 1);
+ if (concat == NULL) {
+ return NULL;
+ }
+
+ rc = snprintf(concat, concat_len, "%s%s%s",
+ prefixed_list == NULL ? "" : prefixed_list,
+ prefixed_list == NULL ? "" : ",",
+ list == NULL ? "" : list);
+ if (rc < 0) {
+ SAFE_FREE(concat);
+ return NULL;
}
ret = ssh_remove_duplicates(concat);
diff --git a/src/ttyopts.c b/src/ttyopts.c
new file mode 100644
index 00000000..f6557884
--- /dev/null
+++ b/src/ttyopts.c
@@ -0,0 +1,449 @@
+/*
+ * ttyopts.c - encoding of TTY modes.
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2023 by Utimaco TS GmbH <oss_committee@utimaco.com>
+ * Author: Daniel Evers <daniel.evers@utimaco.com>
+ *
+ * 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 <stdint.h>
+#include <stdio.h>
+
+#include <libssh/priv.h>
+#include <string.h>
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+/** Terminal mode opcodes */
+enum {
+ TTY_OP_END = 0,
+ TTY_OP_VINTR = 1,
+ TTY_OP_VQUIT = 2,
+ TTY_OP_VERASE = 3,
+ TTY_OP_VKILL = 4,
+ TTY_OP_VEOF = 5,
+ TTY_OP_VEOL = 6,
+ TTY_OP_VEOL2 = 7,
+ TTY_OP_VSTART = 8,
+ TTY_OP_VSTOP = 9,
+ TTY_OP_VSUSP = 10,
+ TTY_OP_VDSUSP = 11,
+ TTY_OP_VREPRINT = 12,
+ TTY_OP_VWERASE = 13,
+ TTY_OP_VLNEXT = 14,
+ TTY_OP_VFLUSH = 15,
+ TTY_OP_VSWTC = 16,
+ TTY_OP_VSTATUS = 17,
+ TTY_OP_VDISCARD = 18,
+ TTY_OP_IGNPAR = 30,
+ TTY_OP_PARMRK = 31,
+ TTY_OP_INPCK = 32,
+ TTY_OP_ISTRIP = 33,
+ TTY_OP_INLCR = 34,
+ TTY_OP_IGNCR = 35,
+ TTY_OP_ICRNL = 36,
+ TTY_OP_IUCLC = 37,
+ TTY_OP_IXON = 38,
+ TTY_OP_IXANY = 39,
+ TTY_OP_IXOFF = 40,
+ TTY_OP_IMAXBEL = 41,
+ TTY_OP_IUTF8 = 42,
+ TTY_OP_ISIG = 50,
+ TTY_OP_ICANON = 51,
+ TTY_OP_XCASE = 52,
+ TTY_OP_ECHO = 53,
+ TTY_OP_ECHOE = 54,
+ TTY_OP_ECHOK = 55,
+ TTY_OP_ECHONL = 56,
+ TTY_OP_NOFLSH = 57,
+ TTY_OP_TOSTOP = 58,
+ TTY_OP_IEXTEN = 59,
+ TTY_OP_ECHOCTL = 60,
+ TTY_OP_ECHOKE = 61,
+ TTY_OP_PENDIN = 62,
+ TTY_OP_OPOST = 70,
+ TTY_OP_OLCUC = 71,
+ TTY_OP_ONLCR = 72,
+ TTY_OP_OCRNL = 73,
+ TTY_OP_ONOCR = 74,
+ TTY_OP_ONLRET = 75,
+ TTY_OP_CS7 = 90,
+ TTY_OP_CS8 = 91,
+ TTY_OP_PARENB = 92,
+ TTY_OP_PARODD = 93,
+ TTY_OP_ISPEED = 128,
+ TTY_OP_OSPEED = 129,
+};
+
+/**
+ * Encodes a single SSH terminal mode option into the buffer.
+ *
+ * @param[in] attr The mode's opcode value.
+ *
+ * @param[in] value The mode's value.
+ *
+ * @param[out] buf Destination buffer to encode into.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes written to the buffer on success, -1 on
+ * error.
+ */
+static int
+encode_termios_opt(unsigned char opcode,
+ uint32_t value,
+ unsigned char *buf,
+ size_t buflen)
+{
+ int offset = 0;
+
+ /* always need 5 bytes */
+ if (buflen < 5) {
+ return -1;
+ }
+
+ /* 1 byte opcode */
+ buf[offset++] = opcode;
+
+ /* 4 bytes value (big endian) */
+ value = htonl(value);
+ memcpy(buf + offset, &value, sizeof(value));
+ offset += sizeof(value);
+
+ return offset;
+}
+
+#ifdef HAVE_TERMIOS_H
+/** Converts a baudrate constant (Bxxxx) to a numeric value. */
+static int
+baud2speed(int baudrate)
+{
+ switch (baudrate) {
+ default:
+ case B0:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+ case B19200:
+ return 19200;
+ case B38400:
+ return 38400;
+ case B57600:
+ return 57600;
+ case B115200:
+ return 115200;
+ case B230400:
+ return 230400;
+ }
+}
+
+/**
+ * Encodes all terminal options from the given \c termios structure
+ * into the buffer.
+ *
+ * @param[in] attr The terminal options to encode.
+ *
+ * @param[out] buf Modes will be encoded into this buffer.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes in the buffer on success, -1 on error.
+ */
+static int
+encode_termios_opts(struct termios *attr, unsigned char *buf, size_t buflen)
+{
+ unsigned int offset = 0;
+ int rc;
+
+#define SSH_ENCODE_OPT(code, value) \
+ rc = encode_termios_opt(code, value, buf + offset, buflen - offset); \
+ if (rc < 0) { \
+ return rc; \
+ } else { \
+ offset += rc; \
+ }
+
+#define SSH_ENCODE_INPUT_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_iflag & opt) ? 1 : 0)
+ SSH_ENCODE_INPUT_OPT(IGNPAR)
+ SSH_ENCODE_INPUT_OPT(PARMRK)
+ SSH_ENCODE_INPUT_OPT(INPCK)
+ SSH_ENCODE_INPUT_OPT(ISTRIP)
+ SSH_ENCODE_INPUT_OPT(INLCR)
+ SSH_ENCODE_INPUT_OPT(IGNCR)
+ SSH_ENCODE_INPUT_OPT(ICRNL)
+#ifdef IUCLC
+ SSH_ENCODE_INPUT_OPT(IUCLC)
+#endif
+ SSH_ENCODE_INPUT_OPT(IXON)
+ SSH_ENCODE_INPUT_OPT(IXANY)
+ SSH_ENCODE_INPUT_OPT(IXOFF)
+ SSH_ENCODE_INPUT_OPT(IMAXBEL)
+#ifdef IUTF8
+ SSH_ENCODE_INPUT_OPT(IUTF8)
+#endif
+#undef SSH_ENCODE_INPUT_OPT
+
+#define SSH_ENCODE_OUTPUT_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_oflag & opt) ? 1 : 0)
+ SSH_ENCODE_OUTPUT_OPT(OPOST)
+#ifdef OLCUC
+ SSH_ENCODE_OUTPUT_OPT(OLCUC)
+#endif
+ SSH_ENCODE_OUTPUT_OPT(ONLCR)
+ SSH_ENCODE_OUTPUT_OPT(OCRNL)
+ SSH_ENCODE_OUTPUT_OPT(ONOCR)
+ SSH_ENCODE_OUTPUT_OPT(ONLRET)
+#undef SSH_ENCODE_OUTPUT_OPT
+
+#define SSH_ENCODE_CONTROL_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_cflag & opt) ? 1 : 0)
+ SSH_ENCODE_CONTROL_OPT(CS7)
+ SSH_ENCODE_CONTROL_OPT(CS8)
+ SSH_ENCODE_CONTROL_OPT(PARENB)
+ SSH_ENCODE_CONTROL_OPT(PARODD)
+#undef SSH_ENCODE_CONTROL_OPT
+
+#define SSH_ENCODE_LOCAL_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_lflag & opt) ? 1 : 0)
+ SSH_ENCODE_LOCAL_OPT(ISIG)
+ SSH_ENCODE_LOCAL_OPT(ICANON)
+#ifdef XCASE
+ SSH_ENCODE_LOCAL_OPT(XCASE)
+#endif
+ SSH_ENCODE_LOCAL_OPT(ECHO)
+ SSH_ENCODE_LOCAL_OPT(ECHOE)
+ SSH_ENCODE_LOCAL_OPT(ECHOK)
+ SSH_ENCODE_LOCAL_OPT(ECHONL)
+ SSH_ENCODE_LOCAL_OPT(NOFLSH)
+ SSH_ENCODE_LOCAL_OPT(TOSTOP)
+ SSH_ENCODE_LOCAL_OPT(IEXTEN)
+ SSH_ENCODE_LOCAL_OPT(ECHOCTL)
+ SSH_ENCODE_LOCAL_OPT(ECHOKE)
+ SSH_ENCODE_LOCAL_OPT(PENDIN)
+#undef SSH_ENCODE_LOCAL_OPT
+
+#define SSH_ENCODE_CC_OPT(opt) SSH_ENCODE_OPT(TTY_OP_##opt, attr->c_cc[opt])
+ SSH_ENCODE_CC_OPT(VINTR)
+ SSH_ENCODE_CC_OPT(VQUIT)
+ SSH_ENCODE_CC_OPT(VERASE)
+ SSH_ENCODE_CC_OPT(VKILL)
+ SSH_ENCODE_CC_OPT(VEOF)
+ SSH_ENCODE_CC_OPT(VEOL)
+ SSH_ENCODE_CC_OPT(VEOL2)
+ SSH_ENCODE_CC_OPT(VSTART)
+ SSH_ENCODE_CC_OPT(VSTOP)
+ SSH_ENCODE_CC_OPT(VSUSP)
+#ifdef VDSUSP
+ SSH_ENCODE_CC_OPT(VDSUSP)
+#endif
+ SSH_ENCODE_CC_OPT(VREPRINT)
+ SSH_ENCODE_CC_OPT(VWERASE)
+ SSH_ENCODE_CC_OPT(VLNEXT)
+#ifdef VFLUSH
+ SSH_ENCODE_CC_OPT(VFLUSH)
+#endif
+#ifdef VSWTC
+ SSH_ENCODE_CC_OPT(VSWTC)
+#endif
+#ifdef VSTATUS
+ SSH_ENCODE_CC_OPT(VSTATUS)
+#endif
+ SSH_ENCODE_CC_OPT(VDISCARD)
+#undef SSH_ENCODE_CC_OPT
+
+ SSH_ENCODE_OPT(TTY_OP_ISPEED, baud2speed(cfgetispeed(attr)))
+ SSH_ENCODE_OPT(TTY_OP_OSPEED, baud2speed(cfgetospeed(attr)))
+#undef SSH_ENCODE_OPT
+
+ /* end of options */
+ if (buflen > offset) {
+ buf[offset++] = TTY_OP_END;
+ } else {
+ return -1;
+ }
+
+ return (int)offset;
+}
+#endif
+
+/**
+ * Encodes a set of default options to ensure "sane" PTY behavior.
+ * This function intentionally doesn't use the \c termios structure
+ * to allow it to work on Windows as well.
+ *
+ * @param[out] buf Modes will be encoded into this buffer.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes in the buffer on success, -1 on error.
+ */
+static int
+encode_default_opts(unsigned char *buf, size_t buflen)
+{
+ unsigned int offset = 0;
+ int rc;
+
+#define SSH_ENCODE_OPT(code, value) \
+ rc = encode_termios_opt(code, value, buf + offset, buflen - offset); \
+ if (rc < 0) { \
+ return rc; \
+ } else { \
+ offset += rc; \
+ }
+
+ SSH_ENCODE_OPT(TTY_OP_VINTR, 003)
+ SSH_ENCODE_OPT(TTY_OP_VQUIT, 034)
+ SSH_ENCODE_OPT(TTY_OP_VERASE, 0177)
+ SSH_ENCODE_OPT(TTY_OP_VKILL, 025)
+ SSH_ENCODE_OPT(TTY_OP_VEOF, 0)
+ SSH_ENCODE_OPT(TTY_OP_VEOL, 0)
+ SSH_ENCODE_OPT(TTY_OP_VEOL2, 0)
+ SSH_ENCODE_OPT(TTY_OP_VSTART, 021)
+ SSH_ENCODE_OPT(TTY_OP_VSTOP, 023)
+ SSH_ENCODE_OPT(TTY_OP_VSUSP, 032)
+ SSH_ENCODE_OPT(TTY_OP_VDSUSP, 031)
+ SSH_ENCODE_OPT(TTY_OP_VREPRINT, 022)
+ SSH_ENCODE_OPT(TTY_OP_VWERASE, 027)
+ SSH_ENCODE_OPT(TTY_OP_VLNEXT, 026)
+ SSH_ENCODE_OPT(TTY_OP_VDISCARD, 017)
+ SSH_ENCODE_OPT(TTY_OP_IGNPAR, 0)
+ SSH_ENCODE_OPT(TTY_OP_PARMRK, 0)
+ SSH_ENCODE_OPT(TTY_OP_INPCK, 0)
+ SSH_ENCODE_OPT(TTY_OP_ISTRIP, 0)
+ SSH_ENCODE_OPT(TTY_OP_INLCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_IGNCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_ICRNL, 0)
+ SSH_ENCODE_OPT(TTY_OP_IUCLC, 0)
+ SSH_ENCODE_OPT(TTY_OP_IXON, 1)
+ SSH_ENCODE_OPT(TTY_OP_IXANY, 0)
+ SSH_ENCODE_OPT(TTY_OP_IXOFF, 0)
+ SSH_ENCODE_OPT(TTY_OP_IMAXBEL, 0)
+ SSH_ENCODE_OPT(TTY_OP_IUTF8, 1)
+ SSH_ENCODE_OPT(TTY_OP_ISIG, 1)
+ SSH_ENCODE_OPT(TTY_OP_ICANON, 1)
+ SSH_ENCODE_OPT(TTY_OP_XCASE, 0)
+ SSH_ENCODE_OPT(TTY_OP_ECHO, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHOE, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHOK, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHONL, 0)
+ SSH_ENCODE_OPT(TTY_OP_NOFLSH, 0)
+ SSH_ENCODE_OPT(TTY_OP_TOSTOP, 0)
+ SSH_ENCODE_OPT(TTY_OP_IEXTEN, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHOCTL, 0)
+ SSH_ENCODE_OPT(TTY_OP_ECHOKE, 1)
+ SSH_ENCODE_OPT(TTY_OP_PENDIN, 0)
+ SSH_ENCODE_OPT(TTY_OP_OPOST, 1)
+ SSH_ENCODE_OPT(TTY_OP_OLCUC, 0)
+ SSH_ENCODE_OPT(TTY_OP_ONLCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_OCRNL, 0)
+ SSH_ENCODE_OPT(TTY_OP_ONOCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_ONLRET, 0)
+ SSH_ENCODE_OPT(TTY_OP_CS7, 1)
+ SSH_ENCODE_OPT(TTY_OP_CS8, 1)
+ SSH_ENCODE_OPT(TTY_OP_PARENB, 0)
+ SSH_ENCODE_OPT(TTY_OP_PARODD, 0)
+ SSH_ENCODE_OPT(TTY_OP_ISPEED, 38400);
+ SSH_ENCODE_OPT(TTY_OP_OSPEED, 38400);
+
+#undef SSH_ENCODE_OPT
+
+ /* end of options */
+ if (buflen > offset) {
+ buf[offset++] = TTY_OP_END;
+ } else {
+ return -1;
+ }
+
+ return (int)offset;
+}
+
+/**
+ * @ingroup libssh_misc
+ *
+ * @brief Encode the current TTY options as SSH modes.
+ *
+ * Call this function to determine the settings of the process' TTY and
+ * encode them as SSH Terminal Modes according to RFC 4254 section 8.
+ *
+ * If STDIN isn't connected to a TTY, this function fills the buffer with
+ * "sane" default modes.
+ *
+ * The encoded modes can be passed to \c ssh_channel_request_pty_size_modes .
+ *
+ * @code
+ * unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
+ * encode_current_tty_opts(modes_buf, sizeof(modes_buf));
+ * @endcode
+ *
+ *
+ * @param[out] buf Modes will be encoded into this buffer.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes in the buffer on success, -1 on error.
+ */
+int
+encode_current_tty_opts(unsigned char *buf, size_t buflen)
+{
+#ifdef HAVE_TERMIOS_H
+ struct termios attr;
+ ZERO_STRUCT(attr);
+
+ if (isatty(STDIN_FILENO)) {
+ /* get local terminal attributes */
+ if (tcgetattr(STDIN_FILENO, &attr) < 0) {
+ perror("tcgetattr");
+ return -1;
+ }
+ return encode_termios_opts(&attr, buf, buflen);
+ }
+#endif
+
+ /* use "sane" default attributes */
+ return encode_default_opts(buf, buflen);
+}
diff --git a/src/wrapper.c b/src/wrapper.c
index bbd4e4b2..bf949ea9 100644
--- a/src/wrapper.c
+++ b/src/wrapper.c
@@ -1,5 +1,5 @@
/*
- * wrapper.c - wrapper for crytpo functions
+ * wrapper.c - wrapper for crypto functions
*
* This file is part of the SSH Library
*
@@ -25,7 +25,7 @@
* Why a wrapper?
*
* Let's say you want to port libssh from libcrypto of openssl to libfoo
- * you are going to spend hours to remove every references to SHA1_Update()
+ * you are going to spend hours removing every reference to SHA1_Update()
* to libfoo_sha1_update after the work is finished, you're going to have
* only this file to modify it's not needed to say that your modifications
* are welcome.
@@ -108,7 +108,7 @@ const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type, bool etm)
}
/* it allocates a new cipher structure based on its offset into the global table */
-static struct ssh_cipher_struct *cipher_new(int offset) {
+static struct ssh_cipher_struct *cipher_new(uint8_t offset) {
struct ssh_cipher_struct *cipher = NULL;
cipher = malloc(sizeof(struct ssh_cipher_struct));
@@ -150,15 +150,16 @@ static void cipher_free(struct ssh_cipher_struct *cipher) {
SAFE_FREE(cipher);
}
-struct ssh_crypto_struct *crypto_new(void) {
- struct ssh_crypto_struct *crypto;
+struct ssh_crypto_struct *crypto_new(void)
+{
+ struct ssh_crypto_struct *crypto;
- crypto = malloc(sizeof(struct ssh_crypto_struct));
- if (crypto == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(crypto);
- return crypto;
+ crypto = malloc(sizeof(struct ssh_crypto_struct));
+ if (crypto == NULL) {
+ return NULL;
+ }
+ ZERO_STRUCTP(crypto);
+ return crypto;
}
void crypto_free(struct ssh_crypto_struct *crypto)
@@ -176,9 +177,13 @@ void crypto_free(struct ssh_crypto_struct *crypto)
#ifdef HAVE_ECDH
SAFE_FREE(crypto->ecdh_client_pubkey);
SAFE_FREE(crypto->ecdh_server_pubkey);
- if(crypto->ecdh_privkey != NULL){
+ if (crypto->ecdh_privkey != NULL) {
#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_KEY_free(crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(crypto->ecdh_privkey);
+#endif /* OPENSSL_VERSION_NUMBER */
#elif defined HAVE_GCRYPT_ECC
gcry_sexp_release(crypto->ecdh_privkey);
#endif
@@ -187,7 +192,7 @@ void crypto_free(struct ssh_crypto_struct *crypto)
#endif
SAFE_FREE(crypto->dh_server_signature);
if (crypto->session_id != NULL) {
- explicit_bzero(crypto->session_id, crypto->digest_len);
+ explicit_bzero(crypto->session_id, crypto->session_id_len);
SAFE_FREE(crypto->session_id);
}
if (crypto->secret_hash != NULL) {
@@ -234,12 +239,35 @@ void crypto_free(struct ssh_crypto_struct *crypto)
SAFE_FREE(crypto);
}
+static void
+compression_enable(ssh_session session,
+ enum ssh_crypto_direction_e direction,
+ bool delayed)
+{
+ /* The delayed compression is turned on AFTER authentication. This means
+ * that we need to turn it on immediately in case of rekeying */
+ if (delayed && !(session->flags & SSH_SESSION_FLAG_AUTHENTICATED)) {
+ if (direction == SSH_DIRECTION_IN) {
+ session->next_crypto->delayed_compress_in = 1;
+ } else { /* SSH_DIRECTION_OUT */
+ session->next_crypto->delayed_compress_out = 1;
+ }
+ } else {
+ if (direction == SSH_DIRECTION_IN) {
+ session->next_crypto->do_compress_in = 1;
+ } else { /* SSH_DIRECTION_OUT */
+ session->next_crypto->do_compress_out = 1;
+ }
+ }
+}
+
static int crypt_set_algorithms2(ssh_session session)
{
const char *wanted = NULL;
+ const char *method = NULL;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
- size_t i = 0;
+ uint8_t i = 0;
int cmp;
/*
@@ -359,22 +387,29 @@ static int crypt_set_algorithms2(ssh_session session)
session->next_crypto->in_hmac = ssh_hmactab[i].hmac_type;
session->next_crypto->in_hmac_etm = ssh_hmactab[i].etm;
- /* compression */
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib");
+ /* compression: client */
+ method = session->next_crypto->kex_methods[SSH_COMP_C_S];
+ cmp = strcmp(method, "zlib");
if (cmp == 0) {
- session->next_crypto->do_compress_out = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S compression");
+ compression_enable(session, SSH_DIRECTION_OUT, false);
}
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib");
+ cmp = strcmp(method, "zlib@openssh.com");
if (cmp == 0) {
- session->next_crypto->do_compress_in = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S delayed compression");
+ compression_enable(session, SSH_DIRECTION_OUT, true);
}
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib@openssh.com");
+
+ method = session->next_crypto->kex_methods[SSH_COMP_S_C];
+ cmp = strcmp(method, "zlib");
if (cmp == 0) {
- session->next_crypto->delayed_compress_out = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling S->C compression");
+ compression_enable(session, SSH_DIRECTION_IN, false);
}
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib@openssh.com");
+ cmp = strcmp(method, "zlib@openssh.com");
if (cmp == 0) {
- session->next_crypto->delayed_compress_in = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling S->C delayed compression");
+ compression_enable(session, SSH_DIRECTION_IN, true);
}
return SSH_OK;
@@ -388,12 +423,11 @@ int crypt_set_algorithms_client(ssh_session session)
#ifdef WITH_SERVER
int crypt_set_algorithms_server(ssh_session session){
const char *method = NULL;
- size_t i = 0;
+ uint8_t i = 0;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
int cmp;
-
if (session == NULL) {
return SSH_ERROR;
}
@@ -512,33 +546,27 @@ int crypt_set_algorithms_server(ssh_session session){
/* compression */
method = session->next_crypto->kex_methods[SSH_COMP_C_S];
- if(strcmp(method,"zlib") == 0){
- SSH_LOG(SSH_LOG_PACKET,"enabling C->S compression");
- session->next_crypto->do_compress_in=1;
+ cmp = strcmp(method, "zlib");
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S compression");
+ compression_enable(session, SSH_DIRECTION_IN, false);
}
- if(strcmp(method,"zlib@openssh.com") == 0){
- SSH_LOG(SSH_LOG_PACKET,"enabling C->S delayed compression");
-
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
- session->next_crypto->do_compress_in = 1;
- } else {
- session->next_crypto->delayed_compress_in = 1;
- }
+ cmp = strcmp(method, "zlib@openssh.com");
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S delayed compression");
+ compression_enable(session, SSH_DIRECTION_IN, true);
}
method = session->next_crypto->kex_methods[SSH_COMP_S_C];
- if(strcmp(method,"zlib") == 0){
+ cmp = strcmp(method, "zlib");
+ if (cmp == 0) {
SSH_LOG(SSH_LOG_PACKET, "enabling S->C compression");
- session->next_crypto->do_compress_out=1;
+ compression_enable(session, SSH_DIRECTION_OUT, false);
}
- if(strcmp(method,"zlib@openssh.com") == 0){
- SSH_LOG(SSH_LOG_PACKET,"enabling S->C delayed compression");
-
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
- session->next_crypto->do_compress_out = 1;
- } else {
- session->next_crypto->delayed_compress_out = 1;
- }
+ cmp = strcmp(method, "zlib@openssh.com");
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_PACKET, "enabling S->C delayed compression");
+ compression_enable(session, SSH_DIRECTION_OUT, true);
}
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 44d4f201..46c19ff7 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,9 +6,7 @@ endif (BSD OR SOLARIS OR OSX)
set(TORTURE_LIBRARY torture)
-include_directories(${OPENSSL_INCLUDE_DIR}
- ${CMOCKA_INCLUDE_DIR}
- ${ZLIB_INCLUDE_DIR}
+include_directories(${CMOCKA_INCLUDE_DIR}
${libssh_BINARY_DIR}/include
${libssh_BINARY_DIR}
${libssh_SOURCE_DIR}/src
@@ -28,10 +26,13 @@ add_library(${TORTURE_LIBRARY}
torture_key.c
torture_pki.c
torture_cmocka.c)
-target_link_libraries(${TORTURE_LIBRARY} ${TORTURE_LINK_LIBRARIES})
+target_link_libraries(${TORTURE_LIBRARY} PRIVATE ${TORTURE_LINK_LIBRARIES})
target_compile_options(${TORTURE_LIBRARY} PRIVATE
-DSSH_PING_EXECUTABLE="${CMAKE_CURRENT_BINARY_DIR}/ssh_ping"
)
+if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(${TORTURE_LIBRARY})
+endif (WITH_COVERAGE)
# The shared version of the library is only useful when client testing is
# enabled
@@ -39,10 +40,6 @@ if (CLIENT_TESTING)
# create shared test library
set(TORTURE_SHARED_LIBRARY torture_shared)
- if (MINGW)
- set(USE_ATTRIBUTE_WEAK "-DUSE_ATTRIBUTE_WEAK")
- endif ()
-
# Create a list of symbols that should be wrapped for override test
set(WRAP_SYMBOLS "")
list(APPEND WRAP_SYMBOLS
@@ -66,20 +63,23 @@ if (CLIENT_TESTING)
torture_pki.c
torture_cmocka.c
)
- target_link_libraries(${TORTURE_SHARED_LIBRARY}
+ target_link_libraries(${TORTURE_SHARED_LIBRARY} PUBLIC
${CMOCKA_LIBRARY}
ssh::static
${WRAP_SYMBOLS}
)
target_compile_options(${TORTURE_SHARED_LIBRARY} PRIVATE
-DSSH_PING_EXECUTABLE="${CMAKE_CURRENT_BINARY_DIR}/ssh_ping"
- ${USE_ATTRIBUTE_WEAK}
+ -DTORTURE_SHARED
)
+ if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(${TORTURE_SHARED_LIBRARY})
+ endif (WITH_COVERAGE)
endif ()
-if (ARGP_LIBRARY)
+if (ARGP_LIBRARIES)
target_link_libraries(${TORTURE_LIBRARY}
- ${ARGP_LIBRARY}
+ PUBLIC ${ARGP_LIBRARIES}
)
endif()
@@ -103,7 +103,7 @@ if (SSH_EXECUTABLE)
set(OPENSSH_CIPHERS "aes128-ctr\naes192-ctr\naes256-ctr\narcfour256\narcfour128\naes128-gcm@openssh.com\naes256-gcm@openssh.com\naes128-cbc\n3des-cbc\nblowfish-cbc\ncast128-cbc\naes192-cbc\naes256-cbc\narcfour\nrijndael-cbc@lysator.liu.se\n")
set(OPENSSH_MACS "hmac-md5-etm@openssh.com\nhmac-sha1-etm@openssh.com\numac-64-etm@openssh.com\numac-128-etm@openssh.com\nhmac-sha2-256-etm@openssh.com\nhmac-sha2-512-etm@openssh.com\nhmac-ripemd160-etm@openssh.com\nhmac-sha1-96-etm@openssh.com\nhmac-md5-96-etm@openssh.com\nhmac-md5\nhmac-sha1\numac-64@openssh.com\numac-128@openssh.com\nhmac-sha2-256\nhmac-sha2-512\nhmac-ripemd160\nhmac-ripemd160@openssh.com\nhmac-sha1-96\nhmac-md5-96\n")
set(OPENSSH_KEX "ecdh-sha2-nistp256\necdh-sha2-nistp384\necdh-sha2-nistp521\ndiffie-hellman-group-exchange-sha256\ndiffie-hellman-group-exchange-sha1\ndiffie-hellman-group14-sha1\ndiffie-hellman-group1-sha1\n")
- set(OPENSSH_KEYS "ssh-rsa\nssh-dss\necdsa-sha2-nistp256\n")
+ set(OPENSSH_KEYS "ssh-rsa\necdsa-sha2-nistp256\n")
else()
execute_process(COMMAND ${SSH_EXECUTABLE} -Q cipher OUTPUT_VARIABLE OPENSSH_CIPHERS)
execute_process(COMMAND ${SSH_EXECUTABLE} -Q mac OUTPUT_VARIABLE OPENSSH_MACS)
@@ -130,9 +130,9 @@ if (SSH_EXECUTABLE)
diffie-hellman-group16-sha512 diffie-hellman-group18-sha512 diffie-hellman-group-exchange-sha1
diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521
curve25519-sha256 curve25519-sha256@libssh.org
- ssh-ed25519 ssh-ed25519-cert-v01@openssh.com ssh-rsa ssh-dss
+ ssh-ed25519 ssh-ed25519-cert-v01@openssh.com ssh-rsa
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
- ssh-rsa-cert-v01@openssh.com ssh-dss-cert-v01@openssh.com
+ ssh-rsa-cert-v01@openssh.com
ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp384-cert-v01@openssh.com
ecdsa-sha2-nistp521-cert-v01@openssh.com
)
@@ -169,13 +169,14 @@ if (CLIENT_TESTING OR SERVER_TESTING)
find_package(nss_wrapper 1.1.2 REQUIRED)
find_package(uid_wrapper 1.2.0 REQUIRED)
find_package(pam_wrapper 1.0.1 REQUIRED)
+ find_package(priv_wrapper 1.0.0)
if (NOT SSHD_EXECUTABLE)
message(SEND_ERROR "Could not find sshd which is required for client testing")
endif()
- find_program(NC_EXECUTABLE
+ find_program(NCAT_EXECUTABLE
NAME
- nc
+ ncat
PATHS
/bin
/usr/bin
@@ -186,6 +187,20 @@ if (CLIENT_TESTING OR SERVER_TESTING)
if (NOT SOFTHSM_FOUND)
message(SEND_ERROR "Could not find softhsm module!")
endif (NOT SOFTHSM_FOUND)
+ find_library(PKCS11SPY
+ NAMES
+ pkcs11-spy.so
+ )
+ if (WITH_PKCS11_PROVIDER)
+ find_package(PkgConfig)
+ if (PKG_CONFIG_FOUND)
+ pkg_check_modules(P11_KIT p11-kit-1)
+ if (P11_KIT_FOUND)
+ pkg_get_variable(P11_MODULE_PATH p11-kit-1 p11_module_path)
+ set(P11_KIT_CLIENT ${P11_MODULE_PATH}/p11-kit-client.so)
+ endif (P11_KIT_FOUND)
+ endif (PKG_CONFIG_FOUND)
+ endif (WITH_PKCS11_PROVIDER)
endif (WITH_PKCS11_URI)
find_program(SSH_EXECUTABLE NAMES ssh)
@@ -216,13 +231,28 @@ if (CLIENT_TESTING OR SERVER_TESTING)
set(WITH_TIMEOUT "1")
endif()
- # chroot_wrapper
- add_library(chroot_wrapper SHARED chroot_wrapper.c)
- set(CHROOT_WRAPPER_LIBRARY ${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}chroot_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX})
+ # For chroot() use priv_wrapper package if found, or internal chroot_wrapper
+ if (priv_wrapper_FOUND)
+ set(CHROOT_WRAPPER "${PRIV_WRAPPER_LIBRARY}")
+ else()
+ add_library(chroot_wrapper SHARED chroot_wrapper.c)
+ set(CHROOT_WRAPPER_LIBRARY ${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}chroot_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX})
+ set(TEST_TARGET_LIBRARIES
+ ${TEST_TARGET_LIBRARIES}
+ chroot_wrapper
+ )
+ set(CHROOT_WRAPPER "${CHROOT_WRAPPER_LIBRARY}")
+ endif()
+
+ # chown wrapper
+ add_library(chown_wrapper SHARED chown_wrapper.c)
+ set(CHOWN_WRAPPER_LIBRARY
+ ${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}chown_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX})
set(TEST_TARGET_LIBRARIES
${TEST_TARGET_LIBRARIES}
- chroot_wrapper
+ chown_wrapper
)
+ set(CHOWN_WRAPPER "${CHOWN_WRAPPER_LIBRARY}")
# ssh_ping
add_executable(ssh_ping ssh_ping.c)
@@ -243,12 +273,19 @@ if (CLIENT_TESTING OR SERVER_TESTING)
configure_file(etc/pam.d/sshd.in ${CMAKE_CURRENT_BINARY_DIR}/etc/pam.d/sshd @ONLY)
- set(TORTURE_ENVIRONMENT "LD_PRELOAD=${SOCKET_WRAPPER_LIBRARY}:${NSS_WRAPPER_LIBRARY}:${UID_WRAPPER_LIBRARY}:${PAM_WRAPPER_LIBRARY}:${CHROOT_WRAPPER_LIBRARY}")
+ set(TORTURE_ENVIRONMENT
+ "LD_PRELOAD=${SOCKET_WRAPPER_LIBRARY}:${NSS_WRAPPER_LIBRARY}:${UID_WRAPPER_LIBRARY}:${PAM_WRAPPER_LIBRARY}:${CHROOT_WRAPPER}:${CHOWN_WRAPPER}")
+ if (priv_wrapper_FOUND)
+ list(APPEND TORTURE_ENVIRONMENT PRIV_WRAPPER=1 PRIV_WRAPPER_CHROOT_DISABLE=1)
+ list(APPEND TORTURE_ENVIRONMENT PRIV_WRAPPER_PRCTL_DISABLE="ALL" PRIV_WRAPPER_SETRLIMIT_DISABLE="ALL")
+ endif()
list(APPEND TORTURE_ENVIRONMENT UID_WRAPPER=1 UID_WRAPPER_ROOT=1)
list(APPEND TORTURE_ENVIRONMENT NSS_WRAPPER_PASSWD=${CMAKE_CURRENT_BINARY_DIR}/etc/passwd)
list(APPEND TORTURE_ENVIRONMENT NSS_WRAPPER_SHADOW=${CMAKE_CURRENT_BINARY_DIR}/etc/shadow)
list(APPEND TORTURE_ENVIRONMENT NSS_WRAPPER_GROUP=${CMAKE_CURRENT_BINARY_DIR}/etc/group)
list(APPEND TORTURE_ENVIRONMENT PAM_WRAPPER_SERVICE_DIR=${CMAKE_CURRENT_BINARY_DIR}/etc/pam.d)
+ list(APPEND TORTURE_ENVIRONMENT LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_SOURCE_DIR}/suppressions/lsan.supp)
+ list(APPEND TORTURE_ENVIRONMENT OPENSSL_ENABLE_SHA1_SIGNATURES=1)
# Give bob some keys
file(COPY keys/id_rsa DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
@@ -285,16 +322,21 @@ if (CLIENT_TESTING OR SERVER_TESTING)
file(READ keys/pkcs11/id_pkcs11_ecdsa_521_openssh.pub CONTENTS)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
- # Copy the signed key to an alternative directory in bob's homedir.
- file(COPY keys/certauth/id_rsa DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
- file(COPY keys/certauth/id_rsa.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
- file(COPY keys/certauth/id_rsa-cert.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
+ # Copy the signed key to an doe's homedir.
+ file(COPY keys/certauth/id_rsa DESTINATION
+ ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
+ file(COPY keys/certauth/id_rsa.pub DESTINATION
+ ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
+ file(COPY keys/certauth/id_rsa-cert.pub DESTINATION
+ ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
+endif ()
+if (WITH_PKCS11_URI)
#Copy the script to setup PKCS11 tokens
file(COPY pkcs11/setup-softhsm-tokens.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/pkcs11 FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
+endif (WITH_PKCS11_URI)
- message(STATUS "TORTURE_ENVIRONMENT=${TORTURE_ENVIRONMENT}")
-endif ()
+message(STATUS "TORTURE_ENVIRONMENT=${TORTURE_ENVIRONMENT}")
configure_file(tests_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/tests_config.h)
@@ -317,3 +359,12 @@ endif ()
if (FUZZ_TESTING)
add_subdirectory(fuzz)
endif()
+
+add_custom_target(test_memcheck
+ # FIXME: The threads_pki_rsa test is skipped under valgrind as it times out
+ # Passing suppression file is also stupid so lets go with override here:
+ # https://stackoverflow.com/a/56116311
+ COMMAND ${CMAKE_CTEST_COMMAND} -E torture_threads_pki_rsa -E pkd_hello_i1
+ --output-on-failure --force-new-ctest-process --test-action memcheck
+ --overwrite MemoryCheckSuppressionFile=${CMAKE_SOURCE_DIR}/tests/valgrind.supp
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
diff --git a/tests/benchmarks/CMakeLists.txt b/tests/benchmarks/CMakeLists.txt
index dbaa4d51..4074b8f4 100644
--- a/tests/benchmarks/CMakeLists.txt
+++ b/tests/benchmarks/CMakeLists.txt
@@ -1,11 +1,17 @@
project(libssh-benchmarks C)
set(benchmarks_SRCS
- bench_scp.c bench_sftp bench_raw.c benchmarks.c latency.c
+ bench_scp.c bench_raw.c benchmarks.c latency.c
)
+if (WITH_SFTP)
+ set(benchmarks_SRCS
+ ${benchmarks_SRCS}
+ bench_sftp.c
+ )
+endif (WITH_SFTP)
include_directories(${libssh_BINARY_DIR})
add_executable(benchmarks ${benchmarks_SRCS})
-target_link_libraries(benchmarks ssh::ssh)
+target_link_libraries(benchmarks ssh::static)
diff --git a/tests/benchmarks/bench1.sh b/tests/benchmarks/bench1.sh
index 4458e9a7..6b3b20f3 100755
--- a/tests/benchmarks/bench1.sh
+++ b/tests/benchmarks/bench1.sh
@@ -1,13 +1,14 @@
+#!/bin/bash
export CIPHER=aes128-cbc
export DEST=localhost
echo "Upload raw SSH statistics"
-echo "local machine: `uname -a`"
-echo "Cipher : $CIPHER ; Destination : $DEST (`ssh $DEST uname -a`)"
-echo "Local ssh version: `ssh -V 2>&1`"
+echo "local machine: $(uname -a)"
+echo "Cipher : $CIPHER ; Destination : $DEST ($(ssh $DEST uname -a))"
+echo "Local ssh version: $(ssh -V 2>&1)"
echo "Ping latency to $DEST":
ping -q -c 1 -n $DEST
-echo "Destination $DEST SSHD vesion : `echo | nc $DEST 22 | head -n1`"
-echo "ssh login latency :`(time -f user:%U ssh $DEST 'id > /dev/null') 2>&1`"
+echo "Destination $DEST SSHD version : $(echo | nc $DEST 22 | head -n1)"
+echo "ssh login latency :$( (command time -f user:%U ssh $DEST 'id > /dev/null') 2>&1)"
./generate.py | dd bs=4096 count=100000 | time ssh -c $CIPHER $DEST "dd bs=4096 of=/dev/null" 2>&1
diff --git a/tests/benchmarks/bench2.sh b/tests/benchmarks/bench2.sh
index 01d67777..cf240fda 100755
--- a/tests/benchmarks/bench2.sh
+++ b/tests/benchmarks/bench2.sh
@@ -1,13 +1,14 @@
+#!/bin/bash
export CIPHER=aes128-cbc
export DEST=localhost
echo "Upload raw SSH statistics"
-echo "local machine: `uname -a`"
-echo "Cipher : $CIPHER ; Destination : $DEST (`ssh $DEST uname -a`)"
-echo "Local ssh version: `samplessh -V 2>&1`"
+echo "local machine: $(uname -a)"
+echo "Cipher : $CIPHER ; Destination : $DEST ($(ssh $DEST uname -a))"
+echo "Local ssh version: $(samplessh -V 2>&1)"
echo "Ping latency to $DEST":
ping -q -c 1 -n $DEST
-echo "Destination $DEST SSHD vesion : `echo | nc $DEST 22 | head -n1`"
-echo "ssh login latency :`(time -f user:%U samplessh $DEST 'id > /dev/null') 2>&1`"
+echo "Destination $DEST SSHD version : $(echo | nc $DEST 22 | head -n1)"
+echo "ssh login latency :$( (command time -f user:%U samplessh $DEST 'id > /dev/null') 2>&1)"
./generate.py | dd bs=4096 count=100000 | strace samplessh -c $CIPHER $DEST "dd bs=4096 of=/dev/null" 2>&1
diff --git a/tests/benchmarks/bench_raw.c b/tests/benchmarks/bench_raw.c
index db1a057c..0fd1446d 100644
--- a/tests/benchmarks/bench_raw.c
+++ b/tests/benchmarks/bench_raw.c
@@ -49,6 +49,9 @@ static char *get_python_eater(unsigned long bytes){
char *ptr;
char buf[12];
+ if (eater == NULL) {
+ return NULL;
+ }
memcpy(eater,python_eater,sizeof(python_eater));
ptr=strstr(eater,"XXXXXXXXXX");
if(!ptr){
@@ -116,7 +119,10 @@ int benchmarks_raw_up (ssh_session session, struct argument_s *args,
unsigned long total=0;
bytes = args->datasize * 1024 * 1024;
- script =get_python_eater(bytes);
+ script = get_python_eater(bytes);
+ if (script == NULL) {
+ return -1;
+ }
err=upload_script(session,"/tmp/eater.py",script);
free(script);
if(err<0)
@@ -205,6 +211,9 @@ static char *get_python_giver(unsigned long bytes){
char *ptr;
char buf[12];
+ if (giver == NULL) {
+ return NULL;
+ }
memcpy(giver,python_giver,sizeof(python_giver));
ptr=strstr(giver,"XXXXXXXXXX");
if(!ptr){
@@ -236,7 +245,10 @@ int benchmarks_raw_down (ssh_session session, struct argument_s *args,
unsigned long total=0;
bytes = args->datasize * 1024 * 1024;
- script =get_python_giver(bytes);
+ script = get_python_giver(bytes);
+ if (script == NULL) {
+ return -1;
+ }
err=upload_script(session,"/tmp/giver.py",script);
free(script);
if(err<0)
diff --git a/tests/benchmarks/bench_sftp.c b/tests/benchmarks/bench_sftp.c
index 601ecec0..e6c68248 100644
--- a/tests/benchmarks/bench_sftp.c
+++ b/tests/benchmarks/bench_sftp.c
@@ -23,6 +23,7 @@
#include "benchmarks.h"
#include <libssh/libssh.h>
#include <libssh/sftp.h>
+#include <libssh/misc.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -180,6 +181,9 @@ int benchmarks_async_sftp_down (ssh_session session, struct argument_s *args,
if(!file)
goto error;
ids = malloc(concurrent_downloads * sizeof(int));
+ if (ids == NULL) {
+ return -1;
+ }
if(args->verbose>0)
fprintf(stdout,"Starting download of %lu bytes now, using %d concurrent downloads\n",bytes,
concurrent_downloads);
@@ -237,3 +241,396 @@ error:
free(ids);
return -1;
}
+
+int benchmarks_async_sftp_aio_down(ssh_session session,
+ struct argument_s *args,
+ float *bps)
+{
+ sftp_session sftp = NULL;
+ sftp_limits_t li = NULL;
+ sftp_file file = NULL;
+ sftp_aio aio = NULL;
+
+ struct ssh_list *aio_queue = NULL;
+
+ int concurrent_downloads = args->concurrent_requests;
+ size_t chunksize;
+ struct timestamp_struct ts = {0};
+ float ms = 0.0f;
+
+ size_t total_bytes = args->datasize * 1024 * 1024;
+ size_t total_bytes_requested = 0, total_bytes_read = 0;
+ size_t bufsize = args->chunksize;
+ size_t to_read;
+ ssize_t bytes_read, bytes_requested;
+ int warned = 0, i, rc;
+
+ sftp = sftp_new(session);
+ if (sftp == NULL) {
+ fprintf(stderr, "Error during sftp aio download: %s\n",
+ ssh_get_error(session));
+ return -1;
+ }
+
+ /*
+ * Errors which are logged in the ssh session are reported after
+ * jumping to the goto label, errors which aren't logged inside the
+ * ssh session are reported before jumping to that label
+ */
+
+ rc = sftp_init(sftp);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
+
+ li = sftp_limits(sftp);
+ if (li == NULL) {
+ goto error;
+ }
+
+ if (args->chunksize > li->max_read_length) {
+ chunksize = li->max_read_length;
+ if (args->verbose > 0) {
+ fprintf(stdout,
+ "Using the chunk size %zu (not the set size %u), "
+ "to respect the max data limit for read packet\n",
+ chunksize, args->chunksize);
+ }
+ } else {
+ chunksize = args->chunksize;
+ }
+
+ file = sftp_open(sftp, SFTPDIR SFTPFILE, O_RDONLY, 0);
+ if (file == NULL) {
+ goto error;
+ }
+
+ aio_queue = ssh_list_new();
+ if (aio_queue == NULL) {
+ fprintf(stderr,
+ "Error during sftp aio download: Insufficient memory\n");
+ goto error;
+ }
+
+ if (args->verbose > 0) {
+ fprintf(stdout,
+ "Starting download of %zu bytes now, "
+ "using %d concurrent downloads.\n",
+ total_bytes, concurrent_downloads);
+ }
+
+ timestamp_init(&ts);
+
+ for (i = 0;
+ i < concurrent_downloads && total_bytes_requested < total_bytes;
+ ++i) {
+ to_read = total_bytes - total_bytes_requested;
+ if (to_read > chunksize) {
+ to_read = chunksize;
+ }
+
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ goto error;
+ }
+
+ if ((size_t)bytes_requested != to_read) {
+ fprintf(stderr,
+ "Error during sftp aio download: sftp_aio_begin_read() "
+ "requesting less bytes even when the number of bytes "
+ "asked to read are within the max limit");
+ sftp_aio_free(aio);
+ goto error;
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ /* enqueue */
+ rc = ssh_list_append(aio_queue, aio);
+ if (rc == SSH_ERROR) {
+ fprintf(stderr,
+ "Error during sftp aio download: Insufficient memory");
+ sftp_aio_free(aio);
+ goto error;
+ }
+ }
+
+ while ((aio = ssh_list_pop_head(sftp_aio, aio_queue)) != NULL) {
+ bytes_read = sftp_aio_wait_read(&aio, buffer, bufsize);
+ if (bytes_read == -1) {
+ goto error;
+ }
+
+ total_bytes_read += (size_t)bytes_read;
+ if (bytes_read == 0) {
+ fprintf(stdout ,
+ "File smaller than expected: %zu bytes (expected %zu).\n",
+ total_bytes_read, total_bytes);
+ break;
+ }
+
+ if (total_bytes_read != total_bytes &&
+ (size_t)bytes_read != chunksize &&
+ warned != 1) {
+ fprintf(stderr,
+ "async_sftp_aio_download: Receiving short reads "
+ "(%zu, expected %zu) before encountering eof, "
+ "the received file will be corrupted and shorted. "
+ "Adapt chunksize to %zu.\n",
+ bytes_read, chunksize, bytes_read);
+ warned = 1;
+ }
+
+ if (total_bytes_requested == total_bytes) {
+ /* No need to issue more requests */
+ continue;
+ }
+
+ /* else issue a request */
+ to_read = total_bytes - total_bytes_requested;
+ if (to_read > chunksize) {
+ to_read = chunksize;
+ }
+
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ goto error;
+ }
+
+ if ((size_t)bytes_requested != to_read) {
+ fprintf(stderr,
+ "Error during sftp aio download: sftp_aio_begin_read() "
+ "requesting less bytes even when the number of bytes "
+ "asked to read are within the max limit");
+ sftp_aio_free(aio);
+ goto error;
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ /* enqueue */
+ rc = ssh_list_append(aio_queue, aio);
+ if (rc == SSH_ERROR) {
+ fprintf(stderr,
+ "Error during sftp aio download: Insufficient memory\n");
+ sftp_aio_free(aio);
+ goto error;
+ }
+ }
+
+ sftp_close(file);
+ ms = elapsed_time(&ts);
+ *bps = (float)(8000 * total_bytes_read) / ms;
+ if (args->verbose > 0) {
+ fprintf(stdout, "Download took %f ms for %zu bytes at %f bps.\n",
+ ms, total_bytes_read, *bps);
+ }
+
+ ssh_list_free(aio_queue);
+ sftp_limits_free(li);
+ sftp_free(sftp);
+ return 0;
+
+error:
+ rc = ssh_get_error_code(session);
+ if (rc != SSH_NO_ERROR) {
+ fprintf(stderr, "Error during sftp aio download: %s\n",
+ ssh_get_error(session));
+ }
+
+ /* Release aio structures corresponding to outstanding requests */
+ while ((aio = ssh_list_pop_head(sftp_aio, aio_queue)) != NULL) {
+ sftp_aio_free(aio);
+ }
+
+ ssh_list_free(aio_queue);
+ sftp_close(file);
+ sftp_limits_free(li);
+ sftp_free(sftp);
+ return -1;
+}
+
+int benchmarks_async_sftp_aio_up(ssh_session session,
+ struct argument_s *args,
+ float *bps)
+{
+ sftp_session sftp = NULL;
+ sftp_limits_t li = NULL;
+ sftp_file file = NULL;
+ sftp_aio aio = NULL;
+ struct ssh_list *aio_queue = NULL;
+
+ int concurrent_uploads = args->concurrent_requests;
+ size_t chunksize;
+ struct timestamp_struct ts = {0};
+ float ms = 0.0f;
+
+ size_t total_bytes = args->datasize * 1024 * 1024;
+ size_t to_write, total_bytes_requested = 0;
+ ssize_t bytes_written, bytes_requested;
+ int i, rc;
+
+ sftp = sftp_new(session);
+ if (sftp == NULL) {
+ fprintf(stderr, "Error during sftp aio upload: %s\n",
+ ssh_get_error(session));
+ return -1;
+ }
+
+ /*
+ * Errors which are logged in the ssh session are reported after
+ * jumping to the goto label, errors which aren't logged inside the
+ * ssh session are reported before jumping to that label
+ */
+
+ rc = sftp_init(sftp);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
+
+ li = sftp_limits(sftp);
+ if (li == NULL) {
+ goto error;
+ }
+
+ if (args->chunksize > li->max_write_length) {
+ chunksize = li->max_write_length;
+ if (args->verbose > 0) {
+ fprintf(stdout,
+ "Using the chunk size %zu (not the set size %u), "
+ "to respect the max data limit for write packet\n",
+ chunksize, args->chunksize);
+ }
+ } else {
+ chunksize = args->chunksize;
+ }
+
+ file = sftp_open(sftp, SFTPDIR SFTPFILE,
+ O_WRONLY | O_CREAT | O_TRUNC, 0777);
+ if (file == NULL) {
+ goto error;
+ }
+
+ aio_queue = ssh_list_new();
+ if (aio_queue == NULL) {
+ fprintf(stderr, "Error during sftp aio upload: Insufficient memory\n");
+ goto error;
+ }
+
+ if (args->verbose > 0) {
+ fprintf(stdout,
+ "Starting upload of %zu bytes now, "
+ "using %d concurrent uploads.\n",
+ total_bytes, concurrent_uploads);
+ }
+
+ timestamp_init(&ts);
+
+ for (i = 0;
+ i < concurrent_uploads && total_bytes_requested < total_bytes;
+ ++i) {
+ to_write = total_bytes - total_bytes_requested;
+ if (to_write > chunksize) {
+ to_write = chunksize;
+ }
+
+ bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ goto error;
+ }
+
+ if ((size_t)bytes_requested != to_write) {
+ fprintf(stderr,
+ "Error during sftp aio upload: sftp_aio_begin_write() "
+ "requesting less bytes even when the number of bytes "
+ "asked to write are within the max write limit");
+ sftp_aio_free(aio);
+ goto error;
+ }
+
+ total_bytes_requested += (size_t)bytes_requested;
+
+ /* enqueue */
+ rc = ssh_list_append(aio_queue, aio);
+ if (rc == SSH_ERROR) {
+ fprintf(stderr,
+ "Error during sftp aio upload: Insufficient memory\n");
+ sftp_aio_free(aio);
+ goto error;
+ }
+ }
+
+ while ((aio = ssh_list_pop_head(sftp_aio, aio_queue)) != NULL) {
+ bytes_written = sftp_aio_wait_write(&aio);
+ if (bytes_written == SSH_ERROR) {
+ goto error;
+ }
+
+ if (total_bytes_requested == total_bytes) {
+ /* No need to issue more requests */
+ continue;
+ }
+
+ /* else issue a request */
+ to_write = total_bytes - total_bytes_requested;
+ if (to_write > chunksize) {
+ to_write = chunksize;
+ }
+
+ bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
+ if (bytes_requested == SSH_ERROR) {
+ goto error;
+ }
+
+ if ((size_t)bytes_requested != to_write) {
+ fprintf(stderr,
+ "Error during sftp aio upload: sftp_aio_begin_write() "
+ "requesting less bytes even when the number of bytes "
+ "asked to write are within the max write limit");
+ sftp_aio_free(aio);
+ goto error;
+ }
+
+ total_bytes_requested += bytes_requested;
+
+ /* enqueue */
+ rc = ssh_list_append(aio_queue, aio);
+ if (rc == SSH_ERROR) {
+ fprintf(stderr,
+ "Error during sftp aio upload: Insufficient memory\n");
+ sftp_aio_free(aio);
+ goto error;
+ }
+ }
+
+ sftp_close(file);
+ ms = elapsed_time(&ts);
+ *bps = (float)(8000 * total_bytes) / ms;
+ if (args->verbose > 0) {
+ fprintf(stdout, "Upload took %f ms for %zu bytes at %f bps.\n",
+ ms, total_bytes, *bps);
+ }
+
+ ssh_list_free(aio_queue);
+ sftp_limits_free(li);
+ sftp_free(sftp);
+ return 0;
+
+error:
+ rc = ssh_get_error_code(session);
+ if (rc != SSH_NO_ERROR) {
+ fprintf(stderr, "Error during sftp aio upload: %s\n",
+ ssh_get_error(session));
+ }
+
+ /* Release aio structures corresponding to outstanding requests */
+ while ((aio = ssh_list_pop_head(sftp_aio, aio_queue)) != NULL) {
+ sftp_aio_free(aio);
+ }
+
+ ssh_list_free(aio_queue);
+ sftp_close(file);
+ sftp_limits_free(li);
+ sftp_free(sftp);
+ return -1;
+}
diff --git a/tests/benchmarks/benchmarks.c b/tests/benchmarks/benchmarks.c
index 5e33dd4b..4229d5a5 100644
--- a/tests/benchmarks/benchmarks.c
+++ b/tests/benchmarks/benchmarks.c
@@ -19,6 +19,8 @@
* MA 02111-1307, USA.
*/
+#define LIBSSH_STATIC
+
#include "config.h"
#include "benchmarks.h"
#include <libssh/libssh.h>
@@ -27,42 +29,54 @@
#include <stdlib.h>
#include <stdio.h>
-struct benchmark benchmarks[]= {
+struct benchmark benchmarks[] = {
{
- .name="benchmark_raw_upload",
- .fct=benchmarks_raw_up,
- .enabled=0
+ .name = "benchmark_raw_upload",
+ .fct = benchmarks_raw_up,
+ .enabled = 0
},
{
- .name="benchmark_raw_download",
- .fct=benchmarks_raw_down,
- .enabled=0
+ .name = "benchmark_raw_download",
+ .fct = benchmarks_raw_down,
+ .enabled = 0
},
{
- .name="benchmark_scp_upload",
- .fct=benchmarks_scp_up,
- .enabled=0
+ .name = "benchmark_scp_upload",
+ .fct = benchmarks_scp_up,
+ .enabled = 0
},
{
- .name="benchmark_scp_download",
- .fct=benchmarks_scp_down,
- .enabled=0
+ .name = "benchmark_scp_download",
+ .fct = benchmarks_scp_down,
+ .enabled = 0
},
+#ifdef WITH_SFTP
{
- .name="benchmark_sync_sftp_upload",
- .fct=benchmarks_sync_sftp_up,
- .enabled=0
+ .name = "benchmark_sync_sftp_upload",
+ .fct = benchmarks_sync_sftp_up,
+ .enabled = 0
},
{
- .name="benchmark_sync_sftp_download",
- .fct=benchmarks_sync_sftp_down,
- .enabled=0
+ .name = "benchmark_sync_sftp_download",
+ .fct = benchmarks_sync_sftp_down,
+ .enabled = 0
},
{
.name="benchmark_async_sftp_download",
.fct=benchmarks_async_sftp_down,
.enabled=0
+ },
+ {
+ .name = "benchmark_async_sftp_aio_download",
+ .fct = benchmarks_async_sftp_aio_down,
+ .enabled = 0
+ },
+ {
+ .name = "benchmark_async_sftp_aio_upload",
+ .fct = benchmarks_async_sftp_aio_up,
+ .enabled = 0
}
+#endif /* WITH_SFTP */
};
#ifdef HAVE_ARGP_H
@@ -71,7 +85,7 @@ struct benchmark benchmarks[]= {
const char *argp_program_version = "libssh benchmarks 2011-08-28";
const char *argp_program_bug_address = "Aris Adamantiadis <aris@0xbadc0de.be>";
-static char **cmdline;
+static char **cmdline = NULL;
/* Program documentation. */
static char doc[] = "libssh benchmarks";
@@ -79,128 +93,142 @@ static char doc[] = "libssh benchmarks";
/* The options we understand. */
static struct argp_option options[] = {
- {
- .name = "verbose",
- .key = 'v',
- .arg = NULL,
- .flags = 0,
- .doc = "Make libssh benchmark more verbose",
- .group = 0
- },
- {
- .name = "raw-upload",
- .key = '1',
- .arg = NULL,
- .flags = 0,
- .doc = "Upload raw data using channel",
- .group = 0
- },
- {
- .name = "raw-download",
- .key = '2',
- .arg = NULL,
- .flags = 0,
- .doc = "Download raw data using channel",
- .group = 0
- },
- {
- .name = "scp-upload",
- .key = '3',
- .arg = NULL,
- .flags = 0,
- .doc = "Upload data using SCP",
- .group = 0
- },
- {
- .name = "scp-download",
- .key = '4',
- .arg = NULL,
- .flags = 0,
- .doc = "Download data using SCP",
- .group = 0
- },
- {
- .name = "sync-sftp-upload",
- .key = '5',
- .arg = NULL,
- .flags = 0,
- .doc = "Upload data using synchronous SFTP",
- .group = 0
-
- },
- {
- .name = "sync-sftp-download",
- .key = '6',
- .arg = NULL,
- .flags = 0,
- .doc = "Download data using synchronous SFTP (slow)",
- .group = 0
-
- },
- {
- .name = "async-sftp-download",
- .key = '7',
- .arg = NULL,
- .flags = 0,
- .doc = "Download data using asynchronous SFTP (fast)",
- .group = 0
-
- },
- {
- .name = "host",
- .key = 'h',
- .arg = "HOST",
- .flags = 0,
- .doc = "Add a host to connect for benchmark (format user@hostname)",
- .group = 0
- },
- {
- .name = "size",
- .key = 's',
- .arg = "MBYTES",
- .flags = 0,
- .doc = "MBytes of data to send/receive per test",
- .group = 0
- },
- {
- .name = "chunk",
- .key = 'c',
- .arg = "bytes",
- .flags = 0,
- .doc = "size of data chunks to send/receive",
- .group = 0
- },
- {
- .name = "prequests",
- .key = 'p',
- .arg = "number [20]",
- .flags = 0,
- .doc = "[async SFTP] number of concurrent requests",
- .group = 0
- },
- {
- .name = "cipher",
- .key = 'C',
- .arg = "cipher",
- .flags = 0,
- .doc = "Cryptographic cipher to be used",
- .group = 0
- },
-
- {NULL, 0, NULL, 0, NULL, 0}
+ {
+ .name = "verbose",
+ .key = 'v',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Make libssh benchmark more verbose",
+ .group = 0
+ },
+ {
+ .name = "raw-upload",
+ .key = '1',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Upload raw data using channel",
+ .group = 0
+ },
+ {
+ .name = "raw-download",
+ .key = '2',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Download raw data using channel",
+ .group = 0
+ },
+ {
+ .name = "scp-upload",
+ .key = '3',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Upload data using SCP",
+ .group = 0
+ },
+ {
+ .name = "scp-download",
+ .key = '4',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Download data using SCP",
+ .group = 0
+ },
+ {
+ .name = "sync-sftp-upload",
+ .key = '5',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Upload data using synchronous SFTP",
+ .group = 0
+ },
+ {
+ .name = "sync-sftp-download",
+ .key = '6',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Download data using synchronous SFTP (slow)",
+ .group = 0
+ },
+ {
+ .name = "async-sftp-download",
+ .key = '7',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Download data using asynchronous SFTP (fast)",
+ .group = 0
+ },
+ {
+ .name = "async-sftp-aio-download",
+ .key = '8',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Download data using asynchronous SFTP AIO api (fast)",
+ .group = 0
+ },
+ {
+ .name = "async-sftp-aio-upload",
+ .key = '9',
+ .arg = NULL,
+ .flags = 0,
+ .doc = "Upload data using asynchronous SFTP AIO api (fast)",
+ .group = 0
+ },
+ {
+ .name = "host",
+ .key = 'h',
+ .arg = "HOST",
+ .flags = 0,
+ .doc = "Add a host to connect for benchmark (format user@hostname)",
+ .group = 0
+ },
+ {
+ .name = "size",
+ .key = 's',
+ .arg = "MBYTES",
+ .flags = 0,
+ .doc = "MBytes of data to send/receive per test",
+ .group = 0
+ },
+ {
+ .name = "chunk",
+ .key = 'c',
+ .arg = "bytes",
+ .flags = 0,
+ .doc = "size of data chunks to send/receive",
+ .group = 0
+ },
+ {
+ .name = "prequests",
+ .key = 'p',
+ .arg = "number [20]",
+ .flags = 0,
+ .doc = "[async SFTP] number of concurrent requests",
+ .group = 0
+ },
+ {
+ .name = "cipher",
+ .key = 'C',
+ .arg = "cipher",
+ .flags = 0,
+ .doc = "Cryptographic cipher to be used",
+ .group = 0
+ },
+
+ {NULL, 0, NULL, 0, NULL, 0}
};
/* Parse a single option. */
-static error_t parse_opt (int key, char *arg, struct argp_state *state) {
- /* Get the input argument from argp_parse, which we
- * know is a pointer to our arguments structure.
- */
- struct argument_s *arguments = state->input;
+static error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* Get the input argument from argp_parse, which we
+ * know is a pointer to our arguments structure.
+ */
+ struct argument_s *arguments = state->input;
- /* arg is currently not used */
- (void) arg;
+ /* arg is currently not used */
+ (void) arg;
- switch (key) {
+ switch (key) {
case '1':
case '2':
case '3':
@@ -208,42 +236,45 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) {
case '5':
case '6':
case '7':
- benchmarks[key - '1'].enabled = 1;
- arguments->ntests ++;
- break;
+ case '8':
+ case '9':
+ benchmarks[key - '1'].enabled = 1;
+ arguments->ntests++;
+ break;
case 'v':
- arguments->verbose++;
- break;
+ arguments->verbose++;
+ break;
case 's':
- arguments->datasize = atoi(arg);
- break;
+ arguments->datasize = atoi(arg);
+ break;
case 'p':
- arguments->concurrent_requests = atoi(arg);
- break;
+ arguments->concurrent_requests = atoi(arg);
+ break;
case 'c':
- arguments->chunksize = atoi(arg);
- break;
+ arguments->chunksize = atoi(arg);
+ break;
case 'C':
- arguments->cipher = arg;
- break;
+ arguments->cipher = arg;
+ break;
case 'h':
- if(arguments->nhosts >= MAX_HOSTS_CONNECT){
- fprintf(stderr, "Too much hosts\n");
- return ARGP_ERR_UNKNOWN;
- }
- arguments->hosts[arguments->nhosts]=arg;
- arguments->nhosts++;
- break;
+ if (arguments->nhosts >= MAX_HOSTS_CONNECT) {
+ fprintf(stderr, "Too much hosts\n");
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ arguments->hosts[arguments->nhosts] = arg;
+ arguments->nhosts++;
+ break;
case ARGP_KEY_ARG:
- /* End processing here. */
- cmdline = &state->argv [state->next - 1];
- state->next = state->argc;
- break;
+ /* End processing here. */
+ cmdline = &state->argv [state->next - 1];
+ state->next = state->argc;
+ break;
default:
- return ARGP_ERR_UNKNOWN;
- }
+ return ARGP_ERR_UNKNOWN;
+ }
- return 0;
+ return 0;
}
/* Our argp parser. */
@@ -251,150 +282,213 @@ static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL};
#endif /* HAVE_ARGP_H */
-static void cmdline_parse(int argc, char **argv, struct argument_s *arguments) {
- /*
- * Parse our arguments; every option seen by parse_opt will
- * be reflected in arguments.
- */
+static void cmdline_parse(int argc, char **argv, struct argument_s *arguments)
+{
+ /*
+ * Parse our arguments; every option seen by parse_opt will
+ * be reflected in arguments.
+ */
#ifdef HAVE_ARGP_H
- argp_parse(&argp, argc, argv, 0, 0, arguments);
+ argp_parse(&argp, argc, argv, 0, 0, arguments);
#else /* HAVE_ARGP_H */
- (void) argc;
- (void) argv;
- arguments->hosts[0]="localhost";
- arguments->nhosts=1;
+ (void) argc;
+ (void) argv;
+ arguments->hosts[0] = "localhost";
+ arguments->nhosts = 1;
#endif /* HAVE_ARGP_H */
}
-static void arguments_init(struct argument_s *arguments){
- memset(arguments,0,sizeof(*arguments));
- arguments->chunksize=32758;
- arguments->concurrent_requests=20;
- arguments->datasize = 10;
+static void arguments_init(struct argument_s *arguments)
+{
+ memset(arguments, 0, sizeof(*arguments));
+ arguments->chunksize = 32758;
+ arguments->concurrent_requests = 20;
+ arguments->datasize = 10;
}
-static ssh_session connect_host(const char *host, int verbose, char *cipher){
- ssh_session session=ssh_new();
- if(session==NULL)
- goto error;
- if(ssh_options_set(session,SSH_OPTIONS_HOST, host)<0)
- goto error;
- ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose);
- if(cipher != NULL){
- if (ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher) ||
- ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher)){
- goto error;
+static ssh_session connect_host(const char *host, int verbose, char *cipher)
+{
+ ssh_session session = NULL;
+ int rc;
+
+ session = ssh_new();
+ if (session == NULL) {
+ fprintf(stderr, "Error connecting to \"%s\": %s\n",
+ host, "Unable to create a new ssh session");
+ return NULL;
}
- }
- ssh_options_parse_config(session, NULL);
- if(ssh_connect(session)==SSH_ERROR)
- goto error;
- if(ssh_userauth_autopubkey(session,NULL) != SSH_AUTH_SUCCESS)
- goto error;
- return session;
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, host);
+ if (rc < 0)
+ goto error;
+
+ rc = ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose);
+ if (rc < 0)
+ goto error;
+
+ if (cipher != NULL) {
+ rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher);
+ if (rc < 0)
+ goto error;
+
+ rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher);
+ if (rc < 0)
+ goto error;
+ }
+
+ rc = ssh_options_parse_config(session, NULL);
+ if (rc < 0)
+ goto error;
+
+ rc = ssh_connect(session);
+ if (rc == SSH_ERROR)
+ goto error;
+
+ rc = ssh_userauth_autopubkey(session, NULL);
+ if (rc != SSH_AUTH_SUCCESS)
+ goto error;
+
+ return session;
+
error:
- fprintf(stderr,"Error connecting to \"%s\": %s\n",host,ssh_get_error(session));
- ssh_free(session);
- return NULL;
+ fprintf(stderr, "Error connecting to \"%s\": %s\n",
+ host, ssh_get_error(session));
+ ssh_free(session);
+ return NULL;
}
-static char *network_speed(float bps){
- static char buf[128];
- if(bps > 1000*1000*1000){
- /* Gbps */
- snprintf(buf,sizeof(buf),"%f Gbps",bps/(1000*1000*1000));
- } else if(bps > 1000*1000){
- /* Mbps */
- snprintf(buf,sizeof(buf),"%f Mbps",bps/(1000*1000));
- } else if(bps > 1000){
- snprintf(buf,sizeof(buf),"%f Kbps",bps/1000);
- } else {
- snprintf(buf,sizeof(buf),"%f bps",bps);
- }
- return buf;
+static char *network_speed(float bps)
+{
+ static char buf[128];
+ if (bps > 1000 * 1000 * 1000) {
+ /* Gbps */
+ snprintf(buf, sizeof(buf), "%f Gbps", bps / (1000 * 1000 * 1000));
+ } else if (bps > 1000 * 1000) {
+ /* Mbps */
+ snprintf(buf, sizeof(buf), "%f Mbps", bps / (1000 * 1000));
+ } else if (bps > 1000) {
+ snprintf(buf, sizeof(buf), "%f Kbps", bps / 1000);
+ } else {
+ snprintf(buf, sizeof(buf), "%f bps", bps);
+ }
+
+ return buf;
}
static void do_benchmarks(ssh_session session, struct argument_s *arguments,
- const char *hostname){
- float ping_rtt=0.0;
- float ssh_rtt=0.0;
- float bps=0.0;
- int i;
- int err;
- struct benchmark *b;
-
- if(arguments->verbose>0)
- fprintf(stdout,"Testing ICMP RTT\n");
- err=benchmarks_ping_latency(hostname, &ping_rtt);
- if(err == 0){
- fprintf(stdout,"ping RTT : %f ms\n",ping_rtt);
- }
- err=benchmarks_ssh_latency(session, &ssh_rtt);
- if(err==0){
- fprintf(stdout, "SSH RTT : %f ms. Theoretical max BW (win=128K) : %s\n",ssh_rtt,network_speed(128000.0/(ssh_rtt / 1000.0)));
- }
- for (i=0 ; i<BENCHMARK_NUMBER ; ++i){
- b = &benchmarks[i];
- if(b->enabled){
- err=b->fct(session,arguments,&bps);
- if(err==0){
- fprintf(stdout, "%s : %s : %s\n",hostname, b->name, network_speed(bps));
- }
+ const char *hostname)
+{
+ float ping_rtt = 0.0;
+ float ssh_rtt = 0.0;
+ float bps = 0.0;
+ int i;
+ int err;
+ struct benchmark *b = NULL;
+
+ if (arguments->verbose > 0)
+ fprintf(stdout, "Testing ICMP RTT\n");
+
+ err = benchmarks_ping_latency(hostname, &ping_rtt);
+ if (err == 0) {
+ fprintf(stdout, "ping RTT : %f ms\n", ping_rtt);
+ }
+
+ err = benchmarks_ssh_latency(session, &ssh_rtt);
+ if (err == 0) {
+ fprintf(stdout,
+ "SSH RTT : %f ms. Theoretical max BW (win=128K) : %s\n",
+ ssh_rtt, network_speed(128000.0 / (ssh_rtt / 1000.0)));
+ }
+
+ for (i=0; i < BENCHMARK_NUMBER; ++i){
+ b = &benchmarks[i];
+ if (b->enabled) {
+ err=b->fct(session, arguments, &bps);
+
+ if (err == 0) {
+ fprintf(stdout,
+ "%s : %s : %s\n",
+ hostname, b->name, network_speed(bps));
+ }
+ }
}
- }
}
-char *buffer;
-
-int main(int argc, char **argv){
- struct argument_s arguments;
- ssh_session session;
- int i;
-
- arguments_init(&arguments);
- cmdline_parse(argc, argv, &arguments);
- if (arguments.nhosts==0){
- fprintf(stderr,"At least one host (-h) must be specified\n");
- return EXIT_FAILURE;
- }
- if (arguments.ntests==0){
- for(i=0; i < BENCHMARK_NUMBER ; ++i){
- benchmarks[i].enabled=1;
+char *buffer = NULL;
+
+int main(int argc, char **argv)
+{
+ struct argument_s arguments;
+ ssh_session session = NULL;
+ int i, r;
+
+ arguments_init(&arguments);
+ cmdline_parse(argc, argv, &arguments);
+ if (arguments.nhosts == 0) {
+ fprintf(stderr, "At least one host (-h) must be specified\n");
+ return EXIT_FAILURE;
}
- arguments.ntests=BENCHMARK_NUMBER;
- }
- buffer=malloc(arguments.chunksize > 1024 ? arguments.chunksize : 1024);
- if(buffer == NULL){
- fprintf(stderr,"Allocation of chunk buffer failed\n");
- return EXIT_FAILURE;
- }
- if (arguments.verbose > 0){
- fprintf(stdout, "Will try hosts ");
- for(i=0;i<arguments.nhosts;++i){
- fprintf(stdout,"\"%s\" ", arguments.hosts[i]);
+
+ if (arguments.ntests == 0) {
+ for (i=0; i < BENCHMARK_NUMBER; ++i) {
+ benchmarks[i].enabled = 1;
+ }
+ arguments.ntests = BENCHMARK_NUMBER;
}
- fprintf(stdout,"with benchmarks ");
- for(i=0;i<BENCHMARK_NUMBER;++i){
- if(benchmarks[i].enabled)
- fprintf(stdout,"\"%s\" ",benchmarks[i].name);
+
+ buffer = malloc(arguments.chunksize > 1024 ? arguments.chunksize : 1024);
+ if (buffer == NULL) {
+ fprintf(stderr, "Allocation of chunk buffer failed\n");
+ return EXIT_FAILURE;
}
- fprintf(stdout,"\n");
- }
-
- for(i=0; i<arguments.nhosts;++i){
- if(arguments.verbose > 0)
- fprintf(stdout,"Connecting to \"%s\"...\n",arguments.hosts[i]);
- session=connect_host(arguments.hosts[i], arguments.verbose, arguments.cipher);
- if(session != NULL && arguments.verbose > 0)
- fprintf(stdout,"Success\n");
- if(session == NULL){
- fprintf(stderr,"Errors occurred, stopping\n");
- return EXIT_FAILURE;
+
+ if (arguments.verbose > 0) {
+ fprintf(stdout, "Will try hosts ");
+ for (i=0; i < arguments.nhosts; ++i) {
+ fprintf(stdout, "\"%s\" ", arguments.hosts[i]);
+ }
+
+ fprintf(stdout, "with benchmarks ");
+ for (i = 0; i < BENCHMARK_NUMBER; ++i) {
+ if (benchmarks[i].enabled)
+ fprintf(stdout, "\"%s\" ", benchmarks[i].name);
+ }
+
+ fprintf(stdout,"\n");
}
- do_benchmarks(session, &arguments, arguments.hosts[i]);
- ssh_disconnect(session);
- ssh_free(session);
- }
- return EXIT_SUCCESS;
+
+ r = ssh_init();
+ if (r == SSH_ERROR) {
+ fprintf(stderr, "Failed to initialize libssh\n");
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; i < arguments.nhosts; ++i) {
+ if (arguments.verbose > 0)
+ fprintf(stdout, "Connecting to \"%s\"...\n", arguments.hosts[i]);
+
+ session = connect_host(arguments.hosts[i],
+ arguments.verbose,
+ arguments.cipher);
+ if (session != NULL && arguments.verbose > 0)
+ fprintf(stdout, "Success\n");
+
+ if (session == NULL) {
+ fprintf(stderr, "Errors occurred, stopping\n");
+ return EXIT_FAILURE;
+ }
+
+ do_benchmarks(session, &arguments, arguments.hosts[i]);
+ ssh_disconnect(session);
+ ssh_free(session);
+ }
+
+ r = ssh_finalize();
+ if (r == SSH_ERROR) {
+ fprintf(stderr, "Failed to finalize libssh\n");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
}
diff --git a/tests/benchmarks/benchmarks.h b/tests/benchmarks/benchmarks.h
index 26da09bb..5f54a950 100644
--- a/tests/benchmarks/benchmarks.h
+++ b/tests/benchmarks/benchmarks.h
@@ -37,6 +37,8 @@ enum libssh_benchmarks {
BENCHMARK_SYNC_SFTP_UPLOAD,
BENCHMARK_SYNC_SFTP_DOWNLOAD,
BENCHMARK_ASYNC_SFTP_DOWNLOAD,
+ BENCHMARK_ASYNC_SFTP_AIO_DOWNLOAD,
+ BENCHMARK_ASYNC_SFTP_AIO_UPLOAD,
BENCHMARK_NUMBER
};
@@ -96,4 +98,8 @@ int benchmarks_sync_sftp_down (ssh_session session, struct argument_s *args,
float *bps);
int benchmarks_async_sftp_down (ssh_session session, struct argument_s *args,
float *bps);
+int benchmarks_async_sftp_aio_down(ssh_session session, struct argument_s *args,
+ float *bps);
+int benchmarks_async_sftp_aio_up(ssh_session session, struct argument_s *args,
+ float *bps);
#endif /* BENCHMARKS_H_ */
diff --git a/tests/chown_wrapper.c b/tests/chown_wrapper.c
new file mode 100644
index 00000000..ee6910ed
--- /dev/null
+++ b/tests/chown_wrapper.c
@@ -0,0 +1,21 @@
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <string.h>
+#include <dlfcn.h>
+
+typedef int (*__libc_chown)(const char *pathname, uid_t owner, gid_t group);
+
+/* silent gcc */
+int chown(const char *pathname, uid_t owner, gid_t group);
+
+int chown(const char *pathname, uid_t owner, gid_t group)
+{
+ __libc_chown original_chown;
+ if (strlen(pathname) > 7 && strncmp(pathname, "/dev/pt", 7) == 0) {
+ /* fake it! */
+ return 0;
+ }
+
+ original_chown = (__libc_chown)dlsym(RTLD_NEXT, "chown");
+ return (*original_chown)(pathname, owner, group);
+}
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index 71e5182e..c99c94ef 100644..100755
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -4,10 +4,12 @@ find_package(socket_wrapper)
set(LIBSSH_CLIENT_TESTS
torture_algorithms
+ torture_client_callbacks
torture_client_config
torture_connect
torture_hostkey
torture_auth
+ torture_auth_cert
torture_rekey
torture_forward
torture_knownhosts
@@ -15,6 +17,7 @@ set(LIBSSH_CLIENT_TESTS
torture_proxycommand
torture_session
torture_request_env
+ torture_request_pty_modes
torture_client_global_requests)
find_program(SCP_EXECUTABLE NAMES scp)
@@ -49,14 +52,22 @@ if (WITH_SFTP)
torture_sftp_dir
torture_sftp_read
torture_sftp_fsync
+ torture_sftp_hardlink
+ torture_sftp_limits
+ torture_sftp_rename
+ torture_sftp_expand_path
+ torture_sftp_aio
${SFTP_BENCHMARK_TESTS})
endif (WITH_SFTP)
+set(TORTURE_CLIENT_ENVIRONMENT ${TORTURE_ENVIRONMENT})
+list(APPEND TORTURE_CLIENT_ENVIRONMENT NSS_WRAPPER_HOSTS=${CMAKE_BINARY_DIR}/tests/etc/hosts)
+
foreach(_CLI_TEST ${LIBSSH_CLIENT_TESTS})
add_cmocka_test(${_CLI_TEST}
SOURCES ${_CLI_TEST}.c
COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS}
- LINK_LIBRARIES ${TORTURE_LIBRARY}
+ LINK_LIBRARIES ${TORTURE_LIBRARY} util
)
if (OSX)
@@ -70,6 +81,6 @@ foreach(_CLI_TEST ${LIBSSH_CLIENT_TESTS})
TEST
${_CLI_TEST}
PROPERTY
- ENVIRONMENT ${TORTURE_ENVIRONMENT})
+ ENVIRONMENT ${TORTURE_CLIENT_ENVIRONMENT})
endif()
endforeach()
diff --git a/tests/client/torture_algorithms.c b/tests/client/torture_algorithms.c
index ea3b647b..60354f9b 100644
--- a/tests/client/torture_algorithms.c
+++ b/tests/client/torture_algorithms.c
@@ -594,24 +594,34 @@ static void torture_algorithms_zlib(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "zlib");
#ifdef WITH_ZLIB
- assert_int_equal(rc, SSH_OK);
+ if (ssh_fips_mode()) {
+ assert_int_equal(rc, SSH_ERROR);
+ } else {
+ assert_int_equal(rc, SSH_OK);
+ }
#else
assert_int_equal(rc, SSH_ERROR);
#endif
rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_S_C, "zlib");
#ifdef WITH_ZLIB
- assert_int_equal(rc, SSH_OK);
+ if (ssh_fips_mode()) {
+ assert_int_equal(rc, SSH_ERROR);
+ } else {
+ assert_int_equal(rc, SSH_OK);
+ }
#else
assert_int_equal(rc, SSH_ERROR);
#endif
rc = ssh_connect(session);
#ifdef WITH_ZLIB
- if (ssh_get_openssh_version(session)) {
- assert_false(rc == SSH_OK);
- ssh_disconnect(session);
- return;
+ if (!ssh_fips_mode()) {
+ if (ssh_get_openssh_version(session)) {
+ assert_false(rc == SSH_OK);
+ ssh_disconnect(session);
+ return;
+ }
}
#endif
assert_int_equal(rc, SSH_OK);
diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c
index 29f6f5a5..77b7bd1f 100644
--- a/tests/client/torture_auth.c
+++ b/tests/client/torture_auth.c
@@ -32,8 +32,7 @@
#include <sys/types.h>
#include <pwd.h>
-/* agent_is_running */
-#include "agent.c"
+#include "torture_auth_common.c"
static int sshd_setup(void **state)
{
@@ -112,7 +111,7 @@ static int agent_setup(void **state)
char ssh_agent_cmd[4096];
char ssh_agent_sock[1024];
char ssh_agent_pidfile[1024];
- char bob_ssh_key[1024];
+ char ssh_key_add[1024];
struct passwd *pwd;
int rc;
@@ -149,38 +148,12 @@ static int agent_setup(void **state)
setenv("SSH_AUTH_SOCK", ssh_agent_sock, 1);
setenv("TORTURE_SSH_AGENT_PIDFILE", ssh_agent_pidfile, 1);
- snprintf(bob_ssh_key,
- sizeof(bob_ssh_key),
+ snprintf(ssh_key_add,
+ sizeof(ssh_key_add),
"ssh-add %s/.ssh/id_rsa",
pwd->pw_dir);
- rc = system(bob_ssh_key);
- assert_return_code(rc, errno);
-
- return 0;
-}
-
-static int agent_cert_setup(void **state)
-{
- char bob_alt_ssh_key[1024];
- struct passwd *pwd;
- int rc;
-
- rc = agent_setup(state);
- if (rc != 0) {
- return rc;
- }
-
- pwd = getpwnam("bob");
- assert_non_null(pwd);
-
- /* remove all keys, load alternative key + cert */
- snprintf(bob_alt_ssh_key,
- sizeof(bob_alt_ssh_key),
- "ssh-add -D && ssh-add %s/.ssh_cert/id_rsa",
- pwd->pw_dir);
-
- rc = system(bob_alt_ssh_key);
+ rc = system(ssh_key_add);
assert_return_code(rc, errno);
return 0;
@@ -257,6 +230,143 @@ static void torture_auth_none_nonblocking(void **state) {
}
+/* Setting MaxAuthTries 0 makes libssh hang. The option is not practical,
+ * but simulates setting low value and requiring multiple authentication
+ * methods to succeed (T233)
+ */
+static void torture_auth_none_max_tries(void **state) {
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+ const char *sshd_config = "MaxAuthTries 0";
+
+ torture_update_sshd_config(state, sshd_config);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_BOB);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session,NULL);
+ assert_int_equal(rc, SSH_AUTH_DENIED);
+
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+
+ /* Reset config back to defaults */
+ torture_update_sshd_config(state, "");
+}
+
+
+static void torture_auth_pubkey(void **state) {
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ ssh_key privkey = NULL;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ /* Authenticate as alice with bob his pubkey */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ /* negative tests */
+ rc = ssh_userauth_try_publickey(NULL, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_ERROR);
+ rc = ssh_userauth_try_publickey(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_ERROR);
+
+ rc = ssh_userauth_try_publickey(session, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ /* negative tests */
+ rc = ssh_userauth_publickey(NULL, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_ERROR);
+ rc = ssh_userauth_publickey(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_ERROR);
+
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ SSH_KEY_FREE(privkey);
+}
+
+static void torture_auth_pubkey_nonblocking(void **state) {
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ ssh_key privkey = NULL;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ /* Authenticate as alice with bob his pubkey */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_none(session,NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_DENIED);
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ do {
+ rc = ssh_userauth_try_publickey(session, NULL, privkey);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ do {
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ SSH_KEY_FREE(privkey);
+}
+
static void torture_auth_autopubkey(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
@@ -401,7 +511,11 @@ static void torture_auth_autopubkey_nonblocking(void **state) {
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
-static void torture_auth_kbdint(void **state) {
+static void
+torture_auth_kbdint(void **state,
+ const char *password,
+ enum ssh_auth_e res)
+{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -424,19 +538,35 @@ static void torture_auth_kbdint(void **state) {
assert_int_equal(rc, SSH_AUTH_INFO);
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1);
- rc = ssh_userauth_kbdint_setanswer(session, 0, TORTURE_SSH_USER_BOB_PASSWORD);
+ rc = ssh_userauth_kbdint_setanswer(session, 0, password);
assert_false(rc < 0);
rc = ssh_userauth_kbdint(session, NULL, NULL);
/* Sometimes, SSH server send an empty query at the end of exchange */
- if(rc == SSH_AUTH_INFO) {
+ if (rc == SSH_AUTH_INFO) {
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0);
rc = ssh_userauth_kbdint(session, NULL, NULL);
}
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
+ assert_int_equal(rc, res);
+}
+
+static void
+torture_auth_kbdint_good(void **state)
+{
+ torture_auth_kbdint(state, TORTURE_SSH_USER_BOB_PASSWORD, SSH_AUTH_SUCCESS);
+}
+
+static void
+torture_auth_kbdint_bad(void **state)
+{
+ torture_auth_kbdint(state, "bad password stample", SSH_AUTH_DENIED);
}
-static void torture_auth_kbdint_nonblocking(void **state) {
+static void
+torture_auth_kbdint_nonblocking(void **state,
+ const char *password,
+ enum ssh_auth_e res)
+{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -447,9 +577,9 @@ static void torture_auth_kbdint_nonblocking(void **state) {
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
- ssh_set_blocking(session,0);
+ ssh_set_blocking(session, 0);
do {
- rc = ssh_userauth_none(session, NULL);
+ rc = ssh_userauth_none(session, NULL);
} while (rc == SSH_AUTH_AGAIN);
/* This request should return a SSH_REQUEST_DENIED error */
@@ -464,23 +594,41 @@ static void torture_auth_kbdint_nonblocking(void **state) {
} while (rc == SSH_AUTH_AGAIN);
assert_int_equal(rc, SSH_AUTH_INFO);
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1);
- rc = ssh_userauth_kbdint_setanswer(session, 0, TORTURE_SSH_USER_BOB_PASSWORD);
+ rc = ssh_userauth_kbdint_setanswer(session, 0, password);
assert_false(rc < 0);
do {
rc = ssh_userauth_kbdint(session, NULL, NULL);
} while (rc == SSH_AUTH_AGAIN);
/* Sometimes, SSH server send an empty query at the end of exchange */
- if(rc == SSH_AUTH_INFO) {
+ if (rc == SSH_AUTH_INFO) {
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0);
do {
rc = ssh_userauth_kbdint(session, NULL, NULL);
} while (rc == SSH_AUTH_AGAIN);
}
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
+ assert_int_equal(rc, res);
+}
+
+static void
+torture_auth_kbdint_nonblocking_good(void **state)
+{
+ torture_auth_kbdint_nonblocking(state,
+ TORTURE_SSH_USER_BOB_PASSWORD,
+ SSH_AUTH_SUCCESS);
+}
+
+static void
+torture_auth_kbdint_nonblocking_bad(void **state)
+{
+ torture_auth_kbdint_nonblocking(state,
+ "bad password stample",
+ SSH_AUTH_DENIED);
}
-static void torture_auth_password(void **state) {
+static void
+torture_auth_password(void **state, const char *password, enum ssh_auth_e res)
+{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -499,11 +647,29 @@ static void torture_auth_password(void **state) {
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
- rc = ssh_userauth_password(session, NULL, TORTURE_SSH_USER_BOB_PASSWORD);
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
+ rc = ssh_userauth_password(session, NULL, password);
+ assert_int_equal(rc, res);
+}
+
+static void
+torture_auth_password_good(void **state)
+{
+ torture_auth_password(state,
+ TORTURE_SSH_USER_BOB_PASSWORD,
+ SSH_AUTH_SUCCESS);
}
-static void torture_auth_password_nonblocking(void **state) {
+static void
+torture_auth_password_bad(void **state)
+{
+ torture_auth_password(state, "bad password stample", SSH_AUTH_DENIED);
+}
+
+static void
+torture_auth_password_nonblocking(void **state,
+ const char *password,
+ enum ssh_auth_e res)
+{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -516,7 +682,7 @@ static void torture_auth_password_nonblocking(void **state) {
ssh_set_blocking(session,0);
do {
- rc = ssh_userauth_none(session, NULL);
+ rc = ssh_userauth_none(session, NULL);
} while (rc == SSH_AUTH_AGAIN);
/* This request should return a SSH_REQUEST_DENIED error */
@@ -528,16 +694,51 @@ static void torture_auth_password_nonblocking(void **state) {
assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
do {
- rc = ssh_userauth_password(session, NULL, TORTURE_SSH_USER_BOB_PASSWORD);
- } while(rc==SSH_AUTH_AGAIN);
+ rc = ssh_userauth_password(session, NULL, password);
+ } while (rc == SSH_AUTH_AGAIN);
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
+ assert_int_equal(rc, res);
+}
+
+static void
+torture_auth_password_nonblocking_good(void **state)
+{
+ torture_auth_password_nonblocking(state,
+ TORTURE_SSH_USER_BOB_PASSWORD,
+ SSH_AUTH_SUCCESS);
}
-static void torture_auth_agent(void **state) {
+static void
+torture_auth_password_nonblocking_bad(void **state)
+{
+ torture_auth_password_nonblocking(state,
+ "bad password stample",
+ SSH_AUTH_DENIED);
+}
+
+/* TODO cover the case:
+ * * when there is accompanying certificate (identities only + agent)
+ * * export private key to public key during _auto() authentication.
+ * this needs to be a encrypted private key in PEM format without
+ * accompanying public key.
+ */
+static void torture_auth_agent_identities_only(void **state)
+{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ struct passwd *pwd = NULL;
int rc;
+ int identities_only = 1;
+ char *id = NULL;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
if (!ssh_agent_is_running(session)){
print_message("*** Agent not running. Test ignored\n");
@@ -546,37 +747,18 @@ static void torture_auth_agent(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
- rc = ssh_connect(session);
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
assert_int_equal(rc, SSH_OK);
- rc = ssh_userauth_none(session,NULL);
- /* This request should return a SSH_REQUEST_DENIED error */
- if (rc == SSH_ERROR) {
- assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
- }
- rc = ssh_userauth_list(session, NULL);
- assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
-
- rc = ssh_userauth_agent(session, NULL);
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
-}
-
-static void torture_auth_agent_nonblocking(void **state) {
- struct torture_state *s = *state;
- ssh_session session = s->ssh.session;
- int rc;
-
- if (!ssh_agent_is_running(session)){
- print_message("*** Agent not running. Test ignored\n");
- return;
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
}
- rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
- assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
- rc = ssh_userauth_none(session,NULL);
+ rc = ssh_userauth_none(session, NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
@@ -584,111 +766,74 @@ static void torture_auth_agent_nonblocking(void **state) {
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
- ssh_set_blocking(session,0);
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
- do {
- rc = ssh_userauth_agent(session, NULL);
- } while (rc == SSH_AUTH_AGAIN);
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
+ /* Re-add a key */
+ rc = ssh_list_append(session->opts.identity, strdup(bob_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
}
-static void torture_auth_cert(void **state) {
+static void torture_auth_agent_identities_only_protected(void **state)
+{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
- ssh_key privkey = NULL;
- ssh_key cert = NULL;
char bob_ssh_key[1024];
- char bob_ssh_cert[2048];
struct passwd *pwd;
int rc;
+ int identities_only = 1;
+ char *id = NULL;
pwd = getpwnam("bob");
assert_non_null(pwd);
snprintf(bob_ssh_key,
sizeof(bob_ssh_key),
- "%s/.ssh_cert/id_rsa",
+ "%s/.ssh/id_rsa_protected",
pwd->pw_dir);
- snprintf(bob_ssh_cert,
- sizeof(bob_ssh_cert),
- "%s-cert.pub",
- bob_ssh_key);
- /* cert has been signed for login as alice */
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
- rc = ssh_connect(session);
- assert_int_equal(rc, SSH_OK);
-
- rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
assert_int_equal(rc, SSH_OK);
- rc = ssh_pki_import_cert_file(bob_ssh_cert, &cert);
- assert_int_equal(rc, SSH_OK);
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
- rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
+ rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
- rc = ssh_userauth_try_publickey(session, NULL, cert);
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
-
- rc = ssh_userauth_publickey(session, NULL, privkey);
- assert_int_equal(rc, SSH_AUTH_SUCCESS);
-
- SSH_KEY_FREE(privkey);
- SSH_KEY_FREE(cert);
-}
-
-static void torture_auth_agent_cert(void **state)
-{
- struct torture_state *s = *state;
- ssh_session session = s->ssh.session;
- int rc;
-
- /* Skip this test if in FIPS mode.
- *
- * OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
- * using certificates. It always uses SHA1.
- *
- * This should be removed as soon as OpenSSH agent bug is fixed.
- * (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
- if (ssh_fips_mode()) {
- skip();
- } else {
- /* After the bug is solved, this also should be removed */
- rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
- "ssh-rsa-cert-v01@openssh.com");
- assert_int_equal(rc, SSH_OK);
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
- /* Setup loads a different key, tests are exactly the same. */
- torture_auth_agent(state);
-}
-
-static void torture_auth_agent_cert_nonblocking(void **state)
-{
- struct torture_state *s = *state;
- ssh_session session = s->ssh.session;
- int rc;
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
- /* Skip this test if in FIPS mode.
- *
- * OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
- * using certificates. It always uses SHA1.
- *
- * This should be removed as soon as OpenSSH agent bug is fixed.
- * (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
- if (ssh_fips_mode()) {
- skip();
- } else {
- /* After the bug is solved, this also should be removed */
- rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
- "ssh-rsa-cert-v01@openssh.com");
- assert_int_equal(rc, SSH_OK);
- }
+ /* Re-add a key */
+ rc = ssh_list_append(session->opts.identity, strdup(bob_ssh_key));
+ assert_int_equal(rc, SSH_OK);
- torture_auth_agent_nonblocking(state);
+ /* Should succeed as key now in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
}
static void torture_auth_pubkey_types(void **state)
@@ -748,7 +893,7 @@ static void torture_auth_pubkey_types_ecdsa(void **state)
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
- /* We have only the 256b key -- whitelisting only larger should fail */
+ /* We have only the 256b key -- allowlisting only larger should fail */
rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
"ecdsa-sha2-nistp384");
assert_ssh_return_code(session, rc);
@@ -885,7 +1030,7 @@ static void torture_auth_pubkey_types_ecdsa_nonblocking(void **state)
ssh_set_blocking(session, 0);
do {
- rc = ssh_userauth_none(session, NULL);
+ rc = ssh_userauth_none(session, NULL);
} while (rc == SSH_AUTH_AGAIN);
/* This request should return a SSH_REQUEST_DENIED error */
@@ -896,7 +1041,7 @@ static void torture_auth_pubkey_types_ecdsa_nonblocking(void **state)
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
- /* We have only the 256b key -- whitelisting only larger should fail */
+ /* We have only the 256b key -- allowlisting only larger should fail */
rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
"ecdsa-sha2-nistp384");
assert_ssh_return_code(session, rc);
@@ -982,6 +1127,124 @@ static void torture_auth_pubkey_types_ed25519_nonblocking(void **state)
} while (rc == SSH_AUTH_AGAIN);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
+ SSH_KEY_FREE(privkey);
+}
+
+static void torture_auth_pubkey_rsa_key_size(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ ssh_key privkey = NULL;
+ struct passwd *pwd;
+ int rc;
+ unsigned int limit = 4096;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_connect(session);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* set unreasonable large minimum key size to trigger the condition */
+ rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit); /* larger than the test key */
+ assert_ssh_return_code(session, rc);
+
+ /* Import the RSA private key */
+ rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_DENIED);
+
+ /* revert to default values which should work also in FIPS mode */
+ limit = 0;
+ rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ SSH_KEY_FREE(privkey);
+}
+
+static void torture_auth_pubkey_rsa_key_size_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ ssh_key privkey = NULL;
+ struct passwd *pwd;
+ int rc;
+ unsigned int limit = 4096;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_connect(session);
+ assert_ssh_return_code(session, rc);
+
+ ssh_set_blocking(session, 0);
+ do {
+ rc = ssh_userauth_none(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* set unreasonable large minimum key size to trigger the condition */
+ rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit); /* larger than the test key */
+ assert_ssh_return_code(session, rc);
+
+ /* Import the RSA private key */
+ rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ do {
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_DENIED);
+
+ /* revert to default values which should work also in FIPS mode */
+ limit = 0;
+ rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &limit);
+ assert_ssh_return_code(session, rc);
+
+ do {
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ SSH_KEY_FREE(privkey);
}
int torture_run_tests(void) {
@@ -993,18 +1256,39 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_none_nonblocking,
session_setup,
session_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_password,
+ cmocka_unit_test_setup_teardown(torture_auth_none_max_tries,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_password_good,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_password_nonblocking_good,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_password_bad,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_password_nonblocking_bad,
session_setup,
session_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_password_nonblocking,
+ cmocka_unit_test_setup_teardown(torture_auth_kbdint_good,
session_setup,
session_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_kbdint,
+ cmocka_unit_test_setup_teardown(torture_auth_kbdint_nonblocking_good,
session_setup,
session_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_kbdint_nonblocking,
+ cmocka_unit_test_setup_teardown(torture_auth_kbdint_bad,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_kbdint_nonblocking_bad,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_pubkey,
+ pubkey_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_pubkey_nonblocking,
+ pubkey_setup,
+ session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_autopubkey,
pubkey_setup,
session_teardown),
@@ -1020,14 +1304,11 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_agent_nonblocking,
agent_setup,
agent_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_cert,
- pubkey_setup,
- session_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_agent_cert,
- agent_cert_setup,
+ cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only,
+ agent_setup,
agent_teardown),
- cmocka_unit_test_setup_teardown(torture_auth_agent_cert_nonblocking,
- agent_cert_setup,
+ cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only_protected,
+ agent_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types,
pubkey_setup,
@@ -1047,6 +1328,12 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ed25519_nonblocking,
pubkey_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size,
+ pubkey_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size_nonblocking,
+ pubkey_setup,
+ session_teardown),
};
ssh_init();
diff --git a/tests/client/torture_auth_cert.c b/tests/client/torture_auth_cert.c
new file mode 100644
index 00000000..18766b93
--- /dev/null
+++ b/tests/client/torture_auth_cert.c
@@ -0,0 +1,1072 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2010 by Aris Adamantiadis
+ * Copyright (c) 2023 by Jakub Jelen
+ *
+ * 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"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include "libssh/libssh.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "torture_auth_common.c"
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, true);
+
+ return 0;
+}
+
+static int sshd_teardown(void **state) {
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ int verbosity = torture_libssh_verbosity();
+ const char *all_keytypes = NULL;
+ struct passwd *pwd = NULL;
+ bool b = false;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ rc = setuid(pwd->pw_uid);
+ assert_return_code(rc, errno);
+
+ s->ssh.session = ssh_new();
+ 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_HOST, TORTURE_SSH_SERVER);
+ /* 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);
+
+ /* Enable all hostkeys */
+ all_keytypes = ssh_kex_get_supported_method(SSH_HOSTKEYS);
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, all_keytypes);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ /* certs have been signed for login as alice */
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Make sure we do not interfere with another ssh-agent */
+ unsetenv("SSH_AUTH_SOCK");
+ unsetenv("SSH_AGENT_PID");
+
+ return 0;
+}
+
+/* This sets up the ssh session in the directory without the default
+ * certificates that are used for authentication, requiring them to be provided
+ * as configuration options or from agent explicitly. */
+static int session_setup_ssh_dir(void **state)
+{
+ struct torture_state *s = *state;
+ const char *no_home = "~/.no_ssh";
+ int rc;
+
+ session_setup(state);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_SSH_DIR, &no_home);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static int agent_setup(void **state)
+{
+ struct torture_state *s = *state;
+ char ssh_agent_cmd[4096];
+ char ssh_agent_sock[1024];
+ char ssh_agent_pidfile[1024];
+ char ssh_key_add[1024];
+ struct passwd *pwd;
+ int rc;
+
+ rc = session_setup(state);
+ if (rc != 0) {
+ return rc;
+ }
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(ssh_agent_sock,
+ sizeof(ssh_agent_sock),
+ "%s/agent.sock",
+ s->socket_dir);
+
+ snprintf(ssh_agent_pidfile,
+ sizeof(ssh_agent_pidfile),
+ "%s/agent.pid",
+ s->socket_dir);
+
+ /* Production ready code!!! */
+ snprintf(ssh_agent_cmd,
+ sizeof(ssh_agent_cmd),
+ "eval `ssh-agent -a %s`; echo $SSH_AGENT_PID > %s",
+ ssh_agent_sock, ssh_agent_pidfile);
+
+ /* run ssh-agent and ssh-add as the normal user */
+ unsetenv("UID_WRAPPER_ROOT");
+
+ rc = system(ssh_agent_cmd);
+ assert_return_code(rc, errno);
+
+ setenv("SSH_AUTH_SOCK", ssh_agent_sock, 1);
+ setenv("TORTURE_SSH_AGENT_PIDFILE", ssh_agent_pidfile, 1);
+
+ snprintf(ssh_key_add,
+ sizeof(ssh_key_add),
+ "ssh-add %s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ rc = system(ssh_key_add);
+ assert_return_code(rc, errno);
+
+ return 0;
+}
+
+static int agent_cert_setup(void **state)
+{
+ char doe_alt_ssh_key[1024];
+ struct passwd *pwd;
+ int rc;
+
+ rc = agent_setup(state);
+ if (rc != 0) {
+ return rc;
+ }
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ /* remove all keys, load alternative key + cert */
+ snprintf(doe_alt_ssh_key,
+ sizeof(doe_alt_ssh_key),
+ "ssh-add -D && ssh-add %s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ rc = system(doe_alt_ssh_key);
+ assert_return_code(rc, errno);
+
+ return 0;
+}
+
+static int agent_teardown(void **state)
+{
+ const char *ssh_agent_pidfile;
+ int rc;
+
+ rc = session_teardown(state);
+ if (rc != 0) {
+ return rc;
+ }
+
+ ssh_agent_pidfile = getenv("TORTURE_SSH_AGENT_PIDFILE");
+ assert_non_null(ssh_agent_pidfile);
+
+ /* kill agent pid */
+ rc = torture_terminate_process(ssh_agent_pidfile);
+ assert_return_code(rc, errno);
+
+ unlink(ssh_agent_pidfile);
+
+ unsetenv("TORTURE_SSH_AGENT_PIDFILE");
+ unsetenv("SSH_AUTH_SOCK");
+
+ return 0;
+}
+
+static void torture_auth_cert(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_key privkey = NULL;
+ ssh_key cert = NULL;
+ char doe_ssh_key[1024];
+ char doe_ssh_cert[2048];
+ struct passwd *pwd;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s-cert.pub",
+ doe_ssh_key);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_pki_import_privkey_file(doe_ssh_key, NULL, NULL, NULL, &privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_pki_import_cert_file(doe_ssh_cert, &cert);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_try_publickey(session, NULL, cert);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ SSH_KEY_FREE(privkey);
+ SSH_KEY_FREE(cert);
+}
+
+static void torture_auth_cert_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_key privkey = NULL;
+ ssh_key cert = NULL;
+ char doe_ssh_key[1024];
+ char doe_ssh_cert[2048];
+ struct passwd *pwd;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s-cert.pub",
+ doe_ssh_key);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ rc = ssh_pki_import_privkey_file(doe_ssh_key, NULL, NULL, NULL, &privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_pki_import_cert_file(doe_ssh_cert, &cert);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
+ assert_int_equal(rc, SSH_OK);
+
+ do {
+ rc = ssh_userauth_try_publickey(session, NULL, cert);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code(session, rc);
+
+ do {
+ rc = ssh_userauth_publickey(session, NULL, privkey);
+ } while (rc == SSH_AUTH_AGAIN);
+
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ SSH_KEY_FREE(privkey);
+ SSH_KEY_FREE(cert);
+}
+
+/* Same as torture_auth_cert, but without explicitly loading certificate to the
+ * private key file, keeping libssh to use default cert path when done with
+ * _auto(). */
+static void torture_auth_cert_default_non_explicit(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ /* the cert is in the default location (~/.ssh/id_rsa-cert.pub) */
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+/* Same as torture_auth_cert_nonblocking, but without explicitly loading
+ * certificate to the private key file, keeping libssh to use default cert path
+ * when done with _auto().
+ * Non-blocking version */
+static void torture_auth_cert_default_non_explicit_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ /* the cert is in the default location (~/.ssh/id_rsa-cert.pub) */
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+/* Sanity test that there are no default identities available and the automatic
+ * pubkey authentication fails without any explicit identities */
+static void torture_auth_auto_fail(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_DENIED);
+}
+
+/* Sanity test that there are no default identities available and the automatic
+ * pubkey authentication fails without any explicit identities
+ * Non-blocking version */
+static void torture_auth_auto_fail_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_DENIED);
+}
+
+/* Same as torture_auth_cert, but the home SSH dir does not have any default
+ * identities and they are loaded through the options, only through the private
+ * key path. */
+static void torture_auth_cert_options_private(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ struct passwd *pwd;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ /* the cert has default naming relative to the private key (*-cert.pub) */
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, doe_ssh_key);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+/* Same as torture_auth_cert, but the home SSH dir does not have any default
+ * identities and they are loaded through the options, only through the private
+ * key path.
+ * Non-blocking version */
+static void torture_auth_cert_options_private_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ struct passwd *pwd;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ /* the cert has default naming relative to the private key (*-cert.pub) */
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, doe_ssh_key);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+/* Same as torture_auth_cert, but the home SSH dir does not have any default
+ * identities and they are loaded through the options, also the certificate file
+ */
+static void torture_auth_cert_options_cert(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ char doe_ssh_cert[2048];
+ struct passwd *pwd;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s-cert.pub",
+ doe_ssh_key);
+
+ /* Explicit private key and cert */
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, doe_ssh_key);
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, doe_ssh_cert);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+/* Same as torture_auth_cert, but the home SSH dir does not have any default
+ * identities and they are loaded through the options, only through the private
+ * key path.
+ * Non-blocking version */
+static void torture_auth_cert_options_cert_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ char doe_ssh_cert[2048];
+ struct passwd *pwd;
+ int rc;
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s-cert.pub",
+ doe_ssh_key);
+
+ /* Explicit private key and cert */
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, doe_ssh_key);
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, doe_ssh_cert);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+static void workaround_old_openssh_bug(void **state)
+{
+#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ /* Skip this test if in FIPS mode.
+ *
+ * OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
+ * using certificates. It always uses SHA1.
+ *
+ * This should be removed as soon as OpenSSH agent bug is fixed.
+ * (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
+ if (ssh_fips_mode()) {
+ skip();
+ } else {
+ /* After the bug is solved, this also should be removed */
+ rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+ "ssh-rsa-cert-v01@openssh.com");
+ assert_int_equal(rc, SSH_OK);
+ }
+#else
+ (void)state;
+#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
+}
+
+static void torture_auth_agent_cert(void **state)
+{
+ workaround_old_openssh_bug(state);
+
+ /* Setup loads a different key, tests are exactly the same. */
+ torture_auth_agent(state);
+}
+
+static void torture_auth_agent_cert_nonblocking(void **state)
+{
+ workaround_old_openssh_bug(state);
+
+ torture_auth_agent_nonblocking(state);
+}
+
+static void
+torture_auth_agent_cert_identities_only(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ struct passwd *pwd = NULL;
+ int identities_only = 1;
+ char *id = NULL;
+ int rc;
+
+ workaround_old_openssh_bug(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a key -- the cert in default location should be loaded
+ * automatically */
+ rc = ssh_list_append(session->opts.identity, strdup(doe_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
+}
+
+static void
+torture_auth_agent_cert_identities_only_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ struct passwd *pwd = NULL;
+ int identities_only = 1;
+ char *id = NULL;
+ int rc;
+
+ workaround_old_openssh_bug(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+ do {
+ rc = ssh_userauth_none(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a key -- the cert in default location should be loaded
+ * automatically */
+ rc = ssh_list_append(session->opts.identity, strdup(doe_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code(session, rc);
+}
+
+static int agent_cert_setup_explicit(void **state)
+{
+ char orig_doe_ssh_key[1024];
+ char doe_ssh_key[1024];
+ char keydata[2048];
+ struct passwd *pwd = NULL;
+ int fd ;
+ int rc;
+
+ agent_cert_setup(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(orig_doe_ssh_key,
+ sizeof(orig_doe_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/my_rsa",
+ pwd->pw_dir);
+
+ /* move the private key away from the default location the certificate can
+ * not be loaded automatically */
+ fd = open(orig_doe_ssh_key, O_RDONLY);
+ assert_true(fd > 0);
+ rc = read(fd, keydata, sizeof(keydata));
+ assert_true(rc > 0);
+ keydata[rc] = '\0';
+ close(fd);
+ torture_write_file(doe_ssh_key, keydata);
+
+ return 0;
+}
+
+static void
+torture_auth_agent_cert_identities_only_explicit(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ char doe_ssh_cert[1024];
+ struct passwd *pwd = NULL;
+ int identities_only = 1;
+ char *id = NULL;
+ int rc;
+
+ workaround_old_openssh_bug(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/my_rsa",
+ pwd->pw_dir);
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s/.ssh/id_rsa-cert.pub",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ skip();
+ }
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a key and cert */
+ rc = ssh_list_append(session->opts.identity, strdup(doe_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_list_append(session->opts.certificate, strdup(doe_ssh_cert));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
+}
+
+static void
+torture_auth_agent_cert_identities_only_nonblocking_explicit(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_key[1024];
+ char doe_ssh_cert[1024];
+ struct passwd *pwd = NULL;
+ int identities_only = 1;
+ char *id = NULL;
+ int rc;
+
+ workaround_old_openssh_bug(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_key,
+ sizeof(doe_ssh_key),
+ "%s/.ssh/my_rsa",
+ pwd->pw_dir);
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s/.ssh/id_rsa-cert.pub",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ skip();
+ }
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_none(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a key and cert */
+ rc = ssh_list_append(session->opts.identity, strdup(doe_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_list_append(session->opts.certificate, strdup(doe_ssh_cert));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code(session, rc);
+}
+
+static void
+torture_auth_agent_cert_only_identities_only(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_cert[1024];
+ struct passwd *pwd = NULL;
+ int identities_only = 1;
+ char *id = NULL;
+ int rc;
+
+ workaround_old_openssh_bug(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s/.ssh/id_rsa-cert.pub",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ skip();
+ }
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a cert: key is in the agent */
+ rc = ssh_list_append(session->opts.certificate, strdup(doe_ssh_cert));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
+}
+
+static void
+torture_auth_agent_cert_only_identities_only_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char doe_ssh_cert[1024];
+ struct passwd *pwd = NULL;
+ int identities_only = 1;
+ char *id = NULL;
+ int rc;
+
+ workaround_old_openssh_bug(state);
+
+ pwd = getpwnam("doe");
+ assert_non_null(pwd);
+
+ snprintf(doe_ssh_cert,
+ sizeof(doe_ssh_cert),
+ "%s/.ssh/id_rsa-cert.pub",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ skip();
+ }
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_userauth_none(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a cert: key is in the agent */
+ rc = ssh_list_append(session->opts.certificate, strdup(doe_ssh_cert));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config/options */
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code(session, rc);
+}
+
+int torture_run_tests(void) {
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_auth_cert,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_nonblocking,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_default_non_explicit,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_default_non_explicit_nonblocking,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_auto_fail,
+ session_setup_ssh_dir,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_auto_fail_nonblocking,
+ session_setup_ssh_dir,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_options_private,
+ session_setup_ssh_dir,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_options_private_nonblocking,
+ session_setup_ssh_dir,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_options_cert,
+ session_setup_ssh_dir,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_cert_options_cert_nonblocking,
+ session_setup_ssh_dir,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert,
+ agent_cert_setup,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_nonblocking,
+ agent_cert_setup,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_identities_only,
+ agent_cert_setup,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_identities_only_nonblocking,
+ agent_cert_setup,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_identities_only_explicit,
+ agent_cert_setup_explicit,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_identities_only_nonblocking_explicit,
+ agent_cert_setup_explicit,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_only_identities_only,
+ agent_cert_setup,
+ agent_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_agent_cert_only_identities_only_nonblocking,
+ agent_cert_setup,
+ agent_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/client/torture_auth_common.c b/tests/client/torture_auth_common.c
new file mode 100644
index 00000000..8a4f2854
--- /dev/null
+++ b/tests/client/torture_auth_common.c
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2010 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 "torture.h"
+#include "libssh/libssh.h"
+
+/* agent_is_running */
+#include "agent.c"
+
+void torture_auth_agent(void **state);
+void torture_auth_agent(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session,NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* negative test case */
+ rc = ssh_userauth_agent(NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_ERROR);
+
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
+}
+
+void torture_auth_agent_nonblocking(void **state);
+void torture_auth_agent_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session,NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ ssh_set_blocking(session,0);
+
+ do {
+ rc = ssh_userauth_agent(session, NULL);
+ } while (rc == SSH_AUTH_AGAIN);
+ assert_ssh_return_code(session, rc);
+}
diff --git a/tests/client/torture_auth_pkcs11.c b/tests/client/torture_auth_pkcs11.c
index ee97bff4..2537d2d8 100644
--- a/tests/client/torture_auth_pkcs11.c
+++ b/tests/client/torture_auth_pkcs11.c
@@ -39,9 +39,8 @@
#define LIBSSH_ECDSA_256_TESTKEY "id_pkcs11_ecdsa_256"
#define LIBSSH_ECDSA_384_TESTKEY "id_pkcs11_ecdsa_384"
#define LIBSSH_ECDSA_521_TESTKEY "id_pkcs11_ecdsa_521"
-#define SOFTHSM_CONF "softhsm.conf"
-const char template[] = "temp_dir_XXXXXX";
+const char template[] = "/tmp/temp_dir_XXXXXX";
struct pki_st {
char *temp_dir;
@@ -109,7 +108,6 @@ static int setup_session(void **state)
struct torture_state *s = *state;
struct pki_st *test_state = NULL;
int rc;
- char conf_path[1024] = {0};
char keys_dir[1024] = {0};
char *temp_dir;
@@ -134,9 +132,6 @@ static int setup_session(void **state)
test_state->keys_dir = strdup(keys_dir);
- snprintf(conf_path, sizeof(conf_path), "%s/softhsm.conf", test_state->temp_dir);
- setenv("SOFTHSM2_CONF", conf_path, 1);
-
setup_tokens(state, LIBSSH_RSA_TESTKEY, "rsa");
setup_tokens(state, LIBSSH_ECDSA_256_TESTKEY, "ecdsa256");
setup_tokens(state, LIBSSH_ECDSA_384_TESTKEY, "ecdsa384");
@@ -160,7 +155,7 @@ static int sshd_teardown(void **state) {
struct pki_st *test_state = s->private_data;
int rc;
- unsetenv("SOFTHSM2_CONF");
+ torture_cleanup_tokens(test_state->temp_dir);
rc = torture_change_dir(test_state->orig_dir);
assert_int_equal(rc, 0);
@@ -196,7 +191,7 @@ static void torture_auth_autopubkey(void **state, const char *obj_name, const ch
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, priv_uri);
assert_int_equal(rc, SSH_OK);
- assert_string_equal(session->opts.identity->root->data, priv_uri);
+ assert_string_equal(session->opts.identity_non_exp->root->data, priv_uri);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
diff --git a/tests/client/torture_client_callbacks.c b/tests/client/torture_client_callbacks.c
new file mode 100644
index 00000000..498783e3
--- /dev/null
+++ b/tests/client/torture_client_callbacks.c
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2012 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"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include "libssh/libssh.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+#include "libssh/callbacks.h"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+
+#define STATE_SUCCESS (1)
+#define STATE_FAILURE (2)
+
+struct callback_state
+{
+ int open_response;
+ int request_response;
+ ssh_session expected_session;
+ ssh_channel expected_channel;
+ struct ssh_channel_callbacks_struct *callback;
+};
+
+static void on_open_response(ssh_session session, ssh_channel channel, bool is_success, void *userdata)
+{
+ struct callback_state *state = (struct callback_state*)userdata;
+ assert_ptr_equal(state->expected_session, session);
+ assert_ptr_equal(state->expected_channel, channel);
+ state->open_response = is_success ? STATE_SUCCESS : STATE_FAILURE;
+}
+
+static void on_request_response(ssh_session session, ssh_channel channel, void *userdata)
+{
+ struct callback_state *state = (struct callback_state*)userdata;
+ assert_ptr_equal(state->expected_session, session);
+ assert_ptr_equal(state->expected_channel, channel);
+ state->request_response = STATE_SUCCESS;
+}
+
+static struct callback_state *set_callbacks(ssh_session session, ssh_channel channel)
+{
+ int rc;
+ struct ssh_channel_callbacks_struct *cb;
+ struct callback_state *cb_state = NULL;
+
+ cb_state = (struct callback_state *)calloc(1,
+ sizeof(struct callback_state));
+ assert_non_null(cb_state);
+ cb_state->expected_session = session;
+ cb_state->expected_channel = channel;
+
+ cb = (struct ssh_channel_callbacks_struct *)calloc(1,
+ sizeof(struct ssh_channel_callbacks_struct));
+ assert_non_null(cb);
+ ssh_callbacks_init(cb);
+ cb->userdata = cb_state;
+ cb->channel_open_response_function = on_open_response;
+ cb->channel_request_response_function = on_request_response;
+ rc = ssh_set_channel_callbacks(channel, cb);
+ assert_ssh_return_code(session, rc);
+
+ /* Keep the reference so it can be cleaned up later */
+ cb_state->callback = cb;
+ return cb_state;
+}
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd;
+ int rc;
+
+ 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 torture_state *s = *state;
+
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_open_success(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ int rc;
+ struct callback_state *cb_state = NULL;
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ cb_state = set_callbacks(session, channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ assert_int_equal(STATE_SUCCESS, cb_state->open_response);
+
+ ssh_channel_free(channel);
+ free(cb_state->callback);
+ free(cb_state);
+}
+
+static void torture_open_failure(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ int rc;
+ struct callback_state *cb_state = NULL;
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ cb_state = set_callbacks(session, channel);
+
+ rc = ssh_channel_open_forward(channel, "0.0.0.0", 0, "0.0.0.0", 0);
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ assert_int_equal(STATE_FAILURE, cb_state->open_response);
+
+ ssh_channel_free(channel);
+ free(cb_state->callback);
+ free(cb_state);
+}
+
+static void torture_request_success(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ int rc;
+ struct callback_state *cb_state = NULL;
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ cb_state = set_callbacks(session, channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_request_exec(channel, "echo -n ABCD");
+ assert_ssh_return_code(session, rc);
+
+ assert_int_equal(STATE_SUCCESS, cb_state->request_response);
+
+ ssh_channel_free(channel);
+ free(cb_state->callback);
+ free(cb_state);
+}
+
+static void torture_request_failure(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ int rc;
+ struct callback_state *cb_state = NULL;
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ cb_state = set_callbacks(session, channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_request_env(channel, "NOT_ACCEPTED", "VALUE");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ assert_int_equal(STATE_SUCCESS, cb_state->request_response);
+
+ ssh_channel_free(channel);
+ free(cb_state->callback);
+ free(cb_state);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_open_success,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_open_failure,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_request_success,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_request_failure,
+ 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/client/torture_client_config.c b/tests/client/torture_client_config.c
index c413619a..5ac22a4c 100644
--- a/tests/client/torture_client_config.c
+++ b/tests/client/torture_client_config.c
@@ -130,7 +130,7 @@ static void torture_client_config_system(void **state)
* configuration files retains OpenSSH semantics (the per-user overrides
* the system-wide values).
* The function ssh_options_parse_config() has hardcoded path to the
- * system-wide configuraion file so we try to emmulate the behavior by parsing
+ * system-wide configuration file so we try to emulate the behavior by parsing
* the files separately in the same order.
*/
static void torture_client_config_emulate(void **state)
diff --git a/tests/client/torture_connect.c b/tests/client/torture_connect.c
index f31f521f..f086488d 100644
--- a/tests/client/torture_connect.c
+++ b/tests/client/torture_connect.c
@@ -83,6 +83,26 @@ static int session_teardown(void **state)
return 0;
}
+static void torture_connect_peer_discon_msg(void **state) {
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+
+ int rc;
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_connect(session);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_service_request(session, "wrong-service");
+ assert_int_not_equal(rc, SSH_OK);
+
+ ssh_disconnect(session);
+ assert_non_null(session->peer_discon_msg);
+ assert_non_null(ssh_get_disconnect_message(session));
+}
+
static void torture_connect_nonblocking(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
@@ -100,6 +120,29 @@ static void torture_connect_nonblocking(void **state) {
assert_ssh_return_code(session, rc);
}
+static void torture_connect_ipv6(void **state) {
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ int rc;
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "testing");
+ assert_ssh_return_code(session, rc);
+ /* set non-blocking mode */
+ ssh_set_blocking(session, 0);
+
+ do {
+ rc = ssh_connect(session);
+ } while (rc == SSH_AGAIN);
+
+ assert_ssh_return_code(session, rc);
+
+ /* should work for blocking mode too */
+ ssh_disconnect(session);
+ ssh_set_blocking(session, 1);
+ rc = ssh_connect(session);
+ assert_ssh_return_code(session, rc);
+}
+
#if 0 /* This does not work with socket_wrapper */
static void torture_connect_timeout(void **state) {
struct torture_state *s = *state;
@@ -189,7 +232,7 @@ static void torture_connect_uninitialized(UNUSED_PARAM(void **state))
ssh_session session;
struct passwd *pwd;
- /* Make sure the library is unitialized */
+ /* Make sure the library is uninitialized */
while (is_ssh_initialized()) {
rc = ssh_finalize();
assert_return_code(rc, errno);
@@ -218,7 +261,9 @@ static void torture_connect_uninitialized(UNUSED_PARAM(void **state))
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_connect_peer_discon_msg, session_setup, session_teardown),
cmocka_unit_test_setup_teardown(torture_connect_nonblocking, session_setup, session_teardown),
+ cmocka_unit_test_setup_teardown(torture_connect_ipv6, session_setup, session_teardown),
cmocka_unit_test_setup_teardown(torture_connect_double, session_setup, session_teardown),
cmocka_unit_test_setup_teardown(torture_connect_failure, session_setup, session_teardown),
#if 0
diff --git a/tests/client/torture_forward.c b/tests/client/torture_forward.c
index dcbdcbdb..18b8c85f 100644
--- a/tests/client/torture_forward.c
+++ b/tests/client/torture_forward.c
@@ -82,6 +82,8 @@ static void torture_ssh_forward(void **state)
ssh_channel c;
int dport;
int bound_port;
+ char *originator_host = NULL;
+ int originator_port;
int rc;
int verbosity = SSH_LOG_TRACE;
@@ -90,7 +92,7 @@ static void torture_ssh_forward(void **state)
rc = ssh_channel_listen_forward(session, "127.0.0.21", 8080, &bound_port);
assert_ssh_return_code(session, rc);
- c = ssh_channel_accept_forward(session, 10, &dport);
+ c = ssh_channel_open_forward_port(session, 10, &dport, &originator_host, &originator_port);
/* We do not get a listener and run into the timeout here */
assert_null(c);
diff --git a/tests/client/torture_hostkey.c b/tests/client/torture_hostkey.c
index 574a8400..88b657b6 100644
--- a/tests/client/torture_hostkey.c
+++ b/tests/client/torture_hostkey.c
@@ -127,30 +127,6 @@ static void torture_hostkey_ed25519(void **state) {
assert_ssh_return_code(session, rc);
}
-#ifdef HAVE_DSA
-static void torture_hostkey_dss(void **state) {
- struct torture_state *s = *state;
- ssh_session session = s->ssh.session;
- char rsa[] = "ssh-dss";
-
- int rc;
-
- if (ssh_fips_mode()) {
- skip();
- }
-
- rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &rsa);
- assert_ssh_return_code(session, rc);
-
- rc = ssh_connect(session);
- assert_ssh_return_code(session, rc);
- ssh_disconnect(session);
-
- rc = ssh_connect(session);
- assert_ssh_return_code(session, rc);
-}
-#endif /* HAVE_DSA */
-
#ifdef HAVE_ECC
static void torture_hostkey_ecdsa(void **state) {
struct torture_state *s = *state;
@@ -221,10 +197,6 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_hostkey_ecdsa, session_setup,
session_teardown),
#endif
-#ifdef HAVE_DSA
- cmocka_unit_test_setup_teardown(torture_hostkey_dss, session_setup,
- session_teardown),
-#endif
/* the client is able to handle SHA2 extension (if negotiated) */
cmocka_unit_test_setup_teardown(torture_hostkey_rsa_sha256,
session_setup, session_teardown),
diff --git a/tests/client/torture_proxycommand.c b/tests/client/torture_proxycommand.c
index c04ff2ab..1bad4ccc 100644
--- a/tests/client/torture_proxycommand.c
+++ b/tests/client/torture_proxycommand.c
@@ -59,7 +59,7 @@ static int session_teardown(void **state)
return 0;
}
-#ifdef NC_EXECUTABLE
+#ifdef NCAT_EXECUTABLE
static void torture_options_set_proxycommand(void **state)
{
struct torture_state *s = *state;
@@ -71,13 +71,18 @@ static void torture_options_set_proxycommand(void **state)
int rc;
socket_t fd;
- rc = stat(NC_EXECUTABLE, &sb);
+ rc = stat(NCAT_EXECUTABLE, &sb);
if (rc != 0 || (sb.st_mode & S_IXOTH) == 0) {
- SSH_LOG(SSH_LOG_WARNING, "Could not find " NC_EXECUTABLE ": Skipping the test");
+ SSH_LOG(SSH_LOG_WARNING,
+ "Could not find " NCAT_EXECUTABLE ": Skipping the test");
skip();
}
- rc = snprintf(command, sizeof(command), NC_EXECUTABLE " %s %d", address, port);
+ rc = snprintf(command,
+ sizeof(command),
+ NCAT_EXECUTABLE " %s %d",
+ address,
+ port);
assert_true((size_t)rc < sizeof(command));
rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command);
@@ -90,7 +95,7 @@ static void torture_options_set_proxycommand(void **state)
assert_int_equal(rc & O_RDWR, O_RDWR);
}
-#else /* NC_EXECUTABLE */
+#else /* NCAT_EXECUTABLE */
static void torture_options_set_proxycommand(void **state)
{
@@ -98,7 +103,7 @@ static void torture_options_set_proxycommand(void **state)
skip();
}
-#endif /* NC_EXECUTABLE */
+#endif /* NCAT_EXECUTABLE */
static void torture_options_set_proxycommand_notexist(void **state) {
struct torture_state *s = *state;
@@ -122,7 +127,7 @@ static void torture_options_set_proxycommand_ssh(void **state)
socket_t fd;
rc = snprintf(command, sizeof(command),
- "ssh -oStrictHostKeyChecking=no -W [%%h]:%%p alice@%s",
+ "ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -W [%%h]:%%p alice@%s",
address);
assert_true((size_t)rc < sizeof(command));
@@ -147,7 +152,7 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state)
/* 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",
+ "ssh -vvv -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -W [%%h]:%%p alice@%s",
address);
assert_true((size_t)rc < sizeof(command));
@@ -161,6 +166,56 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state)
assert_int_equal(rc & O_RDWR, O_RDWR);
}
+static void torture_options_proxycommand_injection(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ const char *malicious_host = "`echo foo > mfile`";
+ const char *command = "nc %h %p";
+ char *current_dir = NULL;
+ char *malicious_file_path = NULL;
+ int mfp_len;
+ int verbosity = torture_libssh_verbosity();
+ struct stat sb;
+ int rc;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ rc = setuid(pwd->pw_uid);
+ assert_return_code(rc, errno);
+
+ s->ssh.session = ssh_new();
+ assert_non_null(s->ssh.session);
+
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ // if we would be checking the rc, this should fail
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, malicious_host);
+
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROXYCOMMAND, command);
+ assert_int_equal(rc, 0);
+ rc = ssh_connect(s->ssh.session);
+ assert_ssh_return_code_equal(s->ssh.session, rc, SSH_ERROR);
+
+ current_dir = torture_get_current_working_dir();
+ assert_non_null(current_dir);
+ mfp_len = strlen(current_dir) + 6;
+ malicious_file_path = malloc(mfp_len);
+ assert_non_null(malicious_file_path);
+ rc = snprintf(malicious_file_path, mfp_len,
+ "%s/mfile", current_dir);
+ assert_int_equal(rc, mfp_len);
+ free(current_dir);
+ rc = stat(malicious_file_path, &sb);
+ assert_int_not_equal(rc, 0);
+
+ // cleanup
+ remove(malicious_file_path);
+ free(malicious_file_path);
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -176,6 +231,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_options_set_proxycommand_ssh_stderr,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_options_proxycommand_injection,
+ NULL,
+ session_teardown),
};
diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c
index c599332d..f6a633fc 100644
--- a/tests/client/torture_rekey.c
+++ b/tests/client/torture_rekey.c
@@ -38,6 +38,10 @@
#include <fcntl.h>
#include <pwd.h>
+#define KEX_RETRY 32
+
+static uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */
+
static int sshd_setup(void **state)
{
torture_setup_sshd_server(state, false);
@@ -144,6 +148,29 @@ static void torture_rekey_default(void **state)
ssh_disconnect(s->ssh.session);
}
+static void sanity_check_session(void **state)
+{
+ struct torture_state *s = *state;
+ struct ssh_crypto_struct *c = NULL;
+
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
+ assert_int_equal(c->in_cipher->max_blocks,
+ bytes / c->in_cipher->blocksize);
+ assert_int_equal(c->out_cipher->max_blocks,
+ bytes / c->out_cipher->blocksize);
+ /* when strict kex is used, the newkeys reset the sequence number */
+ if ((s->ssh.session->flags & SSH_SESSION_FLAG_KEX_STRICT) != 0) {
+ assert_int_equal(c->out_cipher->packets, s->ssh.session->send_seq);
+ assert_int_equal(c->in_cipher->packets, s->ssh.session->recv_seq);
+ } else {
+ /* Otherwise we have less encrypted packets than transferred
+ * (first are not encrypted) */
+ assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
+ assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ }
+}
+
/* We lower the rekey limits manually and check that the rekey
* really happens when sending data
*/
@@ -153,7 +180,6 @@ static void torture_rekey_send(void **state)
int rc;
char data[256];
unsigned int i;
- uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */
struct ssh_crypto_struct *c = NULL;
unsigned char *secret_hash = NULL;
@@ -163,16 +189,10 @@ static void torture_rekey_send(void **state)
rc = ssh_connect(s->ssh.session);
assert_ssh_return_code(s->ssh.session, rc);
- /* The blocks limit is set correctly */
- c = s->ssh.session->current_crypto;
- assert_int_equal(c->in_cipher->max_blocks,
- bytes / c->in_cipher->blocksize);
- assert_int_equal(c->out_cipher->max_blocks,
- bytes / c->out_cipher->blocksize);
- /* We should have less encrypted packets than transfered (first are not encrypted) */
- assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ sanity_check_session(state);
/* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
secret_hash = malloc(c->digest_len);
assert_non_null(secret_hash);
memcpy(secret_hash, c->secret_hash, c->digest_len);
@@ -189,10 +209,11 @@ static void torture_rekey_send(void **state)
rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
- /* send ignore packets of up to 1KB to trigger rekey */
+ /* send ignore packets of up to 1KB to trigger rekey. Send little bit more
+ * to make sure it completes with all different ciphers */
memset(data, 0, sizeof(data));
memset(data, 'A', 128);
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < KEX_RETRY; i++) {
ssh_send_ignore(s->ssh.session, data);
ssh_handle_packets(s->ssh.session, 50);
}
@@ -234,8 +255,6 @@ static void session_setup_sftp(void **state)
assert_non_null(s->ssh.tsftp);
}
-uint64_t bytes = 2048; /* 2KB */
-
static int session_setup_sftp_client(void **state)
{
struct torture_state *s = *state;
@@ -271,14 +290,10 @@ static void torture_rekey_recv(void **state)
mode_t mask;
int rc;
- /* The blocks limit is set correctly */
- c = s->ssh.session->current_crypto;
- assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
- assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
- /* We should have less encrypted packets than transfered (first are not encrypted) */
- assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ sanity_check_session(state);
/* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
secret_hash = malloc(c->digest_len);
assert_non_null(secret_hash);
memcpy(secret_hash, c->secret_hash, c->digest_len);
@@ -442,6 +457,160 @@ static void torture_rekey_server_send(void **state)
ssh_disconnect(s->ssh.session);
}
+static void torture_rekey_different_kex(void **state)
+{
+ struct torture_state *s = *state;
+ int rc;
+ char data[256];
+ unsigned int i;
+ struct ssh_crypto_struct *c = NULL;
+ unsigned char *secret_hash = NULL;
+ size_t secret_hash_len = 0;
+ const char *kex1 = "diffie-hellman-group14-sha256,curve25519-sha256,ecdh-sha2-nistp256";
+ const char *kex2 = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,ecdh-sha2-nistp521";
+
+ /* Use short digest for initial key exchange */
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex1);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ rc = ssh_connect(s->ssh.session);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ /* The blocks limit is set correctly */
+ sanity_check_session(state);
+ /* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
+ secret_hash = malloc(c->digest_len);
+ assert_non_null(secret_hash);
+ memcpy(secret_hash, c->secret_hash, c->digest_len);
+ secret_hash_len = c->digest_len;
+ assert_int_equal(secret_hash_len, 32); /* SHA256 len */
+
+ /* OpenSSH can not rekey before authentication so authenticate here */
+ rc = ssh_userauth_none(s->ssh.session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(s->ssh.session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ /* Now try to change preference of key exchange algorithm to something with larger digest */
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex2);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ /* send ignore packets of up to 1KB to trigger rekey. Send little bit more
+ * to make sure the rekey it completes with all different ciphers (paddings */
+ memset(data, 0, sizeof(data));
+ memset(data, 'A', 128);
+ for (i = 0; i < KEX_RETRY; i++) {
+ ssh_send_ignore(s->ssh.session, data);
+ ssh_handle_packets(s->ssh.session, 1000);
+
+ c = s->ssh.session->current_crypto;
+ /* SHA256 len */
+ if (c->digest_len != 32) {
+ break;
+ }
+ }
+
+ /* The rekey limit was restored in the new crypto to the same value */
+ c = s->ssh.session->current_crypto;
+ assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
+ assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
+ /* Check that the secret hash is different than initially */
+ assert_int_equal(c->digest_len, 64); /* SHA512 len */
+ assert_memory_not_equal(secret_hash, c->secret_hash, secret_hash_len);
+ /* Session ID stays same after one rekey */
+ assert_memory_equal(secret_hash, c->session_id, secret_hash_len);
+ free(secret_hash);
+
+ assert_int_equal(ssh_is_connected(s->ssh.session), 1);
+ assert_int_equal(s->ssh.session->session_state, SSH_SESSION_STATE_AUTHENTICATED);
+
+ ssh_disconnect(s->ssh.session);
+}
+
+static void torture_rekey_server_different_kex(void **state)
+{
+ struct torture_state *s = *state;
+ int rc;
+ char data[256];
+ unsigned int i;
+ struct ssh_crypto_struct *c = NULL;
+ unsigned char *secret_hash = NULL;
+ size_t secret_hash_len = 0;
+ const char *sshd_config = "RekeyLimit 2K none";
+ const char *kex1 = "diffie-hellman-group14-sha256,curve25519-sha256,ecdh-sha2-nistp256";
+ const char *kex2 = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512";
+
+ /* Use short digest for initial key exchange */
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex1);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ torture_update_sshd_config(state, sshd_config);
+
+ rc = ssh_connect(s->ssh.session);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ /* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ secret_hash = malloc(c->digest_len);
+ assert_non_null(secret_hash);
+ memcpy(secret_hash, c->secret_hash, c->digest_len);
+ secret_hash_len = c->digest_len;
+ assert_int_equal(secret_hash_len, 32); /* SHA256 len */
+
+ /* OpenSSH can not rekey before authentication so authenticate here */
+ rc = ssh_userauth_none(s->ssh.session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(s->ssh.session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ /* Now try to change preference of key exchange algorithm to something with larger digest */
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex2);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ /* send ignore packets of up to 1KB to trigger rekey. Send little bit more
+ * to make sure the rekey it completes with all different ciphers (paddings */
+ memset(data, 0, sizeof(data));
+ memset(data, 'A', 128);
+ for (i = 0; i < KEX_RETRY; i++) {
+ ssh_send_ignore(s->ssh.session, data);
+ ssh_handle_packets(s->ssh.session, 1000);
+
+ c = s->ssh.session->current_crypto;
+ /* SHA256 len */
+ if (c->digest_len != 32) {
+ break;
+ }
+ }
+
+ /* Check that the secret hash is different than initially */
+ c = s->ssh.session->current_crypto;
+ assert_int_equal(c->digest_len, 64); /* SHA512 len */
+ assert_memory_not_equal(secret_hash, c->secret_hash, secret_hash_len);
+ /* Session ID stays same after one rekey */
+ assert_memory_equal(secret_hash, c->session_id, secret_hash_len);
+ free(secret_hash);
+
+ ssh_disconnect(s->ssh.session);
+}
+
+
#ifdef WITH_SFTP
static int session_setup_sftp_server(void **state)
{
@@ -510,6 +679,164 @@ static void torture_rekey_server_recv(void **state)
}
#endif /* WITH_SFTP */
+#ifdef WITH_ZLIB
+/* This is disabled by OpenSSH since OpenSSH 7.4p1 */
+#if (OPENSSH_VERSION_MAJOR == 7 && OPENSSH_VERSION_MINOR < 4) || OPENSSH_VERSION_MAJOR < 7
+/* Compression can be funky to get right after rekey
+ */
+static void torture_rekey_send_compression(void **state)
+{
+ struct torture_state *s = *state;
+ const char *comp = "zlib";
+ int rc;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_C_S, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_S_C, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ torture_rekey_send(state);
+}
+
+#ifdef WITH_SFTP
+static void torture_rekey_recv_compression(void **state)
+{
+ struct torture_state *s = *state;
+ const char *comp = "zlib";
+ int rc;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_C_S, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_S_C, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ torture_rekey_recv(state);
+}
+#endif /* WITH_SFTP */
+#endif
+
+/* Especially the delayed compression by openssh.
+ */
+static void torture_rekey_send_compression_delayed(void **state)
+{
+ struct torture_state *s = *state;
+ const char *comp = "zlib@openssh.com";
+ int rc;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_C_S, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_S_C, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ torture_rekey_send(state);
+}
+
+#ifdef WITH_SFTP
+static void torture_rekey_recv_compression_delayed(void **state)
+{
+ struct torture_state *s = *state;
+ const char *comp = "zlib@openssh.com";
+ int rc;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_C_S, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_COMPRESSION_S_C, comp);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ torture_rekey_recv(state);
+}
+#endif /* WITH_SFTP */
+#endif /* WITH_ZLIB */
+
+static void setup_server_for_good_guess(void *state)
+{
+ const char *default_sshd_config = "KexAlgorithms curve25519-sha256";
+ const char *fips_sshd_config = "KexAlgorithms ecdh-sha2-nistp256";
+ const char *sshd_config = default_sshd_config;
+
+ if (ssh_fips_mode()) {
+ sshd_config = fips_sshd_config;
+ }
+ /* This sets an only supported kex algorithm that we do not have as a first
+ * option */
+ torture_update_sshd_config(state, sshd_config);
+}
+
+static void torture_rekey_guess_send(void **state)
+{
+ struct torture_state *s = *state;
+
+ setup_server_for_good_guess(state);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ torture_rekey_send(state);
+}
+
+static void torture_rekey_guess_wrong_send(void **state)
+{
+ struct torture_state *s = *state;
+ const char *sshd_config = "KexAlgorithms diffie-hellman-group14-sha256";
+
+ /* This sets an only supported kex algorithm that we do not have as a first
+ * option */
+ torture_update_sshd_config(state, sshd_config);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ torture_rekey_send(state);
+}
+
+#ifdef WITH_SFTP
+static void torture_rekey_guess_recv(void **state)
+{
+ struct torture_state *s = *state;
+ int rc;
+
+ setup_server_for_good_guess(state);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ session_setup_sftp(state);
+
+ torture_rekey_recv(state);
+}
+
+static void torture_rekey_guess_wrong_recv(void **state)
+{
+ struct torture_state *s = *state;
+ const char *sshd_config = "KexAlgorithms diffie-hellman-group14-sha256";
+ int rc;
+
+ /* This sets an only supported kex algorithm that we do not have as a first
+ * option */
+ torture_update_sshd_config(state, sshd_config);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ session_setup_sftp(state);
+
+ torture_rekey_recv(state);
+}
+#endif /* WITH_SFTP */
int torture_run_tests(void) {
int rc;
@@ -528,16 +855,57 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_rekey_send,
session_setup,
session_teardown),
- /* Note, that this modifies the sshd_config */
+ cmocka_unit_test_setup_teardown(torture_rekey_different_kex,
+ session_setup,
+ session_teardown),
+#ifdef WITH_ZLIB
+#if (OPENSSH_VERSION_MAJOR == 7 && OPENSSH_VERSION_MINOR < 4) || OPENSSH_VERSION_MAJOR < 7
+ cmocka_unit_test_setup_teardown(torture_rekey_send_compression,
+ session_setup,
+ session_teardown),
+#ifdef WITH_SFTP
+ cmocka_unit_test_setup_teardown(torture_rekey_recv_compression,
+ session_setup_sftp_client,
+ session_teardown),
+#endif /* WITH_SFTP */
+#endif
+ cmocka_unit_test_setup_teardown(torture_rekey_send_compression_delayed,
+ session_setup,
+ session_teardown),
+#ifdef WITH_SFTP
+ cmocka_unit_test_setup_teardown(torture_rekey_recv_compression_delayed,
+ session_setup_sftp_client,
+ session_teardown),
+#endif /* WITH_SFTP */
+#endif /* WITH_ZLIB */
+ /* TODO verify the two rekey are possible and the states are not broken after rekey */
+
+ cmocka_unit_test_setup_teardown(torture_rekey_server_different_kex,
+ session_setup,
+ session_teardown),
+ /* Note, that these tests modify the sshd_config so follow-up tests
+ * might get unexpected behavior if they do not update the server with
+ * torture_update_sshd_config() too */
cmocka_unit_test_setup_teardown(torture_rekey_server_send,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_send,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_wrong_send,
+ session_setup,
+ session_teardown),
#ifdef WITH_SFTP
cmocka_unit_test_setup_teardown(torture_rekey_server_recv,
session_setup_sftp_server,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_recv,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_wrong_recv,
+ session_setup,
+ session_teardown),
#endif /* WITH_SFTP */
- /* TODO verify the two rekey are possible and the states are not broken after rekey */
};
ssh_init();
diff --git a/tests/client/torture_request_pty_modes.c b/tests/client/torture_request_pty_modes.c
new file mode 100755
index 00000000..0e8cdc0d
--- /dev/null
+++ b/tests/client/torture_request_pty_modes.c
@@ -0,0 +1,258 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2013 by Andreas Schneider <asn@cryptomilk.org>
+ *
+ * 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"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include <libssh/libssh.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <errno.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pty.h>
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+
+ return 0;
+}
+
+static int sshd_teardown(void **state) {
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd;
+ int rc;
+
+ 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 torture_state *s = *state;
+
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+/* reads from the channel, expecting the given output */
+static int check_channel_output(ssh_channel c, const char *expected)
+{
+ char buffer[4096] = {0};
+ int nbytes;
+
+ nbytes = ssh_channel_read(c, buffer, sizeof(buffer) - 1, 0);
+ while (nbytes > 0) {
+ buffer[nbytes]='\0';
+ ssh_log_hexdump("Read bytes:", (unsigned char *)buffer, nbytes);
+ if (strstr(buffer, expected) != NULL)
+ {
+ return 1;
+ }
+
+ nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0);
+ }
+ return 0;
+}
+
+/* set explicit TTY modes and validate that the server uses them */
+static void torture_request_pty_modes_translate_ocrnl(void **state)
+{
+ const unsigned char modes[] = {
+ /* enable OCRNL */
+ 73, 0, 0, 0, 1,
+ /* disable all other CR/NL handling */
+ 34, 0, 0, 0, 0,
+ 35, 0, 0, 0, 0,
+ 36, 0, 0, 0, 0,
+ 72, 0, 0, 0, 0,
+ 74, 0, 0, 0, 0,
+ 75, 0, 0, 0, 0,
+ 0, /* TTY_OP_END */
+ };
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel c;
+ int rc;
+ int string_found = 0;
+
+ c = ssh_channel_new(session);
+ assert_non_null(c);
+
+ rc = ssh_channel_open_session(c);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_request_pty_size_modes(c, "xterm", 80, 25, modes, sizeof(modes));
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_request_exec(c, "/bin/echo -e '>TEST\\r\\n<'");
+ assert_ssh_return_code(session, rc);
+
+ /* expect 2 newline characters */
+ string_found = check_channel_output(c, ">TEST\n\n<");
+ assert_int_equal(string_found, 1);
+
+ ssh_channel_close(c);
+}
+
+/* if stdin is a TTY, its modes are passed to the server */
+static void torture_request_pty_modes_use_stdin_modes(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel c;
+ int rc;
+ int string_found = 0;
+ struct termios modes;
+ int stdin_backup_fd = -1;
+ int master_fd, slave_fd;
+
+ c = ssh_channel_new(session);
+ assert_non_null(c);
+
+ rc = ssh_channel_open_session(c);
+ assert_ssh_return_code(session, rc);
+
+ /* stdin must be a TTY, so open one and replace the FD */
+ stdin_backup_fd = dup(STDIN_FILENO);
+ rc = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
+ assert_int_equal(rc, 0);
+ dup2(master_fd, STDIN_FILENO);
+ assert_true(isatty(STDIN_FILENO));
+ /* translate NL to CRNL on output to see a noticeable effect */
+ memset(&modes, 0, sizeof(modes));
+ tcgetattr(STDIN_FILENO, &modes);
+ modes.c_oflag |= ONLCR;
+ modes.c_iflag &= ~(ICRNL | INLCR | IGNCR);
+ tcsetattr(STDIN_FILENO, TCSANOW, &modes);
+
+ rc = ssh_channel_request_pty_size(c, "xterm", 80, 25);
+
+ /* revert the changes to STDIN first! */
+ dup2(stdin_backup_fd, STDIN_FILENO);
+ close(stdin_backup_fd);
+ close(master_fd);
+ close(slave_fd);
+
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_request_exec(c, "/bin/echo -e '>TEST\\r\\n<'");
+ assert_ssh_return_code(session, rc);
+
+ /* expect 2 carriage return characters + newline */
+ string_found = check_channel_output(c, ">TEST\r\r\n<");
+ assert_int_equal(string_found, 1);
+
+ ssh_channel_close(c);
+}
+
+/* if stdin is NOT a TTY, default modes are passed to the server */
+static void torture_request_pty_modes_use_default_modes(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel c;
+ int rc;
+ int string_found = 0;
+ int stdin_backup_fd = -1;
+
+ c = ssh_channel_new(session);
+ assert_non_null(c);
+
+ rc = ssh_channel_open_session(c);
+ assert_ssh_return_code(session, rc);
+
+ /* stdin must not a TTY - change the FD to something else */
+ stdin_backup_fd = dup(STDIN_FILENO);
+ close(STDIN_FILENO);
+ rc = open("/dev/null", O_RDONLY); // reuses FD 0 now
+ assert_int_equal(rc, STDIN_FILENO);
+ assert_false(isatty(STDIN_FILENO));
+
+ rc = ssh_channel_request_pty_size(c, "xterm", 80, 25);
+
+ /* revert the changes to STDIN first! */
+ dup2(stdin_backup_fd, STDIN_FILENO);
+ close(stdin_backup_fd);
+
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_request_exec(c, "/bin/echo -e '>TEST\\r\\n<'");
+ assert_ssh_return_code(session, rc);
+
+ /* expect the input unmodified */
+ string_found = check_channel_output(c, ">TEST\r\n<");
+ assert_int_equal(string_found, 1);
+
+ ssh_channel_close(c);
+}
+
+int torture_run_tests(void) {
+ int rc;
+
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_request_pty_modes_translate_ocrnl,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_request_pty_modes_use_stdin_modes,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_request_pty_modes_use_default_modes,
+ 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/client/torture_scp.c b/tests/client/torture_scp.c
index 59a00bae..fe3f239b 100644
--- a/tests/client/torture_scp.c
+++ b/tests/client/torture_scp.c
@@ -39,6 +39,9 @@
#define TEMPLATE BINARYDIR "/tests/home/alice/temp_dir_XXXXXX"
#define ALICE_HOME BINARYDIR "/tests/home/alice"
+/* store the original umask */
+mode_t old;
+
struct scp_st {
struct torture_state *s;
char *tmp_dir;
@@ -99,6 +102,9 @@ static int session_setup(void **state)
s = ts->s;
+ /* store the original umask and set a new one */
+ old = umask(0022);
+
/* Create temporary directory for alice */
tmp_dir = torture_make_temp_dir(TEMPLATE);
assert_non_null(tmp_dir);
@@ -135,6 +141,9 @@ static int session_teardown(void **state)
assert_non_null(ts->s);
s = ts->s;
+ /* restore the umask */
+ umask(old);
+
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
diff --git a/tests/client/torture_session.c b/tests/client/torture_session.c
index 2fa2ae33..8a43e586 100644
--- a/tests/client/torture_session.c
+++ b/tests/client/torture_session.c
@@ -218,6 +218,39 @@ static void torture_max_sessions(void **state)
#undef MAX_CHANNELS
}
+static void torture_no_more_sessions(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channels[2];
+ int rc;
+
+ /* Open a channel session */
+ channels[0] = ssh_channel_new(session);
+ assert_non_null(channels[0]);
+
+ rc = ssh_channel_open_session(channels[0]);
+ assert_ssh_return_code(session, rc);
+
+ /* Send no-more-sessions@openssh.com global request */
+ rc = ssh_request_no_more_sessions(session);
+ assert_ssh_return_code(session, rc);
+
+ /* Try to open an extra session and expect failure */
+ channels[1] = ssh_channel_new(session);
+ assert_non_null(channels[1]);
+
+ rc = ssh_channel_open_session(channels[1]);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* Free the unused channel */
+ ssh_channel_free(channels[1]);
+
+ /* Close and free open channel */
+ ssh_channel_close(channels[0]);
+ ssh_channel_free(channels[0]);
+}
+
static void torture_channel_delayed_close(void **state)
{
struct torture_state *s = *state;
@@ -258,6 +291,225 @@ static void torture_channel_delayed_close(void **state)
}
+/* Ensure that calling 'ssh_channel_poll' on a freed channel does not lead to
+ * segmentation faults. */
+static void torture_freed_channel_poll(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+
+ char request[256];
+ int rc;
+
+ snprintf(request, 256,
+ "dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ /* Make the request, read parts with close */
+ rc = ssh_channel_request_exec(channel, request);
+ assert_ssh_return_code(session, rc);
+
+ ssh_channel_free(channel);
+
+ rc = ssh_channel_poll(channel, 0);
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+/* Ensure that calling 'ssh_channel_poll_timeout' on a freed channel does not
+ * lead to segmentation faults. */
+static void torture_freed_channel_poll_timeout(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ bool channel_freed = false;
+ char request[256];
+ char buff[256] = {0};
+ int rc;
+
+ snprintf(request, 256,
+ "dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ /* Make the request, read parts with close */
+ rc = ssh_channel_request_exec(channel, request);
+ assert_ssh_return_code(session, rc);
+
+ do {
+ rc = ssh_channel_read(channel, buff, 256, 0);
+ } while(rc > 0);
+ assert_ssh_return_code(session, rc);
+
+ /* when either of these conditions is met the call to ssh_channel_free will
+ * actually free the channel so calling poll on that channel will be
+ * use-after-free */
+ if ((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
+ (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)) {
+ channel_freed = true;
+ }
+ ssh_channel_free(channel);
+
+ if (!channel_freed) {
+ rc = ssh_channel_poll_timeout(channel, 500, 0);
+ assert_int_equal(rc, SSH_ERROR);
+ }
+}
+
+/* Ensure that calling 'ssh_channel_read_nonblocking' on a freed channel does
+ * not lead to segmentation faults. */
+static void torture_freed_channel_read_nonblocking(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+
+ char request[256];
+ char buff[256] = {0};
+ int rc;
+
+ snprintf(request, 256,
+ "dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ /* Make the request, read parts with close */
+ rc = ssh_channel_request_exec(channel, request);
+ assert_ssh_return_code(session, rc);
+
+ ssh_channel_free(channel);
+
+ rc = ssh_channel_read_nonblocking(channel, buff, 256, 0);
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+}
+
+/* Ensure that calling 'ssh_channel_get_exit_status' on a freed channel does not
+ * lead to segmentation faults. */
+static void torture_freed_channel_get_exit_status(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ bool channel_freed = false;
+ char request[256];
+ char buff[256] = {0};
+ int rc;
+
+ snprintf(request, 256,
+ "dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ /* Make the request, read parts with close */
+ rc = ssh_channel_request_exec(channel, request);
+ assert_ssh_return_code(session, rc);
+
+ do {
+ rc = ssh_channel_read(channel, buff, 256, 0);
+ } while(rc > 0);
+ assert_ssh_return_code(session, rc);
+
+ /* when either of these conditions is met the call to ssh_channel_free will
+ * actually free the channel so calling poll on that channel will be
+ * use-after-free */
+ if ((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) ||
+ (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)) {
+ channel_freed = true;
+ }
+ ssh_channel_free(channel);
+
+ if (!channel_freed) {
+ rc = ssh_channel_get_exit_status(channel);
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+ }
+}
+
+static void
+torture_channel_read_stderr(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+ int rc;
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ /* This writes to standard error "pipe" */
+ rc = ssh_channel_request_exec(channel, "echo -n ABCD >&2");
+ assert_ssh_return_code(session, rc);
+
+ /* No data in stdout */
+ rc = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
+ assert_int_equal(rc, 0);
+
+ /* poll should say how much we can read */
+ rc = ssh_channel_poll(channel, 1);
+ assert_int_equal(rc, strlen("ABCD"));
+
+ /* Everything in stderr */
+ rc = ssh_channel_read(channel, buffer, sizeof(buffer), 1);
+ assert_int_equal(rc, strlen("ABCD"));
+
+ buffer[rc] = '\0';
+ assert_string_equal("ABCD", buffer);
+
+ ssh_channel_free(channel);
+}
+
+static void torture_pubkey_hash(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char *hash = NULL;
+ char *hexa = NULL;
+ int rc = 0;
+
+ /* bad arguments */
+ rc = ssh_get_pubkey_hash(session, NULL);
+ assert_int_equal(rc, SSH_ERROR);
+
+ rc = ssh_get_pubkey_hash(NULL, (unsigned char **)&hash);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* deprecated, but should be covered by tests! */
+ rc = ssh_get_pubkey_hash(session, (unsigned char **)&hash);
+ if (ssh_fips_mode()) {
+ /* When in FIPS mode, expect the call to fail */
+ assert_int_equal(rc, SSH_ERROR);
+ } else {
+ assert_int_equal(rc, MD5_DIGEST_LEN);
+
+ hexa = ssh_get_hexa((unsigned char *)hash, rc);
+ SSH_STRING_FREE_CHAR(hash);
+ assert_string_equal(hexa,
+ "ee:80:7f:61:f9:d5:be:f1:96:86:cc:96:7a:db:7a:7b");
+
+ SSH_STRING_FREE_CHAR(hexa);
+ }
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -273,9 +525,30 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_max_sessions,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_no_more_sessions,
+ session_setup,
+ session_teardown),
cmocka_unit_test_setup_teardown(torture_channel_delayed_close,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_freed_channel_poll,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_freed_channel_poll_timeout,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_freed_channel_read_nonblocking,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_freed_channel_get_exit_status,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_channel_read_stderr,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_pubkey_hash,
+ session_setup,
+ session_teardown),
};
ssh_init();
diff --git a/tests/client/torture_sftp_aio.c b/tests/client/torture_sftp_aio.c
new file mode 100644
index 00000000..4cbb5793
--- /dev/null
+++ b/tests/client/torture_sftp_aio.c
@@ -0,0 +1,534 @@
+#define LIBSSH_STATIC
+
+#include "config.h"
+
+#include "torture.h"
+#include "sftp.c"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+
+#define MAX_XFER_BUF_SIZE 16384
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ 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);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_aio_read_file(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ struct {
+ char *buf;
+ ssize_t bytes_read;
+ } a = {0}, b = {0};
+
+ sftp_file file = NULL;
+ sftp_attributes file_attr = NULL;
+ int fd;
+
+ size_t chunk_size;
+ int in_flight_requests = 20;
+
+ sftp_aio aio = NULL;
+ struct ssh_list *aio_queue = NULL;
+ sftp_limits_t li = NULL;
+
+ size_t file_size;
+ size_t total_bytes_requested;
+ size_t to_read, total_bytes_read;
+ ssize_t bytes_requested;
+
+ int i, rc;
+
+ /* Get the max limit for reading, use it as the chunk size */
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+ chunk_size = li->max_read_length;
+
+ a.buf = calloc(chunk_size, 1);
+ assert_non_null(a.buf);
+
+ b.buf = calloc(chunk_size, 1);
+ assert_non_null(b.buf);
+
+ aio_queue = ssh_list_new();
+ assert_non_null(aio_queue);
+
+ file = sftp_open(t->sftp, SSH_EXECUTABLE, O_RDONLY, 0);
+ assert_non_null(file);
+
+ fd = open(SSH_EXECUTABLE, O_RDONLY, 0);
+ assert_int_not_equal(fd, -1);
+
+ /* Get the file size */
+ file_attr = sftp_stat(t->sftp, SSH_EXECUTABLE);
+ assert_non_null(file_attr);
+ file_size = file_attr->size;
+
+ total_bytes_requested = 0;
+ for (i = 0;
+ i < in_flight_requests && total_bytes_requested < file_size;
+ ++i) {
+ to_read = file_size - total_bytes_requested;
+ if (to_read > chunk_size) {
+ to_read = chunk_size;
+ }
+
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ assert_int_equal(bytes_requested, to_read);
+ total_bytes_requested += bytes_requested;
+
+ /* enqueue */
+ rc = ssh_list_append(aio_queue, aio);
+ assert_int_equal(rc, SSH_OK);
+ }
+
+ total_bytes_read = 0;
+ while ((aio = ssh_list_pop_head(sftp_aio, aio_queue)) != NULL) {
+ a.bytes_read = sftp_aio_wait_read(&aio, a.buf, chunk_size);
+ assert_int_not_equal(a.bytes_read, SSH_ERROR);
+
+ total_bytes_read += (size_t)a.bytes_read;
+ if (total_bytes_read != file_size) {
+ assert_int_equal((size_t)a.bytes_read, chunk_size);
+ /*
+ * Failure of this assertion means that a short
+ * read is encountered but we have not reached
+ * the end of file yet. A short read before reaching
+ * the end of file should not occur for our test where
+ * the chunk size respects the max limit for reading.
+ */
+ }
+
+ /*
+ * Check whether the bytes read above are bytes
+ * present in the file or some garbage was stored
+ * in the buffer supplied to sftp_aio_wait_read().
+ */
+ b.bytes_read = read(fd, b.buf, a.bytes_read);
+ assert_int_equal(a.bytes_read, b.bytes_read);
+
+ rc = memcmp(a.buf, b.buf, (size_t)a.bytes_read);
+ assert_int_equal(rc, 0);
+
+ /* Issue more read requests if needed */
+ if (total_bytes_requested == file_size) {
+ continue;
+ }
+
+ /* else issue more requests */
+ to_read = file_size - total_bytes_requested;
+ if (to_read > chunk_size) {
+ to_read = chunk_size;
+ }
+
+ bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
+ assert_int_equal(bytes_requested, to_read);
+ total_bytes_requested += bytes_requested;
+
+ /* enqueue */
+ rc = ssh_list_append(aio_queue, aio);
+ assert_int_equal(rc, SSH_OK);
+ }
+
+ /*
+ * Check whether sftp server responds with an
+ * eof for more requests.
+ */
+ bytes_requested = sftp_aio_begin_read(file, chunk_size, &aio);
+ assert_int_equal(bytes_requested, chunk_size);
+
+ a.bytes_read = sftp_aio_wait_read(&aio, a.buf, chunk_size);
+ assert_int_equal(a.bytes_read, 0);
+
+ /* Clean up */
+ sftp_attributes_free(file_attr);
+ close(fd);
+ sftp_close(file);
+ ssh_list_free(aio_queue);
+ free(b.buf);
+ free(a.buf);
+ sftp_limits_free(li);
+}
+
+static void torture_sftp_aio_read_more_than_cap(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ sftp_limits_t li = NULL;
+ sftp_file file = NULL;
+ sftp_aio aio = NULL;
+
+ char *buf = NULL;
+ ssize_t bytes;
+
+ /* Get the max limit for reading */
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+
+ file = sftp_open(t->sftp, SSH_EXECUTABLE, O_RDONLY, 0);
+ assert_non_null(file);
+
+ /* Try reading more than the max limit */
+ bytes = sftp_aio_begin_read(file,
+ li->max_read_length * 2,
+ &aio);
+ assert_int_equal(bytes, li->max_read_length);
+
+ buf = calloc(li->max_read_length, 1);
+ assert_non_null(buf);
+
+ bytes = sftp_aio_wait_read(&aio, buf, li->max_read_length);
+ assert_int_not_equal(bytes, SSH_ERROR);
+
+ free(buf);
+ sftp_close(file);
+ sftp_limits_free(li);
+}
+
+static void torture_sftp_aio_write_file(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ char file_path[128] = {0};
+ sftp_file file = NULL;
+ int fd;
+
+ struct {
+ char *buf;
+ ssize_t bytes;
+ } wr = {0}, rd = {0};
+
+ size_t chunk_size;
+ ssize_t bytes_requested;
+ int in_flight_requests = 2;
+
+ sftp_limits_t li = NULL;
+ sftp_aio *aio_queue = NULL;
+ int rc, i;
+
+ /* Get the max limit for writing, use it as the chunk size */
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+ chunk_size = li->max_write_length;
+
+ rd.buf = calloc(chunk_size, 1);
+ assert_non_null(rd.buf);
+
+ wr.buf = calloc(chunk_size, 1);
+ assert_non_null(wr.buf);
+
+ aio_queue = malloc(sizeof(sftp_aio) * in_flight_requests);
+ assert_non_null(aio_queue);
+
+ snprintf(file_path, sizeof(file_path),
+ "%s/libssh_sftp_aio_write_test", t->testdir);
+ file = sftp_open(t->sftp, file_path, O_CREAT | O_WRONLY, 0777);
+ assert_non_null(file);
+
+ fd = open(file_path, O_RDONLY, 0);
+ assert_int_not_equal(fd, -1);
+
+ for (i = 0; i < in_flight_requests; ++i) {
+ bytes_requested = sftp_aio_begin_write(file,
+ wr.buf,
+ chunk_size,
+ &aio_queue[i]);
+ assert_int_equal(bytes_requested, chunk_size);
+ }
+
+ for (i = 0; i < in_flight_requests; ++i) {
+ wr.bytes = sftp_aio_wait_write(&aio_queue[i]);
+ assert_int_equal(wr.bytes, chunk_size);
+
+ /*
+ * Check whether the bytes written to the file
+ * by SFTP AIO write api were the bytes present
+ * in the buffer to write or some garbage was
+ * written to the file.
+ */
+ rd.bytes = read(fd, rd.buf, wr.bytes);
+ assert_int_equal(rd.bytes, wr.bytes);
+
+ rc = memcmp(rd.buf, wr.buf, wr.bytes);
+ assert_int_equal(rc, 0);
+ }
+
+ /* Clean up */
+ close(fd);
+ sftp_close(file);
+ free(aio_queue);
+
+ rc = unlink(file_path);
+ assert_int_equal(rc, 0);
+
+ free(wr.buf);
+ free(rd.buf);
+ sftp_limits_free(li);
+}
+
+static void torture_sftp_aio_write_more_than_cap(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ sftp_limits_t li = NULL;
+ char *buf = NULL;
+ size_t buf_size;
+
+ char file_path[128] = {0};
+ sftp_file file = NULL;
+
+ sftp_aio aio = NULL;
+ ssize_t bytes;
+ int rc;
+
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+
+ buf_size = li->max_write_length * 2;
+ buf = calloc(buf_size, 1);
+ assert_non_null(buf);
+
+ snprintf(file_path, sizeof(file_path),
+ "%s/libssh_sftp_aio_write_test_cap", t->testdir);
+ file = sftp_open(t->sftp, file_path, O_CREAT | O_WRONLY, 0777);
+ assert_non_null(file);
+
+ /* Try writing more than the max limit for writing */
+ bytes = sftp_aio_begin_write(file, buf, buf_size, &aio);
+ assert_int_equal(bytes, li->max_write_length);
+
+ bytes = sftp_aio_wait_write(&aio);
+ assert_int_equal(bytes, li->max_write_length);
+
+ /* Clean up */
+ sftp_close(file);
+
+ rc = unlink(file_path);
+ assert_int_equal(rc, 0);
+
+ free(buf);
+ sftp_limits_free(li);
+}
+
+static void torture_sftp_aio_read_negative(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ char *buf = NULL;
+ sftp_file file = NULL;
+ sftp_aio aio = NULL;
+ sftp_limits_t li = NULL;
+
+ size_t chunk_size;
+ ssize_t bytes;
+ int rc;
+
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+ chunk_size = li->max_read_length;
+
+ buf = calloc(chunk_size, 1);
+ assert_non_null(buf);
+
+ /* Open a file for reading */
+ file = sftp_open(t->sftp, SSH_EXECUTABLE, O_RDONLY, 0);
+ assert_non_null(file);
+
+ /* Passing NULL as the sftp file handle */
+ bytes = sftp_aio_begin_read(NULL, chunk_size, &aio);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing 0 as the number of bytes to read */
+ bytes = sftp_aio_begin_read(file, 0, &aio);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /*
+ * Passing NULL instead of a pointer to a location to
+ * store an aio handle.
+ */
+ bytes = sftp_aio_begin_read(file, chunk_size, NULL);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing NULL instead of a pointer to an aio handle */
+ bytes = sftp_aio_wait_read(NULL, buf, sizeof(buf));
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing NULL as the buffer's address */
+ bytes = sftp_aio_begin_read(file, chunk_size, &aio);
+ assert_int_equal(bytes, chunk_size);
+
+ bytes = sftp_aio_wait_read(&aio, NULL, sizeof(buf));
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing 0 as the buffer size */
+ bytes = sftp_aio_begin_read(file, chunk_size, &aio);
+ assert_int_equal(bytes, chunk_size);
+
+ bytes = sftp_aio_wait_read(&aio, buf, 0);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /*
+ * Test for the scenario when the number
+ * of bytes read exceed the buffer size.
+ */
+ rc = sftp_seek(file, 0); /* Seek to the start of file */
+ assert_int_equal(rc, 0);
+
+ bytes = sftp_aio_begin_read(file, 2, &aio);
+ assert_int_equal(bytes, 2);
+
+ bytes = sftp_aio_wait_read(&aio, buf, 1);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ sftp_close(file);
+ free(buf);
+ sftp_limits_free(li);
+}
+
+static void torture_sftp_aio_write_negative(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ char *buf = NULL;
+
+ char file_path[128] = {0};
+ sftp_file file = NULL;
+ sftp_aio aio = NULL;
+ sftp_limits_t li = NULL;
+
+ size_t chunk_size;
+ ssize_t bytes;
+ int rc;
+
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+ chunk_size = li->max_write_length;
+
+ buf = calloc(chunk_size, 1);
+ assert_non_null(buf);
+
+ /* Open a file for writing */
+ snprintf(file_path, sizeof(file_path),
+ "%s/libssh_sftp_aio_write_test_negative", t->testdir);
+ file = sftp_open(t->sftp, file_path, O_CREAT | O_WRONLY, 0777);
+ assert_non_null(file);
+
+ /* Passing NULL as the sftp file handle */
+ bytes = sftp_aio_begin_write(NULL, buf, chunk_size, &aio);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing NULL as the buffer's address */
+ bytes = sftp_aio_begin_write(file, NULL, chunk_size, &aio);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing 0 as the size of buffer */
+ bytes = sftp_aio_begin_write(file, buf, 0, &aio);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing NULL instead of a pointer to a location to store an aio handle */
+ bytes = sftp_aio_begin_write(file, buf, chunk_size, NULL);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ /* Passing NULL instead of a pointer to an aio handle */
+ bytes = sftp_aio_wait_write(NULL);
+ assert_int_equal(bytes, SSH_ERROR);
+
+ sftp_close(file);
+ rc = unlink(file_path);
+ assert_int_equal(rc, 0);
+
+ free(buf);
+ sftp_limits_free(li);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_aio_read_file,
+ session_setup,
+ session_teardown),
+
+ cmocka_unit_test_setup_teardown(torture_sftp_aio_read_more_than_cap,
+ session_setup,
+ session_teardown),
+
+ cmocka_unit_test_setup_teardown(torture_sftp_aio_write_file,
+ session_setup,
+ session_teardown),
+
+ cmocka_unit_test_setup_teardown(torture_sftp_aio_write_more_than_cap,
+ session_setup,
+ session_teardown),
+
+ cmocka_unit_test_setup_teardown(torture_sftp_aio_read_negative,
+ session_setup,
+ session_teardown),
+
+ cmocka_unit_test_setup_teardown(torture_sftp_aio_write_negative,
+ 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/client/torture_sftp_expand_path.c b/tests/client/torture_sftp_expand_path.c
new file mode 100644
index 00000000..85ef0108
--- /dev/null
+++ b/tests/client/torture_sftp_expand_path.c
@@ -0,0 +1,125 @@
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include "sftp.c"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ 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);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_expand_path(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ struct passwd *pwd = NULL;
+ char *expanded_path = NULL;
+ int rc;
+
+ rc = sftp_extension_supported(t->sftp, "expand-path@openssh.com", "1");
+ if (rc == 0) {
+ skip();
+ }
+
+ pwd = getpwnam(TORTURE_SSH_USER_ALICE);
+ assert_non_null(pwd);
+
+ /* testing for a absolute path */
+ expanded_path = sftp_expand_path(t->sftp, "~/.");
+ assert_non_null(expanded_path);
+
+ assert_string_equal(expanded_path, pwd->pw_dir);
+
+ SSH_STRING_FREE_CHAR(expanded_path);
+
+ /* testing for a relative path */
+ expanded_path = sftp_expand_path(t->sftp, ".");
+ assert_non_null(expanded_path);
+
+ assert_string_equal(expanded_path, pwd->pw_dir);
+
+ SSH_STRING_FREE_CHAR(expanded_path);
+
+ /* passing a NULL sftp session */
+ expanded_path = sftp_expand_path(NULL, "~/.");
+ assert_null(expanded_path);
+
+ /* passing an invalid path */
+ expanded_path = sftp_expand_path(t->sftp, "/...//");
+ assert_null(expanded_path);
+
+ /* passing null path */
+ expanded_path = sftp_expand_path(t->sftp, NULL);
+ assert_null(expanded_path);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_expand_path,
+ 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/client/torture_sftp_hardlink.c b/tests/client/torture_sftp_hardlink.c
new file mode 100644
index 00000000..37963636
--- /dev/null
+++ b/tests/client/torture_sftp_hardlink.c
@@ -0,0 +1,114 @@
+#define LIBSSH_STATIC
+
+#include "config.h"
+
+#include "torture.h"
+#include "sftp.c"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ 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);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_hardlink(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ char link_1[128] = {0};
+ char link_2[128] = {0};
+ int fd;
+ int rc;
+
+ snprintf(link_1, sizeof(link_1),
+ "%s/libssh_sftp_hardlink_test_1", t->testdir);
+ snprintf(link_2, sizeof(link_2),
+ "%s/libssh_sftp_hardlink_test_2", t->testdir);
+
+ fd = open(link_1, O_CREAT, S_IRWXU);
+ assert_return_code(fd, errno);
+ close(fd);
+
+ rc = sftp_hardlink(t->sftp, link_1, link_2);
+ assert_int_equal(rc, SSH_OK);
+
+ /* check whether the file got associated with link_2 */
+ rc = access(link_2, F_OK);
+ assert_int_equal(rc, 0);
+
+ unlink(link_1);
+ unlink(link_2);
+
+ /*
+ * try to create a hardlink for a file that does not
+ * exist, this should fail
+ */
+ rc = sftp_hardlink(t->sftp, link_1, link_2);
+ assert_int_not_equal(rc, 0);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_hardlink,
+ 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/client/torture_sftp_limits.c b/tests/client/torture_sftp_limits.c
new file mode 100644
index 00000000..07ef9928
--- /dev/null
+++ b/tests/client/torture_sftp_limits.c
@@ -0,0 +1,177 @@
+#define LIBSSH_STATIC
+
+#include "config.h"
+
+#include "torture.h"
+#include "sftp.c"
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <pwd.h>
+#include <errno.h>
+
+#if HAVE_VALGRIND_VALGRIND_H
+ #include <valgrind/valgrind.h>
+#endif
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ 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);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_limits(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_limits_t li = NULL;
+ int rc;
+
+ li = sftp_limits(t->sftp);
+ assert_non_null(li);
+
+ rc = sftp_extension_supported(t->sftp, "limits@openssh.com", "1");
+ if (rc == 1) {
+ /*
+ * Tests are run against the OpenSSH server, hence we check for the
+ * specific limits used by OpenSSH.
+ */
+ uint64_t openssh_max_packet_length = 256 * 1024;
+ uint64_t openssh_max_read_length = openssh_max_packet_length - 1024;
+ uint64_t openssh_max_write_length = openssh_max_packet_length - 1024;
+ size_t vg = 0;
+
+ assert_int_equal(li->max_packet_length, openssh_max_packet_length);
+ assert_int_equal(li->max_read_length, openssh_max_read_length);
+ assert_int_equal(li->max_write_length, openssh_max_write_length);
+
+ /*
+ * fds - File descriptors, w.r.to - With respect to
+ *
+ * Valgrind reserves some fds for itself and changes the rlimits
+ * w.r.to fds for the process its inspecting. Due to this reservation
+ * the rlimits w.r.to fds for our test may not be the same as the
+ * rlimits w.r.to fds seen by OpenSSH server (which Valgrind isn't
+ * inspecting).
+ *
+ * Valgrind changes the limits in such a way that after seeing the
+ * changed limits, the test cannot predict the original unchanged
+ * limits (which OpenSSH would be using). Hence, the test cannot
+ * determine the correct value of "max_open_handles" that the OpenSSH
+ * server should've sent.
+ *
+ * So if Valgrind is running our test, we don't provide any kind of
+ * check for max_open_handles. Check for >= 0 is also not provided in
+ * this case since that's always true for an uint64_t (an unsigned type)
+ */
+#if HAVE_VALGRIND_VALGRIND_H
+ vg = RUNNING_ON_VALGRIND;
+#endif
+
+ if (vg == 0) {
+ struct rlimit rlim = {0};
+ uint64_t openssh_max_open_handles = 0;
+
+ /*
+ * Get the resource limit for max file descriptors that a process
+ * can open. Since the client and the server run on the same machine
+ * in case of tests, this limit should be same for both (except the
+ * case when Valgrind runs the test)
+ */
+ rc = getrlimit(RLIMIT_NOFILE, &rlim);
+ assert_int_equal(rc, 0);
+ if (rlim.rlim_cur > 5) {
+ /*
+ * Leaving file handles for stdout, stdin, stderr, syslog and
+ * a spare file handle, OpenSSH server allows the client to open
+ * at max (rlim.rlim_cur - 5) handles.
+ */
+ openssh_max_open_handles = rlim.rlim_cur - 5;
+ }
+
+ assert_int_equal(li->max_open_handles, openssh_max_open_handles);
+ }
+ } else {
+ /* Check for the default limits */
+ assert_int_equal(li->max_packet_length, 34000);
+ assert_int_equal(li->max_read_length, 32768);
+ assert_int_equal(li->max_write_length, 32768);
+ assert_int_equal(li->max_open_handles, 0);
+ }
+
+ sftp_limits_free(li);
+}
+
+static void torture_sftp_limits_negative(void **state)
+{
+ sftp_limits_t li = NULL;
+
+ (void)state;
+ li = sftp_limits(NULL);
+ assert_null(li);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_limits,
+ session_setup,
+ session_teardown),
+
+ cmocka_unit_test_setup_teardown(torture_sftp_limits_negative,
+ 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/client/torture_sftp_read.c b/tests/client/torture_sftp_read.c
index 687153eb..c6ec4b91 100644
--- a/tests/client/torture_sftp_read.c
+++ b/tests/client/torture_sftp_read.c
@@ -101,7 +101,7 @@ int torture_run_tests(void) {
struct CMUnitTest tests[] = {
/* This test is intentionally running twice to trigger a bug in OpenSSH
* or in pam_wrapper, causing the second invocation to fail.
- * See: https://bugs.libssh.org/T122
+ * See: https://gitlab.com/libssh/libssh-mirror/-/issues/23
*/
cmocka_unit_test_setup_teardown(torture_sftp_read_blocking,
session_setup,
diff --git a/tests/client/torture_sftp_rename.c b/tests/client/torture_sftp_rename.c
new file mode 100644
index 00000000..c2d11a31
--- /dev/null
+++ b/tests/client/torture_sftp_rename.c
@@ -0,0 +1,120 @@
+#define LIBSSH_STATIC
+
+#include "config.h"
+
+#include "torture.h"
+#include "sftp.c"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ 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);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_rename(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+
+ char name_1[128] = {0};
+ char name_2[128] = {0};
+
+ int fd;
+ int rc;
+
+ snprintf(name_1, sizeof(name_1),
+ "%s/libssh_sftp_rename_test_1", t->testdir);
+ snprintf(name_2, sizeof(name_2),
+ "%s/libssh_sftp_rename_test_2", t->testdir);
+
+ fd = open(name_1, O_CREAT, S_IRWXU);
+ assert_return_code(fd, errno);
+ close(fd);
+
+ /* try to rename an existing file */
+ rc = sftp_rename(t->sftp, name_1, name_2);
+ assert_int_equal(rc, SSH_OK);
+
+ /* check whether any file with name_1 exists, it shouldn't */
+ rc = access(name_1, F_OK);
+ assert_int_not_equal(rc, 0);
+
+ /* check whether file with name_2 exists, it should */
+ rc = access(name_2, F_OK);
+ assert_int_equal(rc, 0);
+
+ unlink(name_2);
+
+ /*
+ * try to rename a file that does not exist,
+ * this should fail (-ve test case)
+ */
+ rc = sftp_rename(t->sftp, name_1, name_2);
+ assert_int_not_equal(rc, 0);
+}
+
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_rename,
+ 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/ctest-default.cmake b/tests/ctest-default.cmake
index dbf53e30..51bbebab 100644
--- a/tests/ctest-default.cmake
+++ b/tests/ctest-default.cmake
@@ -17,16 +17,16 @@ set(CTEST_BUILD_OPTIONS "-DUNIT_TESTING=ON -WITH_SFTP=ON -DWITH_SERVER=ON -DWITH
#set(CTEST_CUSTOM_MEMCHECK_IGNORE torture_rand)
-## The Model to set: Nightly, Continous, Experimental
+## The Model to set: Nightly, Continuous, Experimental
set(CTEST_MODEL "Experimental")
## The branch
#set(CTEST_GIT_BRANCH "--branch v0-5")
-## Wether to enable memory checking.
+## Whether to enable memory checking.
set(WITH_MEMCHECK FALSE)
-## Wether to enable code coverage.
+## Whether to enable code coverage.
set(WITH_COVERAGE FALSE)
#######################################################################
diff --git a/tests/etc/hosts.in b/tests/etc/hosts.in
index 2ab69799..8891125d 100644
--- a/tests/etc/hosts.in
+++ b/tests/etc/hosts.in
@@ -1,2 +1,5 @@
127.0.0.10 server.libssh.site
127.0.0.21 client.libssh.site
+
+123.0.0.11 testing
+fd00::5357:5f0a testing
diff --git a/tests/etc/pam_matrix_passdb.in b/tests/etc/pam_matrix_passdb.in
index c0aa54e5..9404bc0e 100644
--- a/tests/etc/pam_matrix_passdb.in
+++ b/tests/etc/pam_matrix_passdb.in
@@ -1,3 +1,4 @@
bob:secret:sshd
alice:secret:sshd
charlie:secret:sshd
+doe:secret:sshd
diff --git a/tests/etc/passwd.in b/tests/etc/passwd.in
index 85e20c6d..cae364b7 100644
--- a/tests/etc/passwd.in
+++ b/tests/etc/passwd.in
@@ -1,6 +1,7 @@
bob:x:5000:9000:bob gecos:@HOMEDIR@/bob:/bin/sh
alice:x:5001:9000:alice gecos:@HOMEDIR@/alice:/bin/sh
charlie:x:5002:9000:charlie gecos:@HOMEDIR@/charlie:/bin/sh
+doe:x:5003:9000:doe gecos:@HOMEDIR@/doe:/bin/sh
sshd:x:65530:65531:sshd:@HOMEDIR@:/sbin/nologin
nobody:x:65533:65534:nobody gecos:@HOMEDIR@:/bin/false
root:x:65534:65532:root gecos:@HOMEDIR@:/bin/false
diff --git a/tests/etc/shadow.in b/tests/etc/shadow.in
index a0b2b9d6..0f03b149 100644
--- a/tests/etc/shadow.in
+++ b/tests/etc/shadow.in
@@ -1,3 +1,4 @@
alice:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
bob:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
charlie:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
+doe:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
diff --git a/tests/external_override/CMakeLists.txt b/tests/external_override/CMakeLists.txt
index a0d584e3..365a1083 100644
--- a/tests/external_override/CMakeLists.txt
+++ b/tests/external_override/CMakeLists.txt
@@ -22,26 +22,58 @@ add_library(poly1305_override SHARED
set(POLY1305_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}poly1305_override${CMAKE_SHARED_LIBRARY_SUFFIX})
+if (WITH_GCRYPT)
+ set (override_src
+ ${libssh_SOURCE_DIR}/src/getrandom_gcrypt.c
+ ${libssh_SOURCE_DIR}/src/md_gcrypt.c
+ )
+ set(override_libs
+ ${GCRYPT_LIBRARIES}
+ )
+elseif (WITH_MBEDTLS)
+ set (override_src
+ ${libssh_SOURCE_DIR}/src/getrandom_mbedcrypto.c
+ ${libssh_SOURCE_DIR}/src/md_mbedcrypto.c
+ )
+ set(override_libs
+ ${MBEDTLS_CRYPTO_LIBRARY}
+ )
+else ()
+ set (override_src
+ ${libssh_SOURCE_DIR}/src/getrandom_crypto.c
+ ${libssh_SOURCE_DIR}/src/md_crypto.c
+ )
+ set(override_libs
+ OpenSSL::Crypto
+ )
+endif (WITH_GCRYPT)
+
# ed25519_override
add_library(ed25519_override SHARED
- ed25519_override.c
- ${libssh_SOURCE_DIR}/src/external/fe25519.c
- ${libssh_SOURCE_DIR}/src/external/ge25519.c
- ${libssh_SOURCE_DIR}/src/external/sc25519.c
- ${libssh_SOURCE_DIR}/src/external/ed25519.c
- )
+ ed25519_override.c
+ ${libssh_SOURCE_DIR}/src/external/fe25519.c
+ ${libssh_SOURCE_DIR}/src/external/ge25519.c
+ ${libssh_SOURCE_DIR}/src/external/sc25519.c
+ ${libssh_SOURCE_DIR}/src/external/ed25519.c
+ ${override_src}
+ )
+target_link_libraries(ed25519_override
+ PRIVATE ${override_libs})
set(ED25519_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}ed25519_override${CMAKE_SHARED_LIBRARY_SUFFIX})
# curve25519_override
add_library(curve25519_override SHARED
- curve25519_override.c
- ${libssh_SOURCE_DIR}/src/external/curve25519_ref.c
- ${libssh_SOURCE_DIR}/src/external/fe25519.c
- ${libssh_SOURCE_DIR}/src/external/ge25519.c
- ${libssh_SOURCE_DIR}/src/external/sc25519.c
- ${libssh_SOURCE_DIR}/src/external/ed25519.c
- )
+ curve25519_override.c
+ ${libssh_SOURCE_DIR}/src/external/curve25519_ref.c
+ ${libssh_SOURCE_DIR}/src/external/fe25519.c
+ ${libssh_SOURCE_DIR}/src/external/ge25519.c
+ ${libssh_SOURCE_DIR}/src/external/sc25519.c
+ ${libssh_SOURCE_DIR}/src/external/ed25519.c
+ ${override_src}
+)
+target_link_libraries(curve25519_override
+ PRIVATE ${override_libs})
set(CURVE25519_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}curve25519_override${CMAKE_SHARED_LIBRARY_SUFFIX})
@@ -66,23 +98,13 @@ elseif (WITH_GCRYPT)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=1")
else ()
- if (HAVE_OPENSSL_EVP_CHACHA20 AND HAVE_OPENSSL_EVP_POLY1305)
+ if (HAVE_OPENSSL_EVP_CHACHA20)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
else ()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=1")
endif ()
-
- if (HAVE_OPENSSL_ED25519)
- list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=0")
- else ()
- list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
- endif ()
-
- if (HAVE_OPENSSL_X25519)
- list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=0")
- else ()
- list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=1")
- endif ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=0")
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=0")
endif ()
if (NOT OSX)
diff --git a/tests/external_override/torture_override.c b/tests/external_override/torture_override.c
index f351d52e..8024574b 100644
--- a/tests/external_override/torture_override.c
+++ b/tests/external_override/torture_override.c
@@ -56,7 +56,7 @@ static int sshd_setup(void **state)
s = *((struct torture_state **)state);
s->private_data = test_state;
- test_state->orig_dir = strdup(torture_get_current_working_dir());
+ test_state->orig_dir = torture_get_current_working_dir();
assert_non_null(test_state->orig_dir);
temp_dir = torture_make_temp_dir(template);
@@ -137,7 +137,7 @@ static void test_algorithm(ssh_session session,
const char *cipher,
const char *hostkey)
{
- char data[256];
+ char data[256] = {0};
int rc;
if (kex != NULL) {
@@ -161,7 +161,7 @@ static void test_algorithm(ssh_session session,
assert_ssh_return_code(session, rc);
/* send ignore packets of all sizes */
- memset(data, 'A', sizeof(data));
+ memset(data, 'A', sizeof(data) - 1);
ssh_send_ignore(session, data);
ssh_handle_packets(session, 50);
@@ -195,8 +195,8 @@ static void torture_override_chacha20_poly1305(void **state)
internal_poly1305_called = internal_poly1305_function_called();
#if SHOULD_CALL_INTERNAL_CHACHAPOLY
- assert_true(internal_chacha20_called ||
- internal_poly1305_called);
+ assert_true(internal_chacha20_called);
+ assert_true(internal_poly1305_called);
#else
assert_false(internal_chacha20_called ||
internal_poly1305_called);
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
index 5982e81c..40a6eb13 100644
--- a/tests/fuzz/CMakeLists.txt
+++ b/tests/fuzz/CMakeLists.txt
@@ -1,7 +1,7 @@
project(fuzzing CXX)
macro(fuzzer name)
- add_executable(${name} ${name}.cpp)
+ add_executable(${name} ${name}.c)
target_link_libraries(${name}
PRIVATE
ssh::static)
@@ -15,9 +15,22 @@ macro(fuzzer name)
else()
target_sources(${name} PRIVATE fuzzer.c)
# Run the fuzzer to make sure it works
- # add_test(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name} EXAMPLE)
+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${name}_corpus")
+ file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/${name}_corpus/*")
+ set(i 0)
+ foreach(file ${files})
+ add_test(${name}_${i}
+ ${CMAKE_CURRENT_BINARY_DIR}/${name} ${file})
+ math(EXPR i "${i} + 1")
+ endforeach()
+ endif()
endif()
endmacro()
fuzzer(ssh_client_fuzzer)
fuzzer(ssh_server_fuzzer)
+fuzzer(ssh_client_config_fuzzer)
+fuzzer(ssh_bind_config_fuzzer)
+fuzzer(ssh_known_hosts_fuzzer)
+fuzzer(ssh_privkey_fuzzer)
+fuzzer(ssh_pubkey_fuzzer)
diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md
index c68ef3d1..088ce27d 100644
--- a/tests/fuzz/README.md
+++ b/tests/fuzz/README.md
@@ -95,7 +95,7 @@ You can either pick up my branch or workaround them locally:
### Reproduce locally
Clone the above repository from https://github.com/google/oss-fuzz/, apply
-changes from previous secion if needed, setup local clone of libssh repository
+changes from previous section if needed, setup local clone of libssh repository
and build the fuzzers locally (where `~/devel/libssh` is path to local libssh
checkout):
@@ -111,7 +111,7 @@ This should give you the same error/leak/crash as you see on the testcase
detail in oss-fuzz.com.
I find it very useful to run libssh in debug mode, to see what happened and
-what exit path was taken to get to the error. Fortunatelly, we can simply
+what exit path was taken to get to the error. Fortunately, we can simply
pass environment variables to the container:
python infra/helper.py reproduce -eLIBSSH_VERBOSITY=9 libssh ssh_client_fuzzer ~/Downloads/clusterfuzz-testcase-ssh_client_fuzzer-4637376441483264
diff --git a/tests/fuzz/ssh_bind_config_fuzzer.c b/tests/fuzz/ssh_bind_config_fuzzer.c
new file mode 100644
index 00000000..3d0d8be8
--- /dev/null
+++ b/tests/fuzz/ssh_bind_config_fuzzer.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 Jakub Jelen <jjelen@redhat.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LIBSSH_STATIC 1
+#include "libssh/libssh.h"
+#include "libssh/server.h"
+#include "libssh/bind_config.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ ssh_bind bind = NULL;
+ char *input = NULL;
+
+ input = (char *)malloc(size + 1);
+ if (!input) {
+ return 1;
+ }
+ strncpy(input, (const char *)data, size);
+ input[size] = '\0';
+
+ ssh_init();
+
+ bind = ssh_bind_new();
+ assert(bind != NULL);
+
+ ssh_bind_config_parse_string(bind, input);
+
+ ssh_bind_free(bind);
+ ssh_finalize();
+
+ free(input);
+
+ return 0;
+}
diff --git a/tests/fuzz/ssh_client_config_fuzzer.c b/tests/fuzz/ssh_client_config_fuzzer.c
new file mode 100644
index 00000000..62eae93c
--- /dev/null
+++ b/tests/fuzz/ssh_client_config_fuzzer.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 Stanislav Zidek <szidek@redhat.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LIBSSH_STATIC 1
+#include "libssh/libssh.h"
+#include "libssh/options.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ ssh_session session = NULL;
+ char *input = NULL;
+
+ input = (char *)malloc(size+1);
+ if (!input) {
+ return 1;
+ }
+ strncpy(input, (const char *)data, size);
+ input[size] = '\0';
+
+ ssh_init();
+
+ session = ssh_new();
+ assert(session != NULL);
+
+ /* Make sure we have default options set */
+ ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "example.com");
+
+ ssh_config_parse_string(session, input);
+
+ ssh_free(session);
+ ssh_finalize();
+
+ free(input);
+
+ return 0;
+}
diff --git a/tests/fuzz/ssh_client_fuzzer.cpp b/tests/fuzz/ssh_client_fuzzer.c
index 8480223c..e69bf385 100644
--- a/tests/fuzz/ssh_client_fuzzer.cpp
+++ b/tests/fuzz/ssh_client_fuzzer.c
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
+#include <stdbool.h>
#define LIBSSH_STATIC 1
#include <libssh/libssh.h>
@@ -83,7 +84,7 @@ static void select_loop(ssh_session session, ssh_channel channel)
ssh_event_free(event);
}
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
ssh_session session = NULL;
ssh_channel channel = NULL;
@@ -92,6 +93,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
ssize_t nwritten;
bool no = false;
int rc;
+ long timeout = 1; /* use short timeout to avoid timeouts during fuzzing */
+
+ /* This is the maximum that can be handled by the socket buffer before the
+ * other side will read some data. Other option would be feeding the socket
+ * from different thread which would not mind if it would be blocked, but I
+ * believe all the important inputs should fit into this size */
+ if (size > 219264) {
+ return -1;
+ }
/* Set up the socket to send data */
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
@@ -128,6 +138,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
assert(rc == 0);
rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &no);
assert(rc == 0);
+ rc = ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout);
+ assert(rc == 0);
ssh_callbacks_init(&cb);
ssh_set_callbacks(session, &cb);
@@ -143,7 +155,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
}
channel = ssh_channel_new(session);
- assert(channel != NULL);
+ if (channel == NULL) {
+ goto out;
+ }
rc = ssh_channel_open_session(channel);
if (rc != SSH_OK) {
@@ -151,7 +165,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
}
rc = ssh_channel_request_exec(channel, "ls");
- assert(rc == SSH_OK);
+ if (rc != SSH_OK) {
+ goto out;
+ }
select_loop(session, channel);
diff --git a/tests/fuzz/ssh_known_hosts_fuzzer.c b/tests/fuzz/ssh_known_hosts_fuzzer.c
new file mode 100644
index 00000000..fabdc28f
--- /dev/null
+++ b/tests/fuzz/ssh_known_hosts_fuzzer.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 Jakub Jelen <jjelen@redhat.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LIBSSH_STATIC 1
+#include "libssh/libssh.h"
+#include "knownhosts.c"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char *hostname = NULL;
+ const uint8_t *hostname_end = NULL;
+ size_t hostname_len = 0;
+ char filename[256];
+ struct ssh_list *entries = NULL;
+ struct ssh_iterator *it = NULL;
+ FILE *fp = NULL;
+
+ /* Interpret the first part of the string (until the first NULL byte)
+ * as a hostname we are searching for in the file */
+ hostname_end = memchr(data, '\0', size);
+ if (hostname_end == NULL) {
+ return 1;
+ }
+ hostname_len = hostname_end - data + 1;
+ if (hostname_len > 253) {
+ /* This is the maximum valid length of a hostname */
+ return 1;
+ }
+ hostname = malloc(hostname_len);
+ if (hostname == NULL) {
+ return 1;
+ }
+ memcpy(hostname, data, hostname_len);
+
+ snprintf(filename, sizeof(filename), "/tmp/libfuzzer.%d", getpid());
+ fp = fopen(filename, "wb");
+ if (!fp) {
+ free(hostname);
+ return 1;
+ }
+ fwrite(data + hostname_len, size - hostname_len, 1, fp);
+ fclose(fp);
+
+ ssh_init();
+
+ ssh_known_hosts_read_entries(hostname, filename, &entries);
+ for (it = ssh_list_get_iterator(entries);
+ it != NULL;
+ it = ssh_list_get_iterator(entries)) {
+ struct ssh_knownhosts_entry *entry = NULL;
+
+ entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
+ ssh_knownhosts_entry_free(entry);
+ ssh_list_remove(entries, it);
+ }
+ ssh_list_free(entries);
+
+ ssh_finalize();
+
+ free(hostname);
+ unlink(filename);
+
+ return 0;
+}
diff --git a/tests/fuzz/ssh_known_hosts_fuzzer_corpus/d7c0eade3f3b70d94b1a7090e09eb8607da0ace4 b/tests/fuzz/ssh_known_hosts_fuzzer_corpus/d7c0eade3f3b70d94b1a7090e09eb8607da0ace4
new file mode 100644
index 00000000..18e779e0
--- /dev/null
+++ b/tests/fuzz/ssh_known_hosts_fuzzer_corpus/d7c0eade3f3b70d94b1a7090e09eb8607da0ace4
Binary files differ
diff --git a/tests/fuzz/ssh_privkey_fuzzer.c b/tests/fuzz/ssh_privkey_fuzzer.c
new file mode 100644
index 00000000..b65d680d
--- /dev/null
+++ b/tests/fuzz/ssh_privkey_fuzzer.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 Jakub Jelen <jjelen@redhat.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LIBSSH_STATIC 1
+#include "libssh/libssh.h"
+#include "libssh/priv.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ ssh_key pkey = NULL;
+ uint8_t *input = NULL;
+ int rc;
+
+ input = bin_to_base64(data, size);
+ if (input == NULL) {
+ return 1;
+ }
+
+ ssh_init();
+
+ rc = ssh_pki_import_privkey_base64((char *)input, NULL, NULL, NULL, &pkey);
+ free(input);
+ if (rc != SSH_OK) {
+ return 1;
+ }
+ ssh_key_free(pkey);
+
+ ssh_finalize();
+
+ return 0;
+}
+
diff --git a/tests/fuzz/ssh_privkey_fuzzer_corpus/855ce609b52aec530bf631a78da7038bed99040a b/tests/fuzz/ssh_privkey_fuzzer_corpus/855ce609b52aec530bf631a78da7038bed99040a
new file mode 100644
index 00000000..2759f43e
--- /dev/null
+++ b/tests/fuzz/ssh_privkey_fuzzer_corpus/855ce609b52aec530bf631a78da7038bed99040a
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCLo6vx1lX6ZZoe05lWTkuwrJUZN0T8hEer5UF9KPhOVgAAAKg+IRNSPiET
+UgAAAAtzc2gtZWQyNTUxOQAAACCLo6vx1lX6ZZoe05lWTkuwrJUZN0T8hEer5UF9KPhOVg
+AAAED2zFg52qYItoZaSUnir4VKubTxJveL9D2oWK7Prg/O24ujq/HWVfplmh7TmVZOS7Cs
+lRk3RPyER6vlQX0o+E5WAAAAHmpqZWxlbkB0NDcwcy5qamVsZW4ucmVkaGF0LmNvbQECAw
+QFBgc=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/tests/fuzz/ssh_pubkey_fuzzer.c b/tests/fuzz/ssh_pubkey_fuzzer.c
new file mode 100644
index 00000000..70c94948
--- /dev/null
+++ b/tests/fuzz/ssh_pubkey_fuzzer.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2023 Jakub Jelen <jjelen@redhat.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LIBSSH_STATIC 1
+#include "libssh/libssh.h"
+#include "libssh/misc.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ ssh_key pkey = NULL;
+ char *filename = NULL;
+ int fd;
+ int rc;
+ ssize_t sz;
+
+ ssh_init();
+
+ filename = strdup("/tmp/libssh_pubkey_XXXXXX");
+ if (filename == NULL) {
+ return -1;
+ }
+ fd = mkstemp(filename);
+ if (fd == -1) {
+ free(filename);
+ close(fd);
+ return -1;
+ }
+ sz = ssh_writen(fd, data, size);
+ close(fd);
+ if (sz == SSH_ERROR) {
+ unlink(filename);
+ free(filename);
+ return -1;
+ }
+
+ rc = ssh_pki_import_pubkey_file(filename, &pkey);
+ if (rc != SSH_OK) {
+ unlink(filename);
+ free(filename);
+ return 1;
+ }
+ ssh_key_free(pkey);
+ unlink(filename);
+ free(filename);
+
+ ssh_finalize();
+
+ return 0;
+}
+
diff --git a/tests/fuzz/ssh_pubkey_fuzzer_corpus/b2c9f01394a2835b2cd7c520395a4977143e8d23 b/tests/fuzz/ssh_pubkey_fuzzer_corpus/b2c9f01394a2835b2cd7c520395a4977143e8d23
new file mode 100644
index 00000000..accd5b65
--- /dev/null
+++ b/tests/fuzz/ssh_pubkey_fuzzer_corpus/b2c9f01394a2835b2cd7c520395a4977143e8d23
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIujq/HWVfplmh7TmVZOS7CslRk3RPyER6vlQX0o+E5W jjelen@t470s.jjelen.redhat.com
diff --git a/tests/fuzz/ssh_server_fuzzer.cpp b/tests/fuzz/ssh_server_fuzzer.c
index 26498df6..aa84b8d2 100644
--- a/tests/fuzz/ssh_server_fuzzer.cpp
+++ b/tests/fuzz/ssh_server_fuzzer.c
@@ -24,6 +24,7 @@
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
+#include <stdbool.h>
#define LIBSSH_STATIC 1
#include <libssh/libssh.h>
@@ -117,7 +118,7 @@ static int write_rsa_hostkey(const char *rsakey_path)
return 0;
}
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
int socket_fds[2] = {-1, -1};
ssize_t nwritten;
@@ -138,6 +139,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
.channel_open_request_session_function = channel_open,
};
+ /* This is the maximum that can be handled by the socket buffer before the
+ * other side will read some data. Other option would be feeding the socket
+ * from different thread which would not mind if it would be blocked, but I
+ * believe all the important inputs should fit into this size */
+ if (size > 219264) {
+ return -1;
+ }
+
/* Write SSH RSA host key to disk */
rc = write_rsa_hostkey("/tmp/libssh_fuzzer_private_key");
assert(rc == 0);
@@ -168,8 +177,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
assert(rc == 0);
}
rc = ssh_bind_options_set(sshbind,
- SSH_BIND_OPTIONS_RSAKEY,
- "/tmp/libssh_fuzzer_private_key");
+ SSH_BIND_OPTIONS_HOSTKEY,
+ "/tmp/libssh_fuzzer_private_key");
assert(rc == 0);
rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_C_S, "none");
assert(rc == 0);
@@ -185,6 +194,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
ssh_set_auth_methods(session, SSH_AUTH_METHOD_NONE);
ssh_callbacks_init(&server_cb);
+ ssh_set_server_callbacks(session, &server_cb);
rc = ssh_bind_accept_fd(sshbind, session, socket_fds[0]);
assert(rc == SSH_OK);
@@ -196,7 +206,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
ssh_event_add_session(event, session);
size_t n = 0;
- while(sdata.authenticated == false || sdata.channel == NULL) {
+ while (sdata.authenticated == false || sdata.channel == NULL) {
if (sdata.auth_attempts >= 3 || n >= 100) {
break;
}
diff --git a/tests/keys/certauth/id_rsa b/tests/keys/certauth/id_rsa
index aa86ac2b..a0b679c2 100644
--- a/tests/keys/certauth/id_rsa
+++ b/tests/keys/certauth/id_rsa
@@ -1,27 +1,27 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEiwIBAAKB/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB
-1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWr
-FIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6
-HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8
-jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/l
-Hb2V/HP/pyIpao19A4daR0a4ia9Hk4UCAwEAAQKB/QKEaPxjrKzlWoQSWRdUaQY5
-Idyy7yw9hiMa9BK1COh/u66XVlY86Fwb9puR5Fu/WF67WIuX1PpizJXkLBBRtuDs
-lvY2BjrPQ/MONtc3JPYp4vbFXYxtAzh6zrTPhMVfcjV7Jr1XWZ+lEVOmhR2G4gvk
-P2WDozIKWub3jMLTt4afgHCGaKfKEUpKjFkiAalz8oLVv8qV1FVPPDT2PWeKMuE3
-XfoN7YUaP6+aPlNnjIv/3BDsrPsiKZ+AKXcERdPvVQa/LypzW08cqC6sIJKWVmQI
-3KgoYs9VvbDXfQ8jKfcsTApZkSDaLX6tf3Ei+76R0lbV4L1rpypa25qj9YECfwHP
-N+v/6yObJFL5/1rEuT7CFbfP8g5J8qUVufcPRKv//ChluLuWNxgLJmIv2ZffWwhe
-GKHlT98QPgFvsMSOyLeut4beZYKDSeVNvEt9eCBjOax2jOBGo3hv8j/Fs8yAfZOV
-Ardv2qUszubM+DVwjJzb3vaZyEesRucJISqkJeUCfwGzGdMp0LXrZ7aaQGgHj/P2
-DKGq0E2gnj/EBapatjxKm4hMRn/vkTWjCDCryTnvJqkW/00tr4GqWXoeilBFD270
-RcvbOe9LQmGlHIYzgwc5nfLDBQyeNnHRmkeD9LQRUfdTdHj4jf+35pHlsVUT0Rnl
-IMNoRA6V07bySFdI3SECfiF+1rbrxuhaCRIA0Ax3pL0eGuuTgksAm8VlbCMTgSiC
-kF1CrXXgSAHOZb02C9Bf4cwEFfjh/KxM/4eXDa+Rfg7JQJxmVLivqEAlxIOvIxBp
-xDnSWAljmrrllozyQnBsJDbbOm6BLf5+e5wIuryHvnP7vHNEU0J24g/78PxrrQJ/
-AVD4OzYzUfESzbUBFJBmyIZSmhJ0aOpwJOpniNvgLymI8zI/l22uhF/TQ/6HRbsV
-sfcBmoA7YKzRx2ZHsIsLvN6p/4u1fsJGkuERCk5yt/HDhfPLwU321IeEeMaVia+w
-T1/u4JF/SADhLTU69az3UJrHmQ7zRmh7I0DZDeB8gQJ+XqIqutPeerNtbqMjXGW8
-TdpqZAzAQAv6dPgaH0W0OzJe2hP9uy0D84H5f8Im/irJh/AXo/QL3obXqopyeLf0
-HfcUUnEZEBPlqsirZFtPClD+HL6Orf1je0oVV/aQssPkQl6/aXBNd+kS27U3NBML
-LmRhC4+Q+/M5MlRggLtn
------END RSA PRIVATE KEY-----
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAQEA0DehrU/ohoimMKojFXdo1uEAcqx4fS87AjDUz8t4s436ppP+0+U3
++qrhOCE/mXZvXewjTHltmEtCHNSbsWhYTjwrEQUDRNOVahn2PEQTcX/9itvsv9PX79Imbv
+ZsLR0f1FsmorkWpnDQmuga7hYBFBj4sV+VML5ieoK2OraUEq46ILsxqRgUTetkErlzX7S2
+SPE7vNM0ahmA+HNBuKNUD+BOtCzkqN54flGA9TZ7kapC7xqiRHK+ZzahQ2PFR4BxbVP1uT
+DsanbjKOpBC4hISao3hi4iUnyj0gJ8itmkhQS+oI/2KWSGW01/k9W7jOUXDSt7LGUTSW6s
+ILYHzmefCwAAA9B/6IFvf+iBbwAAAAdzc2gtcnNhAAABAQDQN6GtT+iGiKYwqiMVd2jW4Q
+ByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE
+05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6gr
+Y6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuR
+qkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/
+YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58LAAAAAwEAAQAAAQEAxjzxFU0LGWtortSN
+apaxnkPCZWuHm8gn6kILm3shg/IdPhORfrSxw1qF6ybcooN8LHPyd5D0oxaj70cMpK+vw2
+zNo/qdzh2UF9x375Dw4hL1lgslMM3EvXPbW7IJ9DnSYCAYfLyzr+ug8JsjaKJSjIvp2xYh
+uLLKl9FzJhtGhzDaJr9FCbSmd5R7Telz4En0Lwo/VxYvyzCoRwzhVUVqJZpdtF7/1du4tT
+agfPzPYY9zM9muR7AawtzMc4UFvMzl1OtjOHYtqSMVBZx44fRpXT3/fy7A98+7erd0zTWj
+s6gaz6I8VmRPk4iTdBH4KBzC8dGZQNMrY9SQ/ZANet8eYQAAAIBI8hS6bX00NpNXwOaEqr
+jZKf/u1W71KHpYBAY1w3xanGqPVOX5PEsFH6NLjqSLF75Bk22pvJvZ8EAaoISyvLSqqO5t
+1vvjCjVKALSaVIFDcA20NpCXRugmVT1HeQNKHCTt3yOoraL8Sh9wlRbxLmlxgISYS6uH6e
+dEPa6qFshMVwAAAIEA7RFx7+mZJfrSJUu9pYiZJc6+Ns2WrSA2mgI+mIhWqreDK4Kw0a5g
+akqD0mb9oPHySnf3lCe+17yqNxH2fcX0G3B5LxiRnFVNm2wC4ZGvb+yMU2+0uSI/Sf8L1N
+sfWm+z4VC93Qhe0fIpuk8JAMNOwCvFcEFu5rr9sxHtPWjWtj0AAACBAODYXjs6jqsvW+3P
+e0efFp9kIezi8CejSxVXX0/zmWMCpTw1laiUmK41coKTKBSDZcNgrsF/ns1uCrPg6C2u/y
+evF8J+DeqU3vo1QhnRAJA2fLZk1Dr/GfAsp9mS9w6FdIQiQjQ0f/X9rYgAr9x4qMogYgrG
+zkb7k6FUoGgD7sbnAAAAE2xpYnNzaF90b3J0dXJlX2F1dGgBAgMEBQYH
+-----END OPENSSH PRIVATE KEY-----
diff --git a/tests/keys/certauth/id_rsa-cert.pub b/tests/keys/certauth/id_rsa-cert.pub
index 615d39f0..ceb44039 100644
--- a/tests/keys/certauth/id_rsa-cert.pub
+++ b/tests/keys/certauth/id_rsa-cert.pub
@@ -1 +1 @@
-ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgHZLan4ufbTFWr8Hl/8JvZTLYa0eNNm2qov9zPlK7qfwAAAADAQABAAAA/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWrFIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/lHb2V/HP/pyIpao19A4daR0a4ia9Hk4UAAAAAAAAAAAAAAAEAAAATdG9ydHVyZV9hdXRoX2NhcmxvcwAAAAkAAAAFYWxpY2UAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKcDafm8fNluz8a9GQaWgk1XUJcchLleeubTke6xQlJbI+rcjWIIwd1gDuh7Mdr0YIVhsh6dpg/L4bpRJBGNhDPxK8BmjTpIU14lKxrWQAirHN09P2QGtGtgrf09lA+xhV9E+pkF2Zz6PCt/P3sgUQnJcwjjsWhMaSASrt67fPanH+10hnfgjkevkMMHGJxmLiOW7JFQkd9I+gHHKEXs6Q9fhtiStzr3WN4hAPG5uXrnRZgseAV9p3TFPMEgUTpdRvnkOnkCBF169KiyjU97QgoXHExWk/rrgsJtgrTou/qRyi18WWm9S1HXLHyNOgZxKirmxLNPC9dIcJBD1kDWG8UAAAEPAAAAB3NzaC1yc2EAAAEAhNLOXT0jyz/Web0HUyrtPCvUZsLkDyBWCNoNTfsxGVoYsE4WCpNwqQO1A4NT5AtIE+R7rn9wfjvXM7sYh6hJyq3HVEWhts1SkQVU7sQBrImTIrj2cWKR3gmQ+ehsgNFGhcFZTK77ugw1fMfzZRvKVTkRWhe6v92wQOtkoINtf3f1fK6xY+vLwAA/E4VdaRJmhwAaNpy3PfMAJytkCLjcjUSWHYDha4hs98/EBPduGNNNiZdyG7lcpSvvq9HBDxzOiHBa/We9m38/Dk4TNVkZ/wrtBFQxH75if6SgGa/feGJrKQHBru7sPh8dO4R1AmZaoLmRzMnzZOtB0oEXmBqHmw== libssh_torture_auth
+ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgllK1Wz9hM1kUks5QXU/vXbDmzpQWMtFWObMvi9ymg38AAAADAQABAAABAQDQN6GtT+iGiKYwqiMVd2jW4QByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6grY6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuRqkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58LAAAAAAAAAAAAAAABAAAAE3RvcnR1cmVfYXV0aF9jYXJsb3MAAAAJAAAABWFsaWNlAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCnA2n5vHzZbs/GvRkGloJNV1CXHIS5Xnrm05HusUJSWyPq3I1iCMHdYA7oezHa9GCFYbIenaYPy+G6USQRjYQz8SvAZo06SFNeJSsa1kAIqxzdPT9kBrRrYK39PZQPsYVfRPqZBdmc+jwrfz97IFEJyXMI47FoTGkgEq7eu3z2px/tdIZ34I5Hr5DDBxicZi4jluyRUJHfSPoBxyhF7OkPX4bYkrc691jeIQDxubl650WYLHgFfad0xTzBIFE6XUb55Dp5AgRdevSoso1Pe0IKFxxMVpP664LCbYK06Lv6kcotfFlpvUtR1yx8jToGcSoq5sSzTwvXSHCQQ9ZA1hvFAAABFAAAAAxyc2Etc2hhMi01MTIAAAEAeI9eUAWyL5DVWBj0vU2kKdGxMXQx1Y8R68DXwXhnxfLwilJPa6IFg+g988lpF5aZzvAiX6TgDtJAhzfuBU+ZREGfdclUQIpz3xwDG76Gmg/DpQHdmqU76n2Na32s6+4SsSmWWKx6cPPdjbCRS0VMSrMohLuDyPGMoC7RjLfDDxqW5TIbMtqQdOiPl/0PpR73Q0FjB50Ec12buQDkExlEOi2Y+yB830vuTJN3ds7bx6NXM1Jjftg/8D0SzNRAIYDQFnpyXKO6kNrN66o48E3mrnVHXuFBTf+kpdYrK+1LKQVk/hVLBHr+NuVmQltL0zcjJfiXj7i5ZqcsqR1UAU5hKg== libssh_torture_auth
diff --git a/tests/keys/certauth/id_rsa.pub b/tests/keys/certauth/id_rsa.pub
index 4cbfc4ce..3f83c6fc 100644
--- a/tests/keys/certauth/id_rsa.pub
+++ b/tests/keys/certauth/id_rsa.pub
@@ -1 +1 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAA/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWrFIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/lHb2V/HP/pyIpao19A4daR0a4ia9Hk4U= libssh_torture_auth
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQN6GtT+iGiKYwqiMVd2jW4QByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6grY6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuRqkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58L libssh_torture_auth
diff --git a/tests/keys/id_rsa_protected b/tests/keys/id_rsa_protected
index cdf5c2b8..034cb287 100644
--- a/tests/keys/id_rsa_protected
+++ b/tests/keys/id_rsa_protected
@@ -1,28 +1,30 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBjmItEMS
-YKDxy/7xvsZY+uAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCz98jP4bLz
-1eNSFd5s2rauzUrREkRlcNt9yh9vXcRIMn19Jt35GUJQzqL5+gRVXbfFZ1qd2zYGSfva0a
-Kclp0iA5ZT6SjGn6BGa0ksT842IAolCpErd44k0EfoC33o0yongbC/nobhbry4+APBRVDB
-UhzoRzpHKmLPsMT5L76BK8FAhVRC3teQ9xc7I3nO6PmoOFkziXpXs6D0taPj/YgXlpy8qN
-8gyl6qaen3PoFNhlC25BTpvVW4RiFfK8zouQzCd2xUaHjqQMoyZFCHIDwDqq8sCWIwyrzy
-TmBHgB4l5OeoNH9DXbQjo8ypg2XpMtOTz8qic448NH9dcZveIXrvAAADwCLre52Jer2DTQ
-TJi91b/xNm5NRuW9366ZdoOC5NdWtbQFk4YJmdImEDo8k1t3Re24rVNxLMQwHwZX4ZLISl
-/e49RtSd6TDP44FkQF4NgtCjLUdmEWRTQj0mtENGto+wdLpL25HkmmI5WGrQU9SufVhhvj
-TxKi6ediSXIXEA5bSrWNvUaw084TT3ZfP9g98/6wr9tAYL1jVfTFUabvZzCR6+wRVoJIVc
-/+uN1bubj+IdOzYSm9Dhj4kUlK+KvI4GtouCzjuEZosjvn0ino3du1vgyT7SPdjmDxtIds
-YI7YiB1Xy3QcWdWFk+SoXhDizf9pupo2r1+G50GoBuXg2ELdsKBLXtxQ9bh37JyAcLzagq
-iVMCJjk3XMZvNXhdELRqLeWyhQ7U1BCtUBatbem0VsH6hQZ/pHReX2We8/GAUQkh4ZN8U2
-lkta9v5cb7XaBm49JjzIa3WeOS+tFHIUAWqd7MQ4f2FCTMhBssLAM7EJDOUXyo6938pa75
-+LvdLZRUycE8d/PWG9SuFWSe4CJJrRlBQqPEwx9OPtKNNKgsXIGVKAFLXe+nJ4z6RXTR3R
-IGe0uaf8v9Jra5j22rq/dbQG1fP1fZNcCnIZQQo6olLaoyQmGCboC8CiCz1PNTsC1+r4pB
-oaRiCx5/qLF6EXQ03mdEqL1L/R+KMDa2+Ncw2hCSRU3GBby4wXmSqFsboRy5uxJB5sK0Ut
-sI3FW48k9zijiqVpdysRkalVVSQj8ymTG9LbjjEEmE7qxRf2dZCEnS/iPFUIu7iO9ISiOm
-4ThpROBspNyHMXKFR6mKArJX1vIwjehlaLAXA3UMY9PEFRDrWQcbatGWj4f/L6e3Tq+n7a
-t0djAgKlh40IvVL+Xf+Bsv8vUr7HAbKnOxpX69nEShiJqR5YWlEPXba+JCOjryE2ycoRB6
-d2d0SgDlB1M04uUmv2sy2Kw/CcSNHPLKGiYqqv8DAZ4GiKH2rI4oWvH9z2uRuQni98/Gw1
-1D5/QwJOHpqrUnVat4JXPBeTqiHYYtbTtqJLeIPX+Dsa6tbdjEOVgx2FkH3104xMwJyUKb
-Ccip4AbWsTwfM4GVPnJE6WCBcXC5WR6AOzuEEDQjhyzLs5K7RVb7irfhHa4Vs1/2LvxnRT
-dmTzdv/mhUNqS9RIPmFttfsSveDqY0P6WOn+K6FcCHQjpFJ3pK08glD+Sx4cbFv3lUQLfw
-hsjL0P+p+M+gTqeJ1kb2z87fiS03mHMV15lmb7nzoqyeJLIukV1jidWdGxf0efnQfmUVfX
-Wa+ehGaw==
------END OPENSSH PRIVATE KEY-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,0B181CC88F75C33B7DEBE5C18B481F18
+
+rYtUw8FhEv48JmNTm3i1TEqEgElC629iaMQu/YzRV5zL/n83HwMKbRpAZ31Cch2a
+8thZRQ6YsL/56vr+fKKVgDF7y3wmStp5sVkOQXMeZ9D746ZEGcYGnYH8JQhibDDB
+sTK2kQrmBERg7H8rOoNCzxxoK+VZl2Z+S+yLVq0//qxBfuluZwCdk9Tie69Cd/Dk
+PeBjOVPnFCavCKCWpUs/So8VQq9jXG84hRltwC0htSTEq+xfgNtS64f63WL6gEnB
+WZ5KSO1gyzKC5/YAB6LXPxIIVzfZYXiuOWV/t8DXZK/lvhqQ3gSyPZezSrX8wEMC
+xQeX41etQGjCcgxWH41iPCNTuoIKo2t/BPlfLJilJotmUSnYOxDmkZbLabuyS+0p
+WGtnEwFSrxQosx6u9GBHX94Ikex0bf00KzNpKExzAIRqTdesaviJ1QX/pRsvT/Xp
+TtH2aWV5kYNc+B+BrCQU7mlx/eEtXR2H5zJQxLSrTVKb1vUIHytufnPePk2BkcQ2
+CTE1xT+ZkUaY1WiCBxWgVTflL5FY9E6BerKEGVSfloso8tGCgsoO/Fch0Ho5/bXp
+T+3nQEY780KduKJ8xCJJDQgD8GbjNR6sCtcPrewqEsgrpAbJUKyXhU7klGC09zzI
+/JnNmdd10w2l/5A92GGrCgXnTYb8/w9J/qa6qyAAYU9/8rPo7ErGb7mKclmzz63j
+cksImoExfrr9CIr7wjrXFO0OoupmMegNOZtgwsN7i0FI8vWYc6a3IaFWSWfE29Ux
+rw9TK9L9pDvhCqS/WjW86S25muqnTSMQ/bhmiPw8z8tOjdi2YRqNcU2TyWoB2Mct
+W+w9G5dSukMwkXQ2RNjDo2GfuXLXpUe5zCVixI2wxYGvIqTGkDZn/u1Jdxy1IxNc
+qEsEZAOCVnJU1cQpB9ENsyrRUIsdQVWNQSvsUZz2XSELULwIFTcCTHr2PAJ5xzZ6
+VQy3DGEpZf7+yGACoi8LY8f5Ve5C9NciyA4/C/uvOUd7PhAf4g41mKw8+bAr8NFt
+ubeXTo0iI29FkmmebfM1sRBHvomGT7qYsHBW2pgqBrm3X9kFcQ9EFhr6S2ULMcIn
+4iX1mbqvC0c1CUmZakkNg94FQp2zbUclAuDkg3BTA0gwbyudvx0ccBmzQ43/6AJ5
+xz1hrfusX5Vcjz6+i5WHJDK/mlUDwTV5GAhcmar9eEcFXJEosD+mrAalflz3Vc2X
+5A9plGfKkaFdth8YUGjLr+O2O5ggkDpCMbjYo4HQ6/dslYvqvnavJYrRKrEZbtvj
+8fR5E11tPrK1aKzPHO0VLKf4UHs57JNqicSlYGy78FSCPG4d17KQlFyzbXsfbsvp
+9EQK4N2jwRNZAOHuTuoqQ8TNzDahdlmbBS2Akd3rVV9H1/eNeN3r6Demww+yixoy
+uPhjofn0P28eH7Gqiyhh20QYYqG7aky9IYMPnIBtA1hJp9MtMa1m8aHGxxZrUigj
+S62Q34JzA8A6Rwc2kTHRzXG2o6oQ3vCQfy0JGlmDlG2yofcn7YgrMCv+srTniuiA
+YBnOeic5cllYnDB9bpF2kufJT6CigoxP18HIw+jhYabuOTHO67MYf2En+is8vlQS
+-----END RSA PRIVATE KEY-----
diff --git a/tests/keys/ssh_host_dsa_key b/tests/keys/ssh_host_dsa_key
deleted file mode 100644
index 9ee8bd86..00000000
--- a/tests/keys/ssh_host_dsa_key
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBuwIBAAKBgQD7vBS+d/eJP6wK2VQw+8AIfgCw9IR50utLRkkrWbfDdiM7V+fp
-tJYKCyqZT9j9ANhqicB2tuqAI6WJBZMaGekxWfI30JxPkHZrrwbdFzlRbjav07lg
-IKqWgcz81iVPmfn5savEoobiSFjJNMmYcizjKZgGmyNUzlJjzF7u5qD08wIVAPFp
-6VKuv8VxNjENciUZCdEDRW/lAoGBAN/BFSBRSP9frsHID6e2NeKHqs8JDUWhCTE9
-/WQKqUUbxO2UU98CfHuf1mNlaSsrOxaBdvTeURcZZc1svhyGr1VG+NbNTDDlzTgA
-UlrzNML61TYcFXQVxgifUy+Tmh8FRGCa6Ko/EsX4ZWLTto5w1u5cPpgzSbLMco9T
-AeeNLgYNAoGAJRuawWN3+NezI7+bBe42Kjg4gVUlpS+8TTlYFbwrM1Esab7gvxHB
-/b2apbk9xIAkkqsnb+EPrXTLUdE2Y7XkEuGLLSTus2UlZKobBGBX/Ioysg5W9Fk/
-2MhI4YssRb2alar8d+gmAHPaT+D+NDd90PBfY3HqcXFEK+eDTWo1JNICFBLdsuoO
-6pObeFSOYbr38kJzZ0xG
------END DSA PRIVATE KEY-----
diff --git a/tests/keys/ssh_host_dsa_key.pub b/tests/keys/ssh_host_dsa_key.pub
deleted file mode 100644
index 5fcb413d..00000000
--- a/tests/keys/ssh_host_dsa_key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-dss AAAAB3NzaC1kc3MAAACBAPu8FL5394k/rArZVDD7wAh+ALD0hHnS60tGSStZt8N2IztX5+m0lgoLKplP2P0A2GqJwHa26oAjpYkFkxoZ6TFZ8jfQnE+QdmuvBt0XOVFuNq/TuWAgqpaBzPzWJU+Z+fmxq8SihuJIWMk0yZhyLOMpmAabI1TOUmPMXu7moPTzAAAAFQDxaelSrr/FcTYxDXIlGQnRA0Vv5QAAAIEA38EVIFFI/1+uwcgPp7Y14oeqzwkNRaEJMT39ZAqpRRvE7ZRT3wJ8e5/WY2VpKys7FoF29N5RFxllzWy+HIavVUb41s1MMOXNOABSWvM0wvrVNhwVdBXGCJ9TL5OaHwVEYJroqj8SxfhlYtO2jnDW7lw+mDNJssxyj1MB540uBg0AAACAJRuawWN3+NezI7+bBe42Kjg4gVUlpS+8TTlYFbwrM1Esab7gvxHB/b2apbk9xIAkkqsnb+EPrXTLUdE2Y7XkEuGLLSTus2UlZKobBGBX/Ioysg5W9Fk/2MhI4YssRb2alar8d+gmAHPaT+D+NDd90PBfY3HqcXFEK+eDTWo1JNI= asn@magrathea
diff --git a/tests/pkcs11/setup-softhsm-tokens.sh b/tests/pkcs11/setup-softhsm-tokens.sh
index 532c86a7..f61c5a67 100755
--- a/tests/pkcs11/setup-softhsm-tokens.sh
+++ b/tests/pkcs11/setup-softhsm-tokens.sh
@@ -5,8 +5,11 @@
TESTDIR=$1
PRIVKEY=$2
OBJNAME=$3
+TOKENLABEL=$3 # yeah. The same as object label
LOADPUBLIC=$4
-shift 4
+LIBSOFTHSM_PATH=$5
+P11_KIT_CLIENT=$6
+shift 5
PUBKEY="$PRIVKEY.pub"
@@ -14,26 +17,29 @@ echo "TESTDIR: $TESTDIR"
echo "PRIVKEY: $PRIVKEY"
echo "PUBKEY: $PUBKEY"
echo "OBJNAME: $OBJNAME"
+echo "TOKENLABEL: $TOKENLABEL"
echo "LOADPUBLIC: $LOADPUBLIC"
-# Create temporary directory for tokens
-install -d -m 0755 $TESTDIR/db
+if [ ! -d "$TESTDIR/db" ]; then
+ # Create temporary directory for tokens
+ install -d -m 0755 "$TESTDIR/db"
-# Create SoftHSM configuration file
-cat >$TESTDIR/softhsm.conf <<EOF
+ # Create SoftHSM configuration file
+ cat >"$TESTDIR/softhsm.conf" <<EOF
directories.tokendir = $TESTDIR/db
objectstore.backend = file
log.level = DEBUG
EOF
-export SOFTHSM2_CONF=$TESTDIR/softhsm.conf
+ cat "$TESTDIR/softhsm.conf"
+fi
-cat $TESTDIR/softhsm.conf
+export SOFTHSM2_CONF=$TESTDIR/softhsm.conf
-#init
-cmd='softhsm2-util --init-token --label "$OBJNAME" --free --pin 1234 --so-pin 1234'
+#init -- each object will have its own token
+cmd="softhsm2-util --init-token --label $TOKENLABEL --free --pin 1234 --so-pin 1234"
eval echo "$cmd"
-out=$(eval $cmd)
+out=$(eval "$cmd")
ret=$?
if [ $ret -ne 0 ]; then
echo "Init token failed"
@@ -42,9 +48,9 @@ if [ $ret -ne 0 ]; then
fi
#load private key
-cmd='p11tool --provider /usr/lib64/pkcs11/libsofthsm2.so --write --load-privkey "$PRIVKEY" --label "$OBJNAME" --login --set-pin=1234 "pkcs11:token="$OBJNAME""'
+cmd="p11tool --provider $LIBSOFTHSM_PATH --write --load-privkey $PRIVKEY --label $OBJNAME --login --set-pin=1234 \"pkcs11:token=$TOKENLABEL\""
eval echo "$cmd"
-out=$(eval $cmd)
+out=$(eval "$cmd")
ret=$?
if [ $ret -ne 0 ]; then
echo "Loading privkey failed"
@@ -52,15 +58,15 @@ if [ $ret -ne 0 ]; then
exit 1
fi
-cat $PUBKEY
+cat "$PUBKEY"
-ls -l $TESTDIR
+ls -l "$TESTDIR"
-if [ $LOADPUBLIC -ne 0 ]; then
+if [ "$LOADPUBLIC" -ne 0 ]; then
#load public key
- cmd='p11tool --provider /usr/lib64/pkcs11/libsofthsm2.so --write --load-pubkey "$PUBKEY" --label "$OBJNAME" --login --set-pin=1234 "pkcs11:token="$OBJNAME""'
+ cmd="p11tool --provider $LIBSOFTHSM_PATH --write --load-pubkey $PUBKEY --label $OBJNAME --login --set-pin=1234 \"pkcs11:token=$TOKENLABEL\""
eval echo "$cmd"
- out=$(eval $cmd)
+ out=$(eval "$cmd")
ret=$?
if [ $ret -ne 0 ]; then
echo "Loading pubkey failed"
@@ -69,15 +75,66 @@ if [ $LOADPUBLIC -ne 0 ]; then
fi
fi
-cmd='p11tool --list-all --login "pkcs11:token="$OBJNAME"" --set-pin=1234'
+cmd="p11tool --list-all --login \"pkcs11:token=$TOKENLABEL\" --set-pin=1234"
eval echo "$cmd"
-out=$(eval $cmd)
+out=$(eval "$cmd")
+ret=$?
+if [ $ret -ne 0 ]; then
+ echo "Logging in failed"
+ echo "$out"
+ exit 1
+fi
+echo "$out"
+
+# Skip the p11-kit if not needed
+if [ -z "$P11_KIT_CLIENT" ]; then
+ exit 0
+fi
+
+# when creating more keys, we need to restart the p11-kit
+# so it can pick up the new keys
+if [ -h "$TESTDIR/p11-kit-server.socket" ]; then
+ kill -9 "$(cat "$TESTDIR/p11-kit-server.pid")"
+ rm "$TESTDIR/p11-kit-server.socket"
+fi
+
+# p11-kit complains if there is no runtime directory
+if [ -z "$XDG_RUNTIME_DIR" ]; then
+ export XDG_RUNTIME_DIR=$PWD
+fi
+
+# Start the p11-kit server
+cmd="p11-kit server --provider $LIBSOFTHSM_PATH pkcs11:"
+echo "$cmd"
+out=$(eval "$cmd")
ret=$?
if [ $ret -ne 0 ]; then
- echo "Loging failed"
+ echo "Starting p11-kit server failed"
echo "$out"
exit 1
fi
+eval "$out"
+
+# Symlink the p11-kit-server socket to "known place"
+P11_KIT_SERVER_ADDRESS_PATH=${P11_KIT_SERVER_ADDRESS:10}
+cmd="ln -s $P11_KIT_SERVER_ADDRESS_PATH $TESTDIR/p11-kit-server.socket"
+echo "$cmd"
+out=$(eval "$cmd")
+
+# Save the PID for the C code to clean up
+cmd="echo $P11_KIT_SERVER_PID > $TESTDIR/p11-kit-server.pid"
+echo "$cmd"
+out=$(eval "$cmd")
+
+cmd="pkcs11-tool -O --login --pin=1234 --module=$P11_KIT_CLIENT --token-label=$TOKENLABEL"
+echo "$cmd"
+out=$(eval "$cmd")
+ret=$?
echo "$out"
+if [ $ret -ne 0 ]; then
+ echo "Failed to list keys through p11-kit remoting"
+ echo "$out"
+ exit 1
+fi
exit 0
diff --git a/tests/pkd/CMakeLists.txt b/tests/pkd/CMakeLists.txt
index 9e550462..f5a62653 100644
--- a/tests/pkd/CMakeLists.txt
+++ b/tests/pkd/CMakeLists.txt
@@ -5,7 +5,6 @@ if (WITH_SERVER AND UNIX AND NOT WIN32)
include_directories(${libssh_SOURCE_DIR}/include
${libssh_BINARY_DIR}/include
${CMOCKA_INCLUDE_DIR}
- ${ZLIB_INCLUDE_DIR}
${CMAKE_BINARY_DIR}
${libssh_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR})
@@ -26,7 +25,10 @@ set(pkd_libs
add_executable(pkd_hello ${pkd_hello_src})
target_compile_options(pkd_hello PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
-target_link_libraries(pkd_hello ${pkd_libs})
+target_link_libraries(pkd_hello PRIVATE ${pkd_libs})
+if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(pkd_hello)
+endif (WITH_COVERAGE)
#
# pkd_hello_i1 runs only one iteration per algorithm combination for
@@ -34,23 +36,22 @@ target_link_libraries(pkd_hello ${pkd_libs})
# specified with `-i` and may be helpful for chasing down bugs that
# are not 100% reproducible.
#
-add_test(pkd_hello_i1 ${CMAKE_CURRENT_BINARY_DIR}/pkd_hello -e -o -i1 -w /tmp/pkd_socket_wrapper_XXXXXX)
-
-#
-# pkd_hello_rekey is used to test server-side implementation of rekeying.
-#
-add_test(pkd_hello_rekey ${CMAKE_CURRENT_BINARY_DIR}/pkd_hello -t torture_pkd_openssh_rsa_rsa_default -i1 --rekey=16 -v -v -v -w /tmp/pkd_socket_wrapper_XXXXXX)
-
+add_test(pkd_hello_i1 ${CMAKE_CURRENT_BINARY_DIR}/pkd_hello -e -o -i1 -w /tmp/pkd_socket_wrapper_XXXXXX -L pkd_scratch_XXXXXX)
#
# Configure environment for cwrap socket wrapper.
#
-find_package(socket_wrapper 1.1.5 REQUIRED)
if (OSX)
set(PKD_ENVIRONMENT "DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${SOCKET_WRAPPER_LIBRARY}")
else ()
- set(PKD_ENVIRONMENT "LD_PRELOAD=${SOCKET_WRAPPER_LIBRARY}")
+ set(PKD_ENVIRONMENT "LD_PRELOAD=${SOCKET_WRAPPER_LIBRARY};OPENSSL_ENABLE_SHA1_SIGNATURES=1")
endif ()
message(STATUS "PKD_ENVIRONMENT=${PKD_ENVIRONMENT}")
set_property(TEST pkd_hello_i1 PROPERTY ENVIRONMENT ${PKD_ENVIRONMENT})
+#
+# pkd_hello_rekey is used to test server-side implementation of rekeying.
+#
+add_test(pkd_hello_rekey ${CMAKE_CURRENT_BINARY_DIR}/pkd_hello -t torture_pkd_openssh_rsa_rsa_default -i1 --rekey=16 -v -v -v -w /tmp/pkd_socket_wrapper_XXXXXX -L pkd_scratch_XXXXXX)
+set_property(TEST pkd_hello_rekey PROPERTY ENVIRONMENT OPENSSL_ENABLE_SHA1_SIGNATURES=1)
+
endif (WITH_SERVER AND UNIX AND NOT WIN32)
diff --git a/tests/pkd/pkd_daemon.c b/tests/pkd/pkd_daemon.c
index 5325a5de..ac4b53b7 100644
--- a/tests/pkd/pkd_daemon.c
+++ b/tests/pkd/pkd_daemon.c
@@ -263,16 +263,10 @@ static int pkd_exec_hello(int fd, struct pkd_daemon_args *args)
goto outclose;
}
- if (type == PKD_RSA) {
- opts = SSH_BIND_OPTIONS_RSAKEY;
- } else if (type == PKD_ED25519) {
+ if (type == PKD_RSA ||
+ type == PKD_ED25519 ||
+ type == PKD_ECDSA) {
opts = SSH_BIND_OPTIONS_HOSTKEY;
-#ifdef HAVE_DSA
- } else if (type == PKD_DSA) {
- opts = SSH_BIND_OPTIONS_DSAKEY;
-#endif
- } else if (type == PKD_ECDSA) {
- opts = SSH_BIND_OPTIONS_ECDSAKEY;
} else {
pkderr("unknown hostkey type: %d\n", type);
rc = -1;
@@ -589,7 +583,8 @@ void pkd_stop(struct pkd_result *out) {
close(pkd_state.server_fd);
rc = pthread_kill(ctx.tid, SIGUSR1);
- assert_int_equal(rc, 0);
+ assert_int_not_equal(rc, EINVAL);
+ assert_int_not_equal(rc, ENOTSUP);
rc = pthread_join(ctx.tid, NULL);
assert_int_equal(rc, 0);
diff --git a/tests/pkd/pkd_daemon.h b/tests/pkd/pkd_daemon.h
index 493326c1..2745f11a 100644
--- a/tests/pkd/pkd_daemon.h
+++ b/tests/pkd/pkd_daemon.h
@@ -12,9 +12,6 @@
enum pkd_hostkey_type_e {
PKD_RSA,
-#ifdef HAVE_DSA
- PKD_DSA,
-#endif
PKD_ED25519,
PKD_ECDSA
};
@@ -30,6 +27,8 @@ struct pkd_daemon_args {
uint64_t rekey_data_limit;
+ int original_dir_fd;
+
struct {
int list;
@@ -42,8 +41,14 @@ struct pkd_daemon_args {
unsigned int iterations;
struct {
+ const char *argv_mkdtemp_str;
char *mkdtemp_str;
} socket_wrapper;
+
+ struct {
+ const char *argv_mkdtemp_str;
+ char *mkdtemp_str;
+ } temp_dir;
} opts;
};
diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c
index 01b1b10d..069ed8df 100644
--- a/tests/pkd/pkd_hello.c
+++ b/tests/pkd/pkd_hello.c
@@ -1,15 +1,17 @@
/*
* pkd_hello.c --
*
- * (c) 2014, 2017-2018 Jon Simons <jon@jonsimons.org>
+ * (c) 2014, 2017-2018, 2022 Jon Simons <jon@jonsimons.org>
*/
#include "config.h"
+#include <fcntl.h>
#include <setjmp.h> // for cmocka
#include <stdarg.h> // for cmocka
+#include <stdint.h> // for cmocka
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h> // for cmocka
+#include <unistd.h>
#include <cmocka.h>
#include "libssh/priv.h"
@@ -33,7 +35,7 @@ static size_t default_payload_len = sizeof(default_payload_buf);
#include <argp.h>
#define PROGNAME "pkd_hello"
#define ARGP_PROGNAME "libssh " PROGNAME
-const char *argp_program_version = ARGP_PROGNAME " 2017-07-12";
+const char *argp_program_version = ARGP_PROGNAME " 2022-11-12";
const char *argp_program_bug_address = "Jon Simons <jon@jonsimons.org>";
static char doc[] = \
@@ -61,6 +63,8 @@ static struct argp_option options[] = {
"Run each test for the given number of iterations (default is 10)", 0 },
{ "match", 'm', "testmatch", 0,
"Run all tests with the given string", 0 },
+ { "temp-dir", 'L', "<mkdtemp-template>", 0,
+ "Run in a temporary directory using the given mkdtemp template", 0 },
{ "socket-wrapper-dir", 'w', "<mkdtemp-template>", 0,
"Run in socket-wrapper mode using the given mkdtemp directory template", 0 },
{ "stdout", 'o', NULL, 0,
@@ -90,6 +94,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) {
case 'l':
pkd_dargs.opts.list = 1;
break;
+ case 'L':
+ pkd_dargs.opts.temp_dir.argv_mkdtemp_str = arg;
+ break;
case 'i':
pkd_dargs.opts.iterations = atoi(arg);
break;
@@ -109,7 +116,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) {
pkd_dargs.opts.libssh_log_level += 1;
break;
case 'w':
- pkd_dargs.opts.socket_wrapper.mkdtemp_str = arg;
+ pkd_dargs.opts.socket_wrapper.argv_mkdtemp_str = arg;
break;
default:
return ARGP_ERR_UNKNOWN;
@@ -177,15 +184,6 @@ static int torture_pkd_setup_ed25519(void **state) {
return 0;
}
-#ifdef HAVE_DSA
-static int torture_pkd_setup_dsa(void **state) {
- setup_dsa_key();
- *state = (void *) torture_pkd_setup(PKD_DSA, LIBSSH_DSA_TESTKEY);
-
- return 0;
-}
-#endif
-
static int torture_pkd_setup_ecdsa_256(void **state) {
setup_ecdsa_keys();
*state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_256_TESTKEY);
@@ -217,16 +215,9 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown) \
f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown)
-#ifdef HAVE_DSA
-#define PKDTESTS_DEFAULT(f, client, cmd) \
- /* Default passes by server key type. */ \
- PKDTESTS_DEFAULT_FIPS(f, client, cmd) \
- f(client, dsa_default, cmd, setup_dsa, teardown)
-#else
#define PKDTESTS_DEFAULT(f, client, cmd) \
/* Default passes by server key type. */ \
PKDTESTS_DEFAULT_FIPS(f, client, cmd)
-#endif
#define PKDTESTS_DEFAULT_OPENSSHONLY(f, client, cmd) \
/* Default passes by server key type. */ \
@@ -308,44 +299,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown)
-#if defined(HAVE_DSA) && defined(WITH_GEX)
- /* GEX_SHA256 with RSA and ECDSA is included in PKDTESTS_KEX_FIPS if available */
-#define PKDTESTS_KEX(f, client, kexcmd) \
- /* Kex algorithms. */ \
- PKDTESTS_KEX_COMMON(f, client, kexcmd) \
- f(client, rsa_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_rsa, teardown) \
- f(client, dsa_curve25519_sha256, kexcmd("curve25519-sha256"), setup_dsa, teardown) \
- f(client, dsa_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_dsa, teardown) \
- f(client, dsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_dsa, teardown) \
- f(client, dsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384 "), setup_dsa, teardown) \
- f(client, dsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521 "), setup_dsa, teardown) \
- 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) \
- f(client, ecdsa_256_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_256, teardown) \
- f(client, ecdsa_384_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_384, teardown) \
- f(client, ecdsa_521_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_521, teardown)
-
-#elif defined(HAVE_DSA) /* && !defined(WITH_GEX) */
-#define PKDTESTS_KEX(f, client, kexcmd) \
- /* Kex algorithms. */ \
- PKDTESTS_KEX_COMMON(f, client, kexcmd) \
- f(client, dsa_curve25519_sha256, kexcmd("curve25519-sha256"), setup_dsa, teardown) \
- f(client, dsa_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_dsa, teardown) \
- f(client, dsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_dsa, teardown) \
- f(client, dsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384 "), setup_dsa, teardown) \
- f(client, dsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521 "), setup_dsa, teardown) \
- 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) */
+#if defined(WITH_GEX)
/* GEX_SHA256 is included in PKDTESTS_KEX_FIPS if available */
#define PKDTESTS_KEX(f, client, kexcmd) \
/* Kex algorithms. */ \
@@ -360,22 +314,6 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
PKDTESTS_KEX_COMMON(f, client, kexcmd)
#endif
-#ifdef HAVE_DSA
-#define PKDTESTS_KEX_OPENSSHONLY(f, client, kexcmd) \
- /* Kex algorithms. */ \
- f(client, ed25519_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ed25519, teardown) \
- f(client, ed25519_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ed25519, teardown) \
- 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) \
- f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown) \
- f(client, ed25519_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ed25519, teardown) \
- f(client, ed25519_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ed25519, teardown)
-#else
#define PKDTESTS_KEX_OPENSSHONLY(f, client, kexcmd) \
/* Kex algorithms. */ \
f(client, ed25519_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ed25519, teardown) \
@@ -389,8 +327,8 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ed25519, teardown)
-#endif
+#define CHACHA20 "chacha20-poly1305@openssh.com"
#define PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown) \
@@ -413,19 +351,18 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown)
-#ifdef HAVE_DSA
+#define PKDTESTS_CIPHER_CHACHA(f, client, ciphercmd) \
+ f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
+ f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
+ f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
+
#define PKDTESTS_CIPHER(f, client, ciphercmd) \
/* Ciphers. */ \
PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
- f(client, dsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_dsa, teardown) \
- f(client, dsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_dsa, teardown)
-#else
-#define PKDTESTS_CIPHER(f, client, ciphercmd) \
- /* Ciphers. */ \
- PKDTESTS_CIPHER_COMMON(f, client, ciphercmd)
-#endif
+ PKDTESTS_CIPHER_CHACHA(f, client, ciphercmd)
-#define CHACHA20 "chacha20-poly1305@openssh.com"
#define AES128_GCM "aes128-gcm@openssh.com"
#define AES256_GCM "aes256-gcm@openssh.com"
@@ -439,7 +376,6 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown)
-#ifdef HAVE_DSA
#define PKDTESTS_CIPHER_OPENSSHONLY(f, client, ciphercmd) \
/* Ciphers. */ \
PKDTESTS_CIPHER_OPENSSHONLY_FIPS(f, client, ciphercmd) \
@@ -448,15 +384,6 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
- f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
- f(client, dsa_3des_cbc, ciphercmd("3des-cbc"), setup_dsa, teardown) \
- f(client, dsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_dsa, teardown) \
- f(client, dsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_dsa, teardown) \
- f(client, dsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_dsa, teardown) \
- f(client, dsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_dsa, teardown) \
- f(client, dsa_chacha20, ciphercmd(CHACHA20), setup_dsa, teardown) \
- f(client, dsa_aes128_gcm, ciphercmd(AES128_GCM), setup_dsa, teardown) \
- f(client, dsa_aes256_gcm, ciphercmd(AES256_GCM), setup_dsa, teardown) \
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown) \
@@ -464,7 +391,6 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ed25519_aes256_ctr, ciphercmd("aes256-ctr"), setup_ed25519, teardown) \
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown) \
- f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown) \
f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown) \
f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \
@@ -472,56 +398,16 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
- f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
- f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \
- f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
- f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
- f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
- f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
- f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
- f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
-#else
-#define PKDTESTS_CIPHER_OPENSSHONLY(f, client, ciphercmd) \
- /* Ciphers. */ \
- PKDTESTS_CIPHER_OPENSSHONLY_FIPS(f, client, ciphercmd) \
- f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown) \
- f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \
- f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
- f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
- f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
- f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
- f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown) \
- f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown) \
- f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown) \
- f(client, ed25519_aes256_cbc, ciphercmd("aes256-cbc"), setup_ed25519, teardown) \
- f(client, ed25519_aes256_ctr, ciphercmd("aes256-ctr"), setup_ed25519, teardown) \
- f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown) \
- f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown) \
- f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
- f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \
- f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \
- f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
- f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
- f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
- f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
- f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown) \
- f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
-#endif
+ f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown)
#define PKDTESTS_MAC_FIPS(f, client, maccmd) \
@@ -552,25 +438,6 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown) \
f(client, rsa_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_rsa, teardown)
-#ifdef HAVE_DSA
-#define PKDTESTS_MAC(f, client, maccmd) \
- /* MACs. */ \
- PKDTESTS_MAC_FIPS(f, client, maccmd) \
- f(client, dsa_hmac_sha1, maccmd("hmac-sha1"), setup_dsa, teardown) \
- f(client, dsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_dsa, teardown)
-#define PKDTESTS_MAC_OPENSSHONLY(f, client, maccmd) \
- PKDTESTS_MAC_OPENSSHONLY_FIPS(f, client, maccmd) \
- f(client, dsa_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_dsa, teardown) \
- f(client, dsa_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_dsa, teardown) \
- f(client, dsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_dsa, teardown) \
- f(client, dsa_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_dsa, teardown) \
- f(client, ed25519_hmac_sha1, maccmd("hmac-sha1"), setup_ed25519, teardown) \
- f(client, ed25519_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ed25519, teardown) \
- f(client, ed25519_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ed25519, teardown) \
- f(client, ed25519_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ed25519, teardown) \
- f(client, ed25519_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ed25519, teardown) \
- f(client, ed25519_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ed25519, teardown)
-#else
#define PKDTESTS_MAC(f, client, maccmd) \
/* MACs. */ \
PKDTESTS_MAC_FIPS(f, client, maccmd)
@@ -582,7 +449,6 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
f(client, ed25519_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ed25519, teardown) \
f(client, ed25519_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ed25519, teardown) \
f(client, ed25519_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ed25519, teardown)
-#endif
#define PKDTESTS_HOSTKEY_OPENSSHONLY_FIPS(f, client, hkcmd) \
@@ -638,21 +504,6 @@ static void torture_pkd_runtest(const char *testname,
/*
* Actual test functions are emitted here.
*/
-
-#ifdef HAVE_DSA
-#define CLIENT_ID_FILE OPENSSH_DSA_TESTKEY
-PKDTESTS_DEFAULT(emit_keytest, openssh_dsa, OPENSSH_CMD)
-PKDTESTS_DEFAULT(emit_keytest, openssh_cert_dsa, OPENSSH_CERT_CMD)
-PKDTESTS_DEFAULT_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_CMD)
-PKDTESTS_KEX(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD)
-PKDTESTS_KEX_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD)
-PKDTESTS_CIPHER(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD)
-PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD)
-PKDTESTS_MAC(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD)
-PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD)
-#undef CLIENT_ID_FILE
-#endif
-
#define CLIENT_ID_FILE OPENSSH_RSA_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, openssh_rsa, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_keytest, openssh_cert_rsa, OPENSSH_CERT_CMD)
@@ -724,17 +575,6 @@ struct {
const struct CMUnitTest test;
} testmap[] = {
/* OpenSSH */
-#ifdef HAVE_DSA
- PKDTESTS_DEFAULT(emit_testmap, openssh_dsa, OPENSSH_CMD)
- PKDTESTS_DEFAULT(emit_testmap, openssh_cert_dsa, OPENSSH_CERT_CMD)
- PKDTESTS_DEFAULT_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_CMD)
- PKDTESTS_KEX(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD)
- PKDTESTS_KEX_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD)
- PKDTESTS_CIPHER(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD)
- PKDTESTS_CIPHER_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD)
- PKDTESTS_MAC(emit_testmap, openssh_dsa, OPENSSH_MAC_CMD)
- PKDTESTS_MAC_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_MAC_CMD)
-#endif
PKDTESTS_DEFAULT(emit_testmap, openssh_rsa, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_testmap, openssh_cert_rsa, OPENSSH_CERT_CMD)
@@ -789,16 +629,6 @@ static int pkd_run_tests(void) {
int tindex = 0;
const struct CMUnitTest openssh_tests[] = {
-#ifdef HAVE_DSA
- PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD)
- PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_cert_dsa, OPENSSH_CERT_CMD)
- PKDTESTS_DEFAULT_OPENSSHONLY(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD)
- PKDTESTS_KEX(emit_unit_test_comma, openssh_dsa, OPENSSH_KEX_CMD)
- PKDTESTS_CIPHER(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD)
- PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD)
- PKDTESTS_MAC(emit_unit_test_comma, openssh_dsa, OPENSSH_MAC_CMD)
- PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_dsa, OPENSSH_MAC_CMD)
-#endif
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_cert_rsa, OPENSSH_CERT_CMD)
@@ -830,6 +660,10 @@ static int pkd_run_tests(void) {
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD)
};
+ /* It is not possible to test hostkey and kex algorithms, because
+ * dbclient does not support setting hostkey and kex algorithms
+ * through cli (see 'man dbclient')
+ */
const struct CMUnitTest dropbear_tests[] = {
PKDTESTS_DEFAULT(emit_unit_test_comma, dropbear, DROPBEAR_CMD)
PKDTESTS_CIPHER(emit_unit_test_comma, dropbear, DROPBEAR_CIPHER_CMD)
@@ -942,23 +776,66 @@ static int pkd_run_tests(void) {
cleanup_ecdsa_keys();
if (!ssh_fips_mode()) {
cleanup_ed25519_key();
-#ifdef HAVE_DSA
- cleanup_dsa_key();
-#endif
}
return rc;
}
+static int pkd_init_temp_dir(void) {
+ int rc = 0;
+ char *mkdtemp_str = NULL;
+ pkd_dargs.original_dir_fd = -1;
+
+ if (pkd_dargs.opts.temp_dir.argv_mkdtemp_str == NULL) {
+ return 0;
+ }
+
+ pkd_dargs.original_dir_fd = open(".", O_RDONLY);
+ if (pkd_dargs.original_dir_fd < 0) {
+ fprintf(stderr, "pkd_init_temp_dir open failed\n");
+ return -1;
+ }
+
+ mkdtemp_str = strdup(pkd_dargs.opts.temp_dir.argv_mkdtemp_str);
+ if (mkdtemp_str == NULL) {
+ fprintf(stderr, "pkd_init_temp_dir strdup failed\n");
+ goto errstrdup;
+ }
+ pkd_dargs.opts.temp_dir.mkdtemp_str = mkdtemp_str;
+
+ if (mkdtemp(mkdtemp_str) == NULL) {
+ fprintf(stderr, "pkd_init_temp_dir mkdtemp '%s' failed\n", mkdtemp_str);
+ goto errmkdtemp;
+ }
+
+ rc = chdir(mkdtemp_str);
+ if (rc != 0) {
+ fprintf(stderr, "pkd_init_temp_dir chdir '%s' failed\n", mkdtemp_str);
+ goto errchdir;
+ }
+
+ return 0;
+
+errchdir:
+ rmdir(mkdtemp_str);
+errmkdtemp:
+ free(mkdtemp_str);
+errstrdup:
+ close(pkd_dargs.original_dir_fd);
+ pkd_dargs.original_dir_fd = -1;
+ rc = -1;
+ return rc;
+}
+
static int pkd_init_socket_wrapper(void) {
int rc = 0;
char *mkdtemp_str = NULL;
- if (pkd_dargs.opts.socket_wrapper.mkdtemp_str == NULL) {
+ if (pkd_dargs.opts.socket_wrapper.argv_mkdtemp_str == NULL) {
goto out;
}
- mkdtemp_str = strdup(pkd_dargs.opts.socket_wrapper.mkdtemp_str);
+ mkdtemp_str = strdup(pkd_dargs.opts.socket_wrapper.argv_mkdtemp_str);
if (mkdtemp_str == NULL) {
fprintf(stderr, "pkd_init_socket_wrapper strdup failed\n");
goto errstrdup;
@@ -991,6 +868,33 @@ static int pkd_rmfiles(const char *path) {
return system_checked(bin);
}
+static int pkd_cleanup_temp_dir(void) {
+ int rc = 0;
+
+ if (pkd_dargs.opts.temp_dir.mkdtemp_str == NULL) {
+ return 0;
+ }
+
+ if (fchdir(pkd_dargs.original_dir_fd) != 0) {
+ fprintf(stderr, "pkd_cleanup_temp_dir failed fchdir\n");
+ rc = -1;
+ goto out;
+ }
+
+ if (rmdir(pkd_dargs.opts.temp_dir.mkdtemp_str) != 0) {
+ fprintf(stderr, "pkd_cleanup_temp_dir rmdir '%s' failed\n",
+ pkd_dargs.opts.temp_dir.mkdtemp_str);
+ rc = -1;
+ goto out;
+ }
+
+out:
+ close(pkd_dargs.original_dir_fd);
+ pkd_dargs.original_dir_fd = -1;
+ free(pkd_dargs.opts.temp_dir.mkdtemp_str);
+ return rc;
+}
+
static int pkd_cleanup_socket_wrapper(void) {
int rc = 0;
@@ -1001,22 +905,22 @@ static int pkd_cleanup_socket_wrapper(void) {
/* clean up socket-wrapper unix domain sockets */
if (pkd_rmfiles(pkd_dargs.opts.socket_wrapper.mkdtemp_str) != 0) {
fprintf(stderr, "pkd_cleanup_socket_wrapper pkd_rmfiles '%s' failed\n",
- pkd_dargs.opts.socket_wrapper.mkdtemp_str);
+ pkd_dargs.opts.socket_wrapper.mkdtemp_str);
goto errrmfiles;
}
if (rmdir(pkd_dargs.opts.socket_wrapper.mkdtemp_str) != 0) {
fprintf(stderr, "pkd_cleanup_socket_wrapper rmdir '%s' failed\n",
- pkd_dargs.opts.socket_wrapper.mkdtemp_str);
+ pkd_dargs.opts.socket_wrapper.mkdtemp_str);
goto errrmdir;
}
- free(pkd_dargs.opts.socket_wrapper.mkdtemp_str);
-
- goto out;
+ goto outfree;
errrmdir:
errrmfiles:
rc = -1;
+outfree:
+ free(pkd_dargs.opts.socket_wrapper.mkdtemp_str);
out:
return rc;
}
@@ -1042,10 +946,16 @@ int main(int argc, char **argv) {
(void) argc; (void) argv;
#endif /* HAVE_ARGP_H */
+ rc = pkd_init_temp_dir();
+ if (rc != 0) {
+ fprintf(stderr, "pkd_init_temp_dir failed: %d\n", rc);
+ goto out_finalize;
+ }
+
rc = pkd_init_socket_wrapper();
if (rc != 0) {
fprintf(stderr, "pkd_init_socket_wrapper failed: %d\n", rc);
- goto out_finalize;
+ goto out_tempdir;
}
if (pkd_dargs.opts.list != 0) {
@@ -1064,6 +974,12 @@ int main(int argc, char **argv) {
fprintf(stderr, "pkd_cleanup_socket_wrapper failed: %d\n", rc);
}
+out_tempdir:
+ rc = pkd_cleanup_temp_dir();
+ if (rc != 0) {
+ fprintf(stderr, "pkd_cleanup_temp_dir failed: %d\n", rc);
+ }
+
out_finalize:
rc = ssh_finalize();
if (rc != 0) {
diff --git a/tests/pkd/pkd_keyutil.c b/tests/pkd/pkd_keyutil.c
index 3991bcbb..daaab134 100644
--- a/tests/pkd/pkd_keyutil.c
+++ b/tests/pkd/pkd_keyutil.c
@@ -8,6 +8,7 @@
#include <setjmp.h> // for cmocka
#include <stdarg.h> // for cmocka
+#include <stdint.h> // for cmocka
#include <unistd.h> // for cmocka
#include <cmocka.h>
@@ -22,7 +23,7 @@
#include "pkd_keyutil.h"
#include "pkd_util.h"
-void setup_rsa_key() {
+void setup_rsa_key(void) {
int rc = 0;
if (access(LIBSSH_RSA_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f "
@@ -31,7 +32,7 @@ void setup_rsa_key() {
assert_int_equal(rc, 0);
}
-void setup_ed25519_key() {
+void setup_ed25519_key(void) {
int rc = 0;
if (access(LIBSSH_ED25519_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
@@ -40,18 +41,7 @@ void setup_ed25519_key() {
assert_int_equal(rc, 0);
}
-#ifdef HAVE_DSA
-void setup_dsa_key() {
- int rc = 0;
- if (access(LIBSSH_DSA_TESTKEY, F_OK) != 0) {
- rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f "
- LIBSSH_DSA_TESTKEY);
- }
- assert_int_equal(rc, 0);
-}
-#endif
-
-void setup_ecdsa_keys() {
+void setup_ecdsa_keys(void) {
int rc = 0;
if (access(LIBSSH_ECDSA_256_TESTKEY, F_OK) != 0) {
@@ -71,27 +61,21 @@ void setup_ecdsa_keys() {
}
}
-void cleanup_rsa_key() {
+void cleanup_rsa_key(void) {
cleanup_key(LIBSSH_RSA_TESTKEY);
}
-void cleanup_ed25519_key() {
+void cleanup_ed25519_key(void) {
cleanup_key(LIBSSH_ED25519_TESTKEY);
}
-#ifdef HAVE_DSA
-void cleanup_dsa_key() {
- cleanup_key(LIBSSH_DSA_TESTKEY);
-}
-#endif
-
-void cleanup_ecdsa_keys() {
+void cleanup_ecdsa_keys(void) {
cleanup_key(LIBSSH_ECDSA_256_TESTKEY);
cleanup_key(LIBSSH_ECDSA_384_TESTKEY);
cleanup_key(LIBSSH_ECDSA_521_TESTKEY);
}
-void setup_openssh_client_keys() {
+void setup_openssh_client_keys(void) {
int rc = 0;
if (access(OPENSSH_CA_TESTKEY, F_OK) != 0) {
@@ -156,19 +140,6 @@ void setup_openssh_client_keys() {
assert_int_equal(rc, 0);
if (!ssh_fips_mode()) {
-#ifdef HAVE_DSA
- if (access(OPENSSH_DSA_TESTKEY, F_OK) != 0) {
- rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f "
- OPENSSH_DSA_TESTKEY);
- }
- assert_int_equal(rc, 0);
-
- if (access(OPENSSH_DSA_TESTKEY "-cert.pub", F_OK) != 0) {
- rc = system_checked(OPENSSH_KEYGEN " -I ident -s " OPENSSH_CA_TESTKEY
- " " OPENSSH_DSA_TESTKEY ".pub 2>/dev/null");
- }
- assert_int_equal(rc, 0);
-#endif
if (access(OPENSSH_ED25519_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
@@ -184,7 +155,7 @@ void setup_openssh_client_keys() {
}
}
-void cleanup_openssh_client_keys() {
+void cleanup_openssh_client_keys(void) {
cleanup_key(OPENSSH_CA_TESTKEY);
cleanup_key(OPENSSH_RSA_TESTKEY);
cleanup_file(OPENSSH_RSA_TESTKEY "-sha256-cert.pub");
@@ -193,13 +164,10 @@ void cleanup_openssh_client_keys() {
cleanup_key(OPENSSH_ECDSA521_TESTKEY);
if (!ssh_fips_mode()) {
cleanup_key(OPENSSH_ED25519_TESTKEY);
-#ifdef HAVE_DSA
- cleanup_key(OPENSSH_DSA_TESTKEY);
-#endif
}
}
-void setup_dropbear_client_rsa_key() {
+void setup_dropbear_client_rsa_key(void) {
int rc = 0;
if (access(DROPBEAR_RSA_TESTKEY, F_OK) != 0) {
rc = system_checked(DROPBEAR_KEYGEN " -t rsa -f "
@@ -208,6 +176,6 @@ void setup_dropbear_client_rsa_key() {
assert_int_equal(rc, 0);
}
-void cleanup_dropbear_client_rsa_key() {
+void cleanup_dropbear_client_rsa_key(void) {
unlink(DROPBEAR_RSA_TESTKEY);
}
diff --git a/tests/pkd/pkd_keyutil.h b/tests/pkd/pkd_keyutil.h
index 7b189040..8e8f50ae 100644
--- a/tests/pkd/pkd_keyutil.h
+++ b/tests/pkd/pkd_keyutil.h
@@ -10,32 +10,20 @@
#include "config.h"
/* Server keys. */
-#ifdef HAVE_DSA
-#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa"
-#endif
#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
#define LIBSSH_ED25519_TESTKEY "libssh_testkey.id_ed25519"
#define LIBSSH_ECDSA_256_TESTKEY "libssh_testkey.id_ecdsa256"
#define LIBSSH_ECDSA_384_TESTKEY "libssh_testkey.id_ecdsa384"
#define LIBSSH_ECDSA_521_TESTKEY "libssh_testkey.id_ecdsa521"
-#ifdef HAVE_DSA
-void setup_dsa_key(void);
-#endif
void setup_rsa_key(void);
void setup_ed25519_key(void);
void setup_ecdsa_keys(void);
-#ifdef HAVE_DSA
-void cleanup_dsa_key(void);
-#endif
void cleanup_rsa_key(void);
void cleanup_ed25519_key(void);
void cleanup_ecdsa_keys(void);
/* Client keys. */
-#ifdef HAVE_DSA
-#define OPENSSH_DSA_TESTKEY "openssh_testkey.id_dsa"
-#endif
#define OPENSSH_RSA_TESTKEY "openssh_testkey.id_rsa"
#define OPENSSH_ECDSA256_TESTKEY "openssh_testkey.id_ecdsa256"
#define OPENSSH_ECDSA384_TESTKEY "openssh_testkey.id_ecdsa384"
diff --git a/tests/pkd/pkd_util.c b/tests/pkd/pkd_util.c
index 0e3b19b4..e8e6fbb7 100644
--- a/tests/pkd/pkd_util.c
+++ b/tests/pkd/pkd_util.c
@@ -81,6 +81,7 @@ static int is_openssh_client_new_enough(void) {
((major < 1) || (major > 100))) {
fprintf(stderr, "failed to parse OpenSSH client version, "
"errno %d\n", errno);
+ errno = 0;
goto errversion;
}
diff --git a/tests/server/CMakeLists.txt b/tests/server/CMakeLists.txt
index 96457cd2..f741e95a 100644
--- a/tests/server/CMakeLists.txt
+++ b/tests/server/CMakeLists.txt
@@ -11,6 +11,7 @@ set(LIBSSH_SERVER_TESTS
torture_server_auth_kbdint
torture_server_config
torture_server_algorithms
+ torture_sftpserver
)
include_directories(${libssh_SOURCE_DIR}/include
diff --git a/tests/server/test_server/CMakeLists.txt b/tests/server/test_server/CMakeLists.txt
index fe5e7d09..7e0f88c1 100644
--- a/tests/server/test_server/CMakeLists.txt
+++ b/tests/server/test_server/CMakeLists.txt
@@ -10,7 +10,11 @@ set(server_SRCS
add_library(testserver STATIC
test_server.c
- default_cb.c)
+ default_cb.c
+ sftpserver_cb.c)
+if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(testserver)
+endif (WITH_COVERAGE)
set(LIBSSH_SERVER_TESTS
# torture_server_kbdint
@@ -28,10 +32,10 @@ if (UNIX AND NOT WIN32)
add_executable(test_server ${server_SRCS})
target_compile_options(test_server PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
target_link_libraries(test_server
- testserver
- ssh::ssh
- ${ARGP_LIBRARY}
- util)
+ PRIVATE testserver ssh::ssh ${ARGP_LIBRARIES} util)
+ if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(test_server)
+ endif (WITH_COVERAGE)
endif ()
endif (WITH_SERVER AND UNIX AND NOT WIN32)
diff --git a/tests/server/test_server/default_cb.c b/tests/server/test_server/default_cb.c
index 03c34190..a4ac81b0 100644
--- a/tests/server/test_server/default_cb.c
+++ b/tests/server/test_server/default_cb.c
@@ -51,6 +51,40 @@
#include <util.h>
#endif
+int auth_none_cb(UNUSED_PARAM(ssh_session session),
+ const char *user,
+ void *userdata)
+{
+ struct session_data_st *sdata = NULL;
+ ssh_string banner = NULL;
+
+ sdata = (struct session_data_st *)userdata;
+ if (sdata == NULL) {
+ fprintf(stderr, "Error: NULL userdata\n");
+ goto denied;
+ }
+
+ if (sdata->username == NULL) {
+ fprintf(stderr, "Error: expected username not set\n");
+ goto denied;
+ }
+
+ printf("None authentication of user %s\n", user);
+
+ /* Send the banner */
+ banner = ssh_string_from_char(SSHD_BANNER_MESSAGE);
+ if (banner == NULL) {
+ goto denied;
+ }
+ if (ssh_send_issue_banner(session, banner) == SSH_ERROR) {
+ fprintf(stderr, "Error: Failed to send the banner.\n");
+ goto denied;
+ }
+denied:
+ ssh_string_free(banner);
+ return SSH_AUTH_DENIED;
+}
+
int auth_pubkey_cb(UNUSED_PARAM(ssh_session session),
const char *user,
UNUSED_PARAM(struct ssh_key_struct *pubkey),
@@ -76,7 +110,7 @@ int auth_pubkey_cb(UNUSED_PARAM(ssh_session session),
}
/* TODO */
- /* Check wheter the user and public key are in authorized keys list */
+ /* Check whether the user and public key are in authorized keys list */
/* Authenticated */
printf("Authenticated\n");
@@ -743,6 +777,7 @@ struct ssh_server_callbacks_struct *get_default_server_cb(void)
goto end;
}
+ cb->auth_none_function = auth_none_cb;
cb->auth_password_function = auth_password_cb;
cb->auth_pubkey_function = auth_pubkey_cb;
cb->channel_open_request_session_function = channel_new_session_cb;
@@ -851,7 +886,6 @@ void default_handle_session_cb(ssh_event event,
}
sdata.server_state = (void *)state;
- cdata.server_state = (void *)state;
#ifdef WITH_PCAP
set_pcap(&sdata, session, state->pcap_file);
@@ -867,7 +901,7 @@ void default_handle_session_cb(ssh_event event,
if (ssh_handle_key_exchange(session) != SSH_OK) {
fprintf(stderr, "%s\n", ssh_get_error(session));
- return;
+ goto end;
}
/* Set the supported authentication methods */
@@ -886,7 +920,7 @@ void default_handle_session_cb(ssh_event event,
/* If the user has used up all attempts, or if he hasn't been able to
* authenticate in 10 seconds (n * 100ms), disconnect. */
if (sdata.auth_attempts >= state->max_tries || n >= 100) {
- return;
+ goto end;
}
if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
diff --git a/tests/server/test_server/default_cb.h b/tests/server/test_server/default_cb.h
index 487794c0..529c71e1 100644
--- a/tests/server/test_server/default_cb.h
+++ b/tests/server/test_server/default_cb.h
@@ -24,6 +24,7 @@
#include "config.h"
#include <libssh/libssh.h>
+#include <libssh/sftp.h>
#include <libssh/callbacks.h>
#define SSHD_DEFAULT_USER "libssh"
@@ -32,6 +33,8 @@
#define SSHD_DEFAULT_ADDRESS "127.0.0.1"
#define SSHD_DEFAULT_PCAP_FILE "debug.server.pcap"
+#define SSHD_BANNER_MESSAGE "Test Banner Message\nlibssh-send-banner\n"
+
#ifndef KEYS_FOLDER
#ifdef _WIN32
#define KEYS_FOLDER
@@ -68,6 +71,7 @@ struct channel_data_st {
void *server_state;
/* This pointer is useful to set data for custom callbacks */
void *extra_data;
+ sftp_session sftp;
};
/* A userdata struct for session. */
diff --git a/tests/server/test_server/main.c b/tests/server/test_server/main.c
index b4676b4b..39c01223 100644
--- a/tests/server/test_server/main.c
+++ b/tests/server/test_server/main.c
@@ -45,7 +45,6 @@ struct arguments_st {
char *port;
char *ecdsa_key;
- char *dsa_key;
char *ed25519_key;
char *rsa_key;
char *host_key;
@@ -74,7 +73,6 @@ static void free_arguments(struct arguments_st *arguments)
SAFE_FREE(arguments->port);
SAFE_FREE(arguments->ecdsa_key);
- SAFE_FREE(arguments->dsa_key);
SAFE_FREE(arguments->ed25519_key);
SAFE_FREE(arguments->rsa_key);
SAFE_FREE(arguments->host_key);
@@ -153,8 +151,6 @@ static void print_server_state(struct server_state_st *state)
printf("=================================================\n");
printf("ecdsa_key = %s\n",
state->ecdsa_key? state->ecdsa_key: "NULL");
- printf("dsa_key = %s\n",
- state->dsa_key? state->dsa_key: "NULL");
printf("ed25519_key = %s\n",
state->ed25519_key? state->ed25519_key: "NULL");
printf("rsa_key = %s\n",
@@ -218,13 +214,6 @@ static int init_server_state(struct server_state_st *state,
state->ecdsa_key = NULL;
}
- if (arguments->dsa_key) {
- state->dsa_key = arguments->dsa_key;
- arguments->dsa_key = NULL;
- } else {
- state->dsa_key = NULL;
- }
-
if (arguments->ed25519_key) {
state->ed25519_key = arguments->ed25519_key;
arguments->ed25519_key = NULL;
@@ -364,14 +353,6 @@ static struct argp_option options[] = {
.group = 0
},
{
- .name = "dsakey",
- .key = 'd',
- .arg = "FILE",
- .flags = 0,
- .doc = "Set the DSA key.",
- .group = 0
- },
- {
.name = "ed25519key",
.key = 'e',
.arg = "FILE",
@@ -486,14 +467,6 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
goto end;
}
break;
- case 'd':
- arguments->dsa_key = strdup(arg);
- if (arguments->dsa_key == NULL) {
- fprintf(stderr, "Out of memory\n");
- rc = ENOMEM;
- goto end;
- }
- break;
case 'e':
arguments->ed25519_key = strdup(arg);
if (arguments->ed25519_key == NULL) {
@@ -623,9 +596,12 @@ int main(UNUSED_PARAM(int argc), UNUSED_PARAM(char **argv))
.address = NULL,
.with_global_config = true,
};
- struct server_state_st state = {
- .address = NULL,
- };
+ struct server_state_st *state = calloc(1, sizeof(struct server_state_st));
+
+ if (state == NULL) {
+ printf("Failed to allocate memory\n");
+ return -1;
+ }
#ifdef HAVE_ARGP_H
argp_parse (&argp, argc, argv, 0, 0, &arguments);
@@ -635,6 +611,8 @@ int main(UNUSED_PARAM(int argc), UNUSED_PARAM(char **argv))
pid_file = fopen(arguments.pid_file, "w");
if (pid_file == NULL) {
rc = -1;
+ free_server_state(state);
+ SAFE_FREE(state);
goto free_arguments;
}
pid = getpid();
@@ -643,22 +621,19 @@ int main(UNUSED_PARAM(int argc), UNUSED_PARAM(char **argv))
}
/* Initialize the state using default or given parameters */
- rc = init_server_state(&state, &arguments);
+ rc = init_server_state(state, &arguments);
if (rc != 0) {
+ free_server_state(state);
+ SAFE_FREE(state);
goto free_arguments;
}
/* Free the arguments used to initialize the state before fork */
free_arguments(&arguments);
- /* Run the server */
- rc = run_server(&state);
- if (rc != 0) {
- goto free_state;
- }
+ /* Run the server: Frees the state in all processes */
+ rc = run_server(state);
-free_state:
- free_server_state(&state);
free_arguments:
free_arguments(&arguments);
return rc;
diff --git a/tests/server/test_server/sftpserver_cb.c b/tests/server/test_server/sftpserver_cb.c
new file mode 100644
index 00000000..6c58b262
--- /dev/null
+++ b/tests/server/test_server/sftpserver_cb.c
@@ -0,0 +1,403 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2018 by Red Hat, Inc.
+ *
+ * Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
+ *
+ * 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 "test_server.h"
+#include "default_cb.h"
+
+#include <libssh/callbacks.h>
+#include <libssh/server.h>
+#include <libssh/priv.h>
+#include <libssh/sftp.h>
+#include <libssh/sftpserver.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+/* below are for sftp */
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+
+
+#define BUF_SIZE 1048576
+#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
+
+
+/* TODO implement proper pam authentication cb */
+static int sftp_auth_password_cb(UNUSED_PARAM(ssh_session session),
+ const char *user,
+ const char *password,
+ void *userdata)
+{
+ bool known_user = false;
+ bool valid_password = false;
+
+ struct session_data_st *sdata;
+
+ sdata = (struct session_data_st *)userdata;
+
+ if (sdata == NULL) {
+ fprintf(stderr, "Error: NULL userdata\n");
+ goto null_userdata;
+ }
+
+ if (sdata->username == NULL) {
+ fprintf(stderr, "Error: expected username not set\n");
+ goto denied;
+ }
+
+ if (sdata->password == NULL) {
+ fprintf(stderr, "Error: expected password not set\n");
+ goto denied;
+ }
+
+ printf("Password authentication of user %s\n", user);
+
+ known_user = !(strcmp(user, sdata->username));
+ valid_password = !(strcmp(password, sdata->password));
+
+ if (known_user && valid_password) {
+ sdata->authenticated = 1;
+ sdata->auth_attempts = 0;
+ printf("Authenticated\n");
+ return SSH_AUTH_SUCCESS;
+ }
+
+denied:
+ sdata->auth_attempts++;
+null_userdata:
+ return SSH_AUTH_DENIED;
+}
+
+static ssh_channel sftp_channel_new_session_cb(ssh_session session, void *userdata)
+{
+ struct session_data_st *sdata = NULL;
+ ssh_channel chan = NULL;
+
+ sdata = (struct session_data_st *)userdata;
+
+ if (sdata == NULL) {
+ fprintf(stderr, "NULL userdata");
+ goto end;
+ }
+
+ chan = ssh_channel_new(session);
+ if (chan == NULL) {
+ fprintf(stderr, "Error creating channel: %s\n",
+ ssh_get_error(session));
+ goto end;
+ }
+
+ sdata->channel = chan;
+
+end:
+ return chan;
+}
+
+#ifdef WITH_PCAP
+static void set_pcap(struct session_data_st *sdata,
+ ssh_session session,
+ char *pcap_file)
+{
+ int rc = 0;
+
+ if (sdata == NULL) {
+ return;
+ }
+
+ if (pcap_file == NULL) {
+ return;
+ }
+
+ sdata->pcap = ssh_pcap_file_new();
+ if (sdata->pcap == NULL) {
+ return;
+ }
+
+ rc = ssh_pcap_file_open(sdata->pcap, pcap_file);
+ if (rc == SSH_ERROR) {
+ fprintf(stderr, "Error opening pcap file\n");
+ ssh_pcap_file_free(sdata->pcap);
+ sdata->pcap = NULL;
+ return;
+ }
+ ssh_set_pcap_file(session, sdata->pcap);
+}
+
+static void cleanup_pcap(struct session_data_st *sdata)
+{
+ if (sdata == NULL) {
+ return;
+ }
+
+ if (sdata->pcap == NULL) {
+ return;
+ }
+
+ ssh_pcap_file_free(sdata->pcap);
+ sdata->pcap = NULL;
+}
+#endif
+
+
+/* The caller is responsible to set the userdata to be provided to the callback
+ * The caller is responsible to free the allocated structure
+ */
+struct ssh_server_callbacks_struct *get_sftp_server_cb(void)
+{
+
+ struct ssh_server_callbacks_struct *cb;
+
+ cb = (struct ssh_server_callbacks_struct *)calloc(1,
+ sizeof(struct ssh_server_callbacks_struct));
+
+ if (cb == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ goto end;
+ }
+
+ cb->auth_password_function = sftp_auth_password_cb;
+ cb->channel_open_request_session_function = sftp_channel_new_session_cb;
+
+end:
+ return cb;
+}
+
+/* The caller is responsible to set the userdata to be provided to the callback
+ * The caller is responsible to free the allocated structure
+ * */
+struct ssh_channel_callbacks_struct *get_sftp_channel_cb(void)
+{
+ struct ssh_channel_callbacks_struct *cb;
+
+ cb = (struct ssh_channel_callbacks_struct *)calloc(1,
+ sizeof(struct ssh_channel_callbacks_struct));
+ if (cb == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ goto end;
+ }
+
+ cb->channel_data_function = sftp_channel_default_data_callback;
+ cb->channel_subsystem_request_function = sftp_channel_default_subsystem_request;
+
+end:
+ return cb;
+};
+
+void sftp_handle_session_cb(ssh_event event,
+ ssh_session session,
+ struct server_state_st *state)
+{
+ int n;
+ int rc = 0;
+
+ /* Structure for storing the pty size. */
+ struct winsize wsize = {
+ .ws_row = 0,
+ .ws_col = 0,
+ .ws_xpixel = 0,
+ .ws_ypixel = 0
+ };
+
+ /* Our struct holding information about the channel. */
+ struct channel_data_st cdata = {
+ .event = NULL,
+ .winsize = &wsize,
+ .sftp = NULL
+ };
+
+ /* Our struct holding information about the session. */
+ struct session_data_st sdata = {
+ .channel = NULL,
+ .auth_attempts = 0,
+ .authenticated = 0,
+ .username = SSHD_DEFAULT_USER,
+ .password = SSHD_DEFAULT_PASSWORD
+ };
+
+ struct ssh_channel_callbacks_struct *channel_cb = NULL;
+ struct ssh_server_callbacks_struct *server_cb = NULL;
+
+ if (state == NULL) {
+ fprintf(stderr, "NULL server state provided\n");
+ goto end;
+ }
+
+ /* If callbacks were provided use them. Otherwise, use default callbacks */
+ if (state->server_cb != NULL) {
+ /* This is a macro, it does not return a value */
+ ssh_callbacks_init(state->server_cb);
+
+ rc = ssh_set_server_callbacks(session, state->server_cb);
+ if (rc) {
+ goto end;
+ }
+ } else {
+ server_cb = get_sftp_server_cb();
+ if (server_cb == NULL) {
+ goto end;
+ }
+
+ server_cb->userdata = &sdata;
+
+ /* This is a macro, it does not return a value */
+ ssh_callbacks_init(server_cb);
+
+ rc = ssh_set_server_callbacks(session, server_cb);
+ if (rc) {
+ goto end;
+ }
+ }
+
+ sdata.server_state = (void *)state;
+
+#ifdef WITH_PCAP
+ set_pcap(&sdata, session, state->pcap_file);
+#endif
+
+ if (state->expected_username != NULL) {
+ sdata.username = state->expected_username;
+ }
+
+ if (state->expected_password != NULL) {
+ sdata.password = state->expected_password;
+ }
+
+ if (ssh_handle_key_exchange(session) != SSH_OK) {
+ fprintf(stderr, "%s\n", ssh_get_error(session));
+ goto end;
+ }
+
+ /* Set the supported authentication methods */
+ if (state->auth_methods) {
+ ssh_set_auth_methods(session, state->auth_methods);
+ } else {
+ ssh_set_auth_methods(session,
+ SSH_AUTH_METHOD_PASSWORD |
+ SSH_AUTH_METHOD_PUBLICKEY);
+ }
+
+ ssh_event_add_session(event, session);
+
+ n = 0;
+ while (sdata.authenticated == 0 || sdata.channel == NULL) {
+ /* If the user has used up all attempts, or if he hasn't been able to
+ * authenticate in 10 seconds (n * 100ms), disconnect. */
+ if (sdata.auth_attempts >= state->max_tries || n >= 100) {
+ goto end;
+ }
+
+ if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
+ fprintf(stderr, "do_poll error: %s\n", ssh_get_error(session));
+ goto end;
+ }
+ n++;
+ }
+
+ /* TODO check return values */
+ if (state->channel_cb != NULL) {
+ ssh_callbacks_init(state->channel_cb);
+
+ rc = ssh_set_channel_callbacks(sdata.channel, state->channel_cb);
+ if (rc) {
+ goto end;
+ }
+ } else {
+ channel_cb = get_sftp_channel_cb();
+ if (channel_cb == NULL) {
+ goto end;
+ }
+
+ channel_cb->userdata = &(cdata.sftp);
+
+ ssh_callbacks_init(channel_cb);
+ rc = ssh_set_channel_callbacks(sdata.channel, channel_cb);
+ if (rc) {
+ goto end;
+ }
+ }
+
+ do {
+ /* Poll the main event which takes care of the session, the channel and
+ * even our child process's stdout/stderr (once it's started). */
+ if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
+ ssh_channel_close(sdata.channel);
+ }
+
+ /* If child process's stdout/stderr has been registered with the event,
+ * or the child process hasn't started yet, continue. */
+ if (cdata.event != NULL) {
+ continue;
+ }
+
+ } while (ssh_channel_is_open(sdata.channel));
+
+ ssh_channel_send_eof(sdata.channel);
+ ssh_channel_close(sdata.channel);
+ sftp_server_free(cdata.sftp);
+
+ /* Wait up to 5 seconds for the client to terminate the session. */
+ for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
+ ssh_event_dopoll(event, 100);
+ }
+
+end:
+#ifdef WITH_PCAP
+ cleanup_pcap(&sdata);
+#endif
+ if (channel_cb != NULL) {
+ free(channel_cb);
+ }
+ if (server_cb != NULL) {
+ free(server_cb);
+ }
+ return;
+}
diff --git a/tests/server/test_server/test_server.c b/tests/server/test_server/test_server.c
index 42d21495..6d0f0808 100644
--- a/tests/server/test_server/test_server.c
+++ b/tests/server/test_server/test_server.c
@@ -40,13 +40,12 @@
void free_server_state(struct server_state_st *state)
{
if (state == NULL) {
- goto end;
+ return;
}
SAFE_FREE(state->address);
SAFE_FREE(state->ecdsa_key);
- SAFE_FREE(state->dsa_key);
SAFE_FREE(state->ed25519_key);
SAFE_FREE(state->rsa_key);
SAFE_FREE(state->host_key);
@@ -56,9 +55,7 @@ void free_server_state(struct server_state_st *state)
SAFE_FREE(state->expected_username);
SAFE_FREE(state->expected_password);
SAFE_FREE(state->config_file);
-
-end:
- return;
+ SAFE_FREE(state->log_file);
}
/* SIGCHLD handler for cleaning up dead children. */
@@ -67,6 +64,15 @@ static void sigchld_handler(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
+bool done = false;
+
+static void sigterm_handler(int signo)
+{
+ (void) signo;
+ fprintf(stderr, "Received SIGTERM. Gracefully exiting ...\n");
+ done = true;
+}
+
int run_server(struct server_state_st *state)
{
ssh_session session = NULL;
@@ -77,12 +83,12 @@ int run_server(struct server_state_st *state)
.sa_flags = 0
};
- int rc;
+ int rc = SSH_ERROR;
/* Check provided state */
if (state == NULL) {
fprintf(stderr, "Invalid state\n");
- return SSH_ERROR;
+ goto out;
}
/* Set up SIGCHLD handler. */
@@ -92,23 +98,57 @@ int run_server(struct server_state_st *state)
if (sigaction(SIGCHLD, &sa, NULL) != 0) {
fprintf(stderr, "Failed to register SIGCHLD handler\n");
- return SSH_ERROR;
+ goto out;
}
- if (state->address == NULL) {
- fprintf(stderr, "Missing bind address\n");
- return SSH_ERROR;
+ /* Set up SIGTERM handler. */
+ sa.sa_handler = sigterm_handler;
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGTERM, &sa, NULL) != 0) {
+ fprintf(stderr, "Failed to register SIGTERM handler\n");
+ goto out;
+ }
+
+ /* Redirect all the output and errors to the file to avoid mixing up with
+ * the output from the client */
+ if (state->log_file != NULL) {
+ int fd;
+ FILE *f = fopen(state->log_file, "a");
+ if (f == NULL) {
+ fprintf(stderr, "Failed to open the log file: %s\n", strerror(errno));
+ goto out;
+ }
+ fd = dup2(fileno(f), STDERR_FILENO);
+ if (fd == -1) {
+ fprintf(stderr, "dup2 of log file to stderr failed: %s\n",
+ strerror(errno));
+ goto out;
+ }
+ fd = dup2(fileno(f), STDOUT_FILENO);
+ if (fd == -1) {
+ fprintf(stderr, "dup2 of log file to stdout failed: %s\n",
+ strerror(errno));
+ goto out;
+ }
+ fclose(f);
}
if (state->address == NULL) {
fprintf(stderr, "Missing bind address\n");
- return SSH_ERROR;
+ goto out;
+ }
+
+ if (state->host_key == NULL && state->rsa_key == NULL &&
+ state->ecdsa_key == NULL && state->ed25519_key) {
+ fprintf(stderr, "Missing host key\n");
+ goto out;
}
sshbind = ssh_bind_new();
if (sshbind == NULL) {
fprintf(stderr, "Out of memory\n");
- return SSH_ERROR;
+ goto out;
}
if (state->verbosity) {
@@ -119,7 +159,7 @@ int run_server(struct server_state_st *state)
fprintf(stderr,
"Error setting verbosity level: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
+ goto out;
}
}
@@ -128,14 +168,14 @@ int run_server(struct server_state_st *state)
SSH_BIND_OPTIONS_PROCESS_CONFIG,
&(state->parse_global_config));
if (rc != 0) {
- goto free_sshbind;
+ goto out;
}
}
if (state->config_file) {
rc = ssh_bind_options_parse_config(sshbind, state->config_file);
if (rc != 0) {
- goto free_sshbind;
+ goto out;
}
}
@@ -146,7 +186,7 @@ int run_server(struct server_state_st *state)
fprintf(stderr,
"Error setting bind address: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
+ goto out;
}
rc = ssh_bind_options_set(sshbind,
@@ -156,42 +196,30 @@ int run_server(struct server_state_st *state)
fprintf(stderr,
"Error setting bind port: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
- }
-
- if (state->dsa_key != NULL) {
- rc = ssh_bind_options_set(sshbind,
- SSH_BIND_OPTIONS_DSAKEY,
- state->dsa_key);
- if (rc != 0) {
- fprintf(stderr,
- "Error setting DSA key: %s\n",
- ssh_get_error(sshbind));
- goto free_sshbind;
- }
+ goto out;
}
if (state->rsa_key != NULL) {
rc = ssh_bind_options_set(sshbind,
- SSH_BIND_OPTIONS_RSAKEY,
+ SSH_BIND_OPTIONS_HOSTKEY,
state->rsa_key);
if (rc != 0) {
fprintf(stderr,
"Error setting RSA key: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
+ goto out;
}
}
if (state->ecdsa_key != NULL) {
rc = ssh_bind_options_set(sshbind,
- SSH_BIND_OPTIONS_ECDSAKEY,
+ SSH_BIND_OPTIONS_HOSTKEY,
state->ecdsa_key);
if (rc != 0) {
fprintf(stderr,
"Error setting ECDSA key: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
+ goto out;
}
}
@@ -203,7 +231,7 @@ int run_server(struct server_state_st *state)
fprintf(stderr,
"Error setting hostkey: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
+ goto out;
}
}
@@ -212,17 +240,17 @@ int run_server(struct server_state_st *state)
fprintf(stderr,
"Error listening to socket: %s\n",
ssh_get_error(sshbind));
- goto free_sshbind;
+ goto out;
}
- printf("Started libssh test server on port %d\n", state->port);
+ printf("%d: Started libssh test server on port %d\n", getpid(), state->port);
- for (;;) {
+ while (done == false) {
session = ssh_new();
if (session == NULL) {
fprintf(stderr, "Out of memory\n");
rc = SSH_ERROR;
- goto free_sshbind;
+ goto out;
}
/* Blocks until there is a new incoming connection. */
@@ -235,6 +263,9 @@ int run_server(struct server_state_st *state)
/* Remove the SIGCHLD handler inherited from parent. */
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
+ /* Remove the SIGTERM handler inherited from parent. */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
/* Remove socket binding, which allows us to restart the
* parent process, without terminating existing sessions. */
ssh_bind_free(sshbind);
@@ -252,11 +283,12 @@ int run_server(struct server_state_st *state)
ssh_free(session);
free_server_state(state);
-
+ SAFE_FREE(state);
exit(0);
case -1:
fprintf(stderr, "Failed to fork\n");
}
+ fprintf(stderr, "Forked process PID %d\n", pid);
} else {
fprintf(stderr,
"Error accepting a connection: %s\n",
@@ -271,12 +303,17 @@ int run_server(struct server_state_st *state)
rc = 0;
-free_sshbind:
+out:
+ free_server_state(state);
+ SAFE_FREE(state);
ssh_bind_free(sshbind);
return rc;
}
-pid_t fork_run_server(struct server_state_st *state)
+pid_t
+fork_run_server(struct server_state_st *state,
+ void (*free_test_state) (void **userdata),
+ void *userdata)
{
pid_t pid;
int rc;
@@ -306,6 +343,8 @@ pid_t fork_run_server(struct server_state_st *state)
pid = fork();
switch(pid) {
case 0:
+ /* no longer needed */
+ free_test_state(userdata);
/* Remove the SIGCHLD handler inherited from parent. */
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
@@ -324,6 +363,7 @@ pid_t fork_run_server(struct server_state_st *state)
return -1;
default:
/* Return the child pid */
+ fprintf(stderr, "Forked process PID %d\n", pid);
return pid;
}
}
diff --git a/tests/server/test_server/test_server.h b/tests/server/test_server/test_server.h
index b53894bb..7c6bcb76 100644
--- a/tests/server/test_server/test_server.h
+++ b/tests/server/test_server/test_server.h
@@ -36,7 +36,6 @@ struct server_state_st {
int port;
char *ecdsa_key;
- char *dsa_key;
char *ed25519_key;
char *rsa_key;
char *host_key;
@@ -53,6 +52,8 @@ struct server_state_st {
char *config_file;
bool parse_global_config;
+ char *log_file;
+
/* State */
int max_tries;
int error;
@@ -73,4 +74,7 @@ void free_server_state(struct server_state_st *state);
int run_server(struct server_state_st *state);
/*TODO: Add documentation */
-pid_t fork_run_server(struct server_state_st *state);
+pid_t
+fork_run_server(struct server_state_st *state,
+ void (*free_state) (void **userdata),
+ void *userdata);
diff --git a/tests/server/torture_server.c b/tests/server/torture_server.c
index 910cd1bd..f6b9dea4 100644
--- a/tests/server/torture_server.c
+++ b/tests/server/torture_server.c
@@ -174,6 +174,7 @@ static void torture_server_auth_none(void **state)
struct test_server_st *tss = *state;
struct torture_state *s = NULL;
ssh_session session = NULL;
+ char *banner = NULL;
int rc;
assert_non_null(tss);
@@ -193,6 +194,11 @@ static void torture_server_auth_none(void **state)
rc = ssh_userauth_none(session, NULL);
assert_int_equal(rc, SSH_AUTH_DENIED);
+ banner = ssh_get_issue_banner(session);
+ assert_string_equal(banner, SSHD_BANNER_MESSAGE);
+ free(banner);
+ banner = NULL;
+
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
@@ -364,6 +370,118 @@ static void torture_server_unknown_global_request(void **state)
ssh_channel_close(channel);
}
+static void torture_server_no_more_sessions(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s = NULL;
+ ssh_session session = NULL;
+ ssh_channel channels[2];
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Using the default password for the server */
+ rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ /* Open a channel session */
+ channels[0] = ssh_channel_new(session);
+ assert_non_null(channels[0]);
+
+ rc = ssh_channel_open_session(channels[0]);
+ assert_ssh_return_code(session, rc);
+
+ /* Send no-more-sessions@openssh.com global request */
+ rc = ssh_request_no_more_sessions(session);
+ assert_ssh_return_code(session, rc);
+
+ /* Try to open an extra session and expect failure */
+ channels[1] = ssh_channel_new(session);
+ assert_non_null(channels[1]);
+
+ rc = ssh_channel_open_session(channels[1]);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* Free the unused channel */
+ ssh_channel_free(channels[1]);
+
+ /* Close and free open channel */
+ ssh_channel_close(channels[0]);
+ ssh_channel_free(channels[0]);
+}
+
+static void torture_server_set_disconnect_message(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s = NULL;
+ ssh_session session;
+ int rc;
+ const char *message = "Goodbye";
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ rc = ssh_session_set_disconnect_message(session,message);
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->disconnect_message,message);
+}
+
+static void torture_null_server_set_disconnect_message(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s = NULL;
+ ssh_session session;
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ rc = ssh_session_set_disconnect_message(NULL,"Goodbye");
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_server_set_null_disconnect_message(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s = NULL;
+ ssh_session session;
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ rc = ssh_session_set_disconnect_message(session,NULL);
+ assert_int_equal(rc, SSH_OK);
+ assert_string_equal(session->disconnect_message,"Bye Bye");
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -382,6 +500,18 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_server_unknown_global_request,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_no_more_sessions,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_set_disconnect_message,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_null_server_set_disconnect_message,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_set_null_disconnect_message,
+ session_setup,
+ session_teardown),
};
ssh_init();
diff --git a/tests/server/torture_server_auth_kbdint.c b/tests/server/torture_server_auth_kbdint.c
index 5bf81598..1d5eb733 100644
--- a/tests/server/torture_server_auth_kbdint.c
+++ b/tests/server/torture_server_auth_kbdint.c
@@ -96,11 +96,7 @@ static void cleanup_pcap(struct session_data_st *sdata)
return;
}
- /* Do not free the pcap data context here since its ownership was
- * transfered to the session object, which will take care of its cleanup.
- * Morover it is still in use so we can very simply crash by freeing
- * it here.
- */
+ ssh_pcap_file_free(sdata->pcap);
sdata->pcap = NULL;
}
#endif
@@ -161,7 +157,7 @@ static int authenticate_kbdint(ssh_session session,
initial_prompt[0] = "username: ";
initial_prompt[1] = "password: ";
- /* Prompt for aditional prompts */
+ /* Prompt for additional prompts */
retype_prompt[0] = "retype password: ";
if ((session == NULL) || (message == NULL) || (sdata == NULL)) {
@@ -514,6 +510,14 @@ end:
return;
}
+static void free_test_server_state(void **state)
+{
+ struct test_server_st *tss = *state;
+
+ torture_free_state(tss->state);
+ SAFE_FREE(tss);
+}
+
static int setup_kbdint_server(void **state)
{
struct torture_state *s;
@@ -523,6 +527,7 @@ static int setup_kbdint_server(void **state)
char rsa_hostkey[1024] = {0};
char sshd_path[1024];
+ char log_file[1024];
int rc;
@@ -550,6 +555,11 @@ static int setup_kbdint_server(void **state)
rc = mkdir(sshd_path, 0755);
assert_return_code(rc, errno);
+ snprintf(log_file,
+ sizeof(log_file),
+ "%s/sshd/log",
+ s->socket_dir);
+
snprintf(rsa_hostkey,
sizeof(rsa_hostkey),
"%s/sshd/ssh_host_rsa_key",
@@ -569,7 +579,9 @@ static int setup_kbdint_server(void **state)
ss->host_key = strdup(rsa_hostkey);
assert_non_null(rsa_hostkey);
+ /* not to mix up the client and server messages */
ss->verbosity = torture_libssh_verbosity();
+ ss->log_file = strdup(log_file);
#ifdef WITH_PCAP
ss->with_pcap = 1;
@@ -580,12 +592,15 @@ static int setup_kbdint_server(void **state)
ss->max_tries = 3;
ss->error = 0;
+ tss->state = s;
+ tss->ss = ss;
+
/* Set the session handling function */
ss->handle_session = handle_kbdint_session_cb;
assert_non_null(ss->handle_session);
/* Start the server */
- pid = fork_run_server(ss);
+ pid = fork_run_server(ss, free_test_server_state, &tss);
if (pid < 0) {
fail();
}
@@ -597,11 +612,8 @@ static int setup_kbdint_server(void **state)
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
unsetenv("PAM_WRAPPER");
- /* Wait 200ms */
- usleep(200 * 1000);
-
- tss->state = s;
- tss->ss = ss;
+ rc = torture_wait_for_daemon(15);
+ assert_int_equal(rc, 0);
*state = tss;
diff --git a/tests/server/torture_server_config.c b/tests/server/torture_server_config.c
index 5f66f015..0bb11ec0 100644
--- a/tests/server/torture_server_config.c
+++ b/tests/server/torture_server_config.c
@@ -51,9 +51,6 @@ struct test_server_st {
char ecdsa_521_hostkey[1024];
char ecdsa_384_hostkey[1024];
char ecdsa_256_hostkey[1024];
-#ifdef HAVE_DSA
- char dsa_hostkey[1024];
-#endif /* HAVE_DSA */
};
static int setup_files(void **state)
@@ -117,14 +114,6 @@ static int setup_files(void **state)
torture_write_file(tss->ed25519_hostkey,
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
-#ifdef HAVE_DSA
- snprintf(tss->dsa_hostkey,
- sizeof(tss->dsa_hostkey),
- "%s/sshd/ssh_host_dsa_key",
- s->socket_dir);
- torture_write_file(tss->dsa_hostkey,
- torture_get_testkey(SSH_KEYTYPE_DSS, 0));
-#endif /* HAVE_DSA */
}
tss->state = s;
@@ -372,10 +361,6 @@ static size_t setup_hostkey_files(struct test_server_st *tss)
if (!ssh_fips_mode()) {
hostkey_files[4] = tss->ed25519_hostkey;
num_hostkey_files++;
-#ifdef HAVE_DSA
- hostkey_files[5] = tss->dsa_hostkey;
- num_hostkey_files++;
-#endif
}
#endif /* TEST_ALL_CRYPTO_COMBINATIONS */
@@ -670,18 +655,6 @@ static void torture_server_config_hostkey_algorithms(void **state)
rc = try_config_content(state, config_content, false);
assert_int_equal(rc, 0);
-#ifdef HAVE_DSA
- if (!ssh_fips_mode()) {
- /* ssh-dss */
- snprintf(config_content,
- sizeof(config_content),
- "HostKey %s\nHostkeyAlgorithms %s\n",
- tss->dsa_hostkey, "ssh-dss");
-
- rc = try_config_content(state, config_content, false);
- assert_int_equal(rc, 0);
- }
-#endif
}
static void torture_server_config_unknown(void **state)
diff --git a/tests/server/torture_sftpserver.c b/tests/server/torture_sftpserver.c
new file mode 100644
index 00000000..275f4508
--- /dev/null
+++ b/tests/server/torture_sftpserver.c
@@ -0,0 +1,1012 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2018 by Red Hat, Inc.
+ *
+ * Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
+ *
+ * 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/sftp.h"
+
+#define LIBSSH_STATIC
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+
+#include "torture.h"
+#include "torture_key.h"
+#include "libssh/libssh.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+
+#include "test_server.h"
+#include "default_cb.h"
+
+#define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts"
+
+const char template[] = "temp_dir_XXXXXX";
+
+struct test_server_st {
+ struct torture_state *state;
+ struct server_state_st *ss;
+ char *cwd;
+ char *temp_dir;
+};
+
+void sftp_handle_session_cb(ssh_event event,
+ ssh_session session,
+ struct server_state_st *state);
+
+static void free_test_server_state(void **state)
+{
+ struct test_server_st *tss = *state;
+
+ torture_free_state(tss->state);
+ SAFE_FREE(tss);
+}
+
+static int setup_default_server(void **state)
+{
+ struct torture_state *s;
+ struct server_state_st *ss;
+ struct test_server_st *tss;
+
+ char ed25519_hostkey[1024] = {0};
+ char rsa_hostkey[1024];
+ char ecdsa_hostkey[1024];
+ //char trusted_ca_pubkey[1024];
+
+ char sshd_path[1024];
+ char log_file[1024];
+ int rc;
+
+ char pid_str[1024];
+
+ pid_t pid;
+
+ assert_non_null(state);
+
+ tss = (struct test_server_st*)calloc(1, sizeof(struct test_server_st));
+ assert_non_null(tss);
+
+ torture_setup_socket_dir((void **)&s);
+ assert_non_null(s->socket_dir);
+
+ /* Set the default interface for the server */
+ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
+ setenv("PAM_WRAPPER", "1", 1);
+
+ snprintf(sshd_path,
+ sizeof(sshd_path),
+ "%s/sshd",
+ s->socket_dir);
+
+ rc = mkdir(sshd_path, 0755);
+ assert_return_code(rc, errno);
+
+ snprintf(log_file,
+ sizeof(log_file),
+ "%s/sshd/log",
+ s->socket_dir);
+
+ snprintf(ed25519_hostkey,
+ sizeof(ed25519_hostkey),
+ "%s/sshd/ssh_host_ed25519_key",
+ s->socket_dir);
+ torture_write_file(ed25519_hostkey,
+ torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
+
+ snprintf(rsa_hostkey,
+ sizeof(rsa_hostkey),
+ "%s/sshd/ssh_host_rsa_key",
+ s->socket_dir);
+ torture_write_file(rsa_hostkey, torture_get_testkey(SSH_KEYTYPE_RSA, 0));
+
+ snprintf(ecdsa_hostkey,
+ sizeof(ecdsa_hostkey),
+ "%s/sshd/ssh_host_ecdsa_key",
+ s->socket_dir);
+ torture_write_file(ecdsa_hostkey,
+ torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
+
+ /* Create default server state */
+ ss = (struct server_state_st *)calloc(1, sizeof(struct server_state_st));
+ assert_non_null(ss);
+
+ ss->address = strdup("127.0.0.10");
+ assert_non_null(ss->address);
+
+ ss->port = 22;
+
+ ss->ecdsa_key = strdup(ecdsa_hostkey);
+ assert_non_null(ss->ecdsa_key);
+
+ ss->ed25519_key = strdup(ed25519_hostkey);
+ assert_non_null(ss->ed25519_key);
+
+ ss->rsa_key = strdup(rsa_hostkey);
+ assert_non_null(ss->rsa_key);
+
+ ss->host_key = NULL;
+
+ /* Use default username and password (set in default_handle_session_cb) */
+ ss->expected_username = NULL;
+ ss->expected_password = NULL;
+
+ /* not to mix up the client and server messages */
+ ss->verbosity = torture_libssh_verbosity();
+ ss->log_file = strdup(log_file);
+
+ ss->auth_methods = SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY;
+
+#ifdef WITH_PCAP
+ ss->with_pcap = 1;
+ ss->pcap_file = strdup(s->pcap_file);
+ assert_non_null(ss->pcap_file);
+#endif
+
+ /* TODO make configurable */
+ ss->max_tries = 3;
+ ss->error = 0;
+
+ tss->state = s;
+ tss->ss = ss;
+
+ /* Use the default session handling function */
+ ss->handle_session = sftp_handle_session_cb;
+ assert_non_null(ss->handle_session);
+
+ /* Do not use global configuration */
+ ss->parse_global_config = false;
+
+ /* Start the server using the default values */
+ pid = fork_run_server(ss, free_test_server_state, &tss);
+ if (pid < 0) {
+ fail();
+ }
+
+ snprintf(pid_str, sizeof(pid_str), "%d", pid);
+
+ torture_write_file(s->srv_pidfile, (const char *)pid_str);
+
+ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
+ unsetenv("PAM_WRAPPER");
+
+ /* Wait until the sshd is ready to accept connections */
+ rc = torture_wait_for_daemon(5);
+ assert_int_equal(rc, 0);
+
+ *state = tss;
+
+ return 0;
+}
+
+static int teardown_default_server(void **state)
+{
+ struct torture_state *s;
+ struct server_state_st *ss;
+ struct test_server_st *tss;
+
+ tss = *state;
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ ss = tss->ss;
+ assert_non_null(ss);
+
+ /* This function can be reused */
+ torture_teardown_sshd_server((void **)&s);
+
+ free_server_state(tss->ss);
+ SAFE_FREE(tss->ss);
+ SAFE_FREE(tss);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ int verbosity = torture_libssh_verbosity();
+ char *cwd = NULL;
+ char *tmp_dir = NULL;
+ bool b = false;
+ int rc;
+
+ assert_non_null(tss);
+
+ /* Make sure we do not test the agent */
+ unsetenv("SSH_AUTH_SOCK");
+
+ cwd = torture_get_current_working_dir();
+ assert_non_null(cwd);
+
+ tmp_dir = torture_make_temp_dir(template);
+ assert_non_null(tmp_dir);
+
+ tss->cwd = cwd;
+ tss->temp_dir = tmp_dir;
+
+ s = tss->state;
+ assert_non_null(s);
+
+ s->ssh.session = ssh_new();
+ assert_non_null(s->ssh.session);
+
+ s->ssh.tsftp = (struct torture_sftp*)calloc(1, sizeof(struct torture_sftp));
+ assert_non_null(s->ssh.tsftp);
+
+ 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_HOST, TORTURE_SSH_SERVER);
+ 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);
+
+ return 0;
+}
+
+static int session_setup_sftp(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ ssh_session session;
+ sftp_session sftp;
+ int rc;
+
+ assert_non_null(tss);
+
+ rc = session_setup(state);
+ assert_int_equal(rc, 0);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_AUTH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
+
+ rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ ssh_get_issue_banner(session);
+
+ /* init sftp session */
+ tsftp = s->ssh.tsftp;
+
+ printf("in establish before sftp_new\n");
+ sftp = sftp_new(session);
+ assert_non_null(sftp);
+ tsftp->sftp = sftp;
+
+ rc = sftp_init(sftp);
+ assert_int_equal(rc, SSH_OK);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ int rc = 0;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ sftp_free(s->ssh.tsftp->sftp);
+ SAFE_FREE(s->ssh.tsftp);
+
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ rc = torture_change_dir(tss->cwd);
+ assert_int_equal(rc, 0);
+
+ rc = torture_rmdirs(tss->temp_dir);
+ assert_int_equal(rc, 0);
+
+ SAFE_FREE(tss->temp_dir);
+ SAFE_FREE(tss->cwd);
+
+ return 0;
+}
+
+static void torture_server_establish_sftp(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ ssh_session session;
+ sftp_session sftp;
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ /* TODO: implement proper pam authentication in callback */
+ /* Using the default user for the server */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_AUTH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
+
+ /* TODO: implement proper pam authentication in callback */
+ /* Using the default password for the server */
+ rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ ssh_get_issue_banner(session);
+
+ /* init sftp session */
+ tsftp = s->ssh.tsftp;
+ sftp = tsftp->sftp;
+
+ printf("in establish before sftp_new\n");
+ sftp = sftp_new(session);
+ assert_non_null(sftp);
+
+ rc = sftp_init(sftp);
+ assert_int_equal(rc, SSH_OK);
+
+ tsftp->sftp = sftp;
+}
+
+static void torture_server_test_sftp_function(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ ssh_session session;
+ sftp_session sftp;
+ int rc;
+ char *rv_str;
+ sftp_dir dir;
+
+ char data[65535] = {0};
+ sftp_file source;
+ sftp_file to;
+ int read_len;
+ int write_len;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_AUTH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
+
+ /* TODO: implement proper pam authentication in callback */
+ /* Using the default password for the server */
+ rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ rv_str = ssh_get_issue_banner(session);
+
+ /* init sftp session */
+ tsftp = s->ssh.tsftp;
+ sftp = sftp_new(session);
+ assert_non_null(sftp);
+ tsftp->sftp = sftp;
+
+ rc = sftp_init(sftp);
+ assert_int_equal(rc, SSH_OK);
+
+ /* symbol link */
+ rc = sftp_symlink(sftp, "/tmp/this_is_the_link", "/tmp/sftp_symlink_test");
+ assert_int_equal(rc, SSH_OK);
+
+ rv_str = sftp_readlink(sftp, "/tmp/sftp_symlink_test");
+ assert_non_null(rv_str);
+ ssh_string_free_char(rv_str);
+
+ rc = sftp_unlink(sftp, "/tmp/sftp_symlink_test");
+ assert_int_equal(rc, SSH_OK);
+
+ /* open and close dir */
+ dir = sftp_opendir(sftp, "./");
+ assert_non_null(dir);
+
+ rc = sftp_closedir(dir);
+ assert_int_equal(rc, SSH_OK);
+
+ /* file read and write */
+ source = sftp_open(sftp, "/usr/bin/ssh", O_RDONLY, 0);
+ assert_non_null(source);
+
+ to = sftp_open(sftp, "ssh-copy", O_WRONLY | O_CREAT, 0700);
+ assert_non_null(to);
+
+ read_len = sftp_read(source, data, 4096);
+ write_len = sftp_write(to, data, read_len);
+ assert_int_equal(write_len, read_len);
+
+ rc = sftp_close(source);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = sftp_close(to);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = sftp_unlink(sftp, "ssh-copy");
+ assert_int_equal(rc, SSH_OK);
+}
+
+static void torture_server_sftp_open_read_write(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ sftp_session sftp;
+ ssh_session session;
+ sftp_attributes a = NULL;
+ sftp_file new_file = NULL;
+ char tmp_file[PATH_MAX] = {0};
+ char data[10] = "0123456789";
+ char read_data[10] = {0};
+ struct stat sb;
+ int rc, write_len, read_len;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ tsftp = s->ssh.tsftp;
+ assert_non_null(tsftp);
+
+ sftp = tsftp->sftp;
+ assert_non_null(sftp);
+
+ snprintf(tmp_file, sizeof(tmp_file), "%s/newfile", tss->temp_dir);
+
+ /*
+ * Create a new file
+ */
+ new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0751);
+ assert_non_null(new_file);
+
+ /* Write should work ok */
+ write_len = sftp_write(new_file, data, sizeof(data));
+ assert_int_equal(write_len, sizeof(data));
+
+ /* Reading should fail */
+ read_len = sftp_read(new_file, read_data, sizeof(read_data));
+ assert_int_equal(read_len, SSH_ERROR);
+
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /* Verify locally the mode is correct */
+ rc = stat(tmp_file, &sb);
+ assert_int_equal(rc, 0);
+ assert_int_equal(sb.st_mode, S_IFREG | 0751);
+ assert_int_equal(sb.st_size, sizeof(data)); /* 10b written */
+
+ /* Remote stat */
+ a = sftp_stat(sftp, tmp_file);
+ assert_non_null(a);
+ assert_int_equal(a->permissions, S_IFREG | 0751);
+ assert_int_equal(a->size, sizeof(data)); /* 10b written */
+ assert_int_equal(a->type, SSH_FILEXFER_TYPE_REGULAR);
+ sftp_attributes_free(a);
+
+ /*
+ * Now, lets try O_APPEND, mode is ignored
+ */
+ new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_APPEND, 0);
+ assert_non_null(new_file);
+
+ /* fstat is not implemented */
+ a = sftp_fstat(new_file);
+ assert_null(a);
+
+ /* Write should work ok */
+ write_len = sftp_write(new_file, data, sizeof(data));
+ assert_int_equal(write_len, sizeof(data));
+
+ /* Reading should fail */
+ read_len = sftp_read(new_file, read_data, sizeof(read_data));
+ assert_int_equal(read_len, SSH_ERROR);
+
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /*
+ * Now, lets try read+write, mode is ignored
+ */
+ new_file = sftp_open(sftp, tmp_file, O_RDWR, 0);
+ assert_non_null(new_file);
+
+ /* Reading should work */
+ read_len = sftp_read(new_file, read_data, sizeof(read_data));
+ assert_int_equal(read_len, sizeof(read_data));
+ assert_int_equal(sizeof(read_data), sizeof(data)); /* sanity */
+ assert_memory_equal(read_data, data, sizeof(data));
+
+ rc = sftp_seek(new_file, 20);
+ assert_ssh_return_code(session, rc);
+
+ /* Write should work also ok */
+ write_len = sftp_write(new_file, data, sizeof(data));
+ assert_int_equal(write_len, sizeof(data));
+
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /* Remove the file */
+ rc = sftp_unlink(sftp, tmp_file);
+ assert_ssh_return_code(session, rc);
+
+ /* again: the file does not exist anymore so we should fail now */
+ rc = sftp_unlink(sftp, tmp_file);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /*
+ * Now, lets try read+write+create
+ */
+ new_file = sftp_open(sftp, tmp_file, O_RDWR | O_CREAT, 0700);
+ assert_non_null(new_file);
+
+ /* Reading should not fail but return no data */
+ read_len = sftp_read(new_file, read_data, sizeof(read_data));
+ assert_int_equal(read_len, 0);
+
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /* be nice */
+ rc = sftp_unlink(sftp, tmp_file);
+ assert_ssh_return_code(session, rc);
+
+ /* null flags should be invalid */
+ /* but there is no way in libssh client to force null flags so skip this
+ new_file = sftp_open(sftp, tmp_file, 0, 0700);
+ assert_null(new_file);
+ */
+
+ /* Only O_CREAT is invalid on file which does not exist. Read is implicit */
+ new_file = sftp_open(sftp, tmp_file, O_CREAT, 0700);
+ assert_null(new_file);
+}
+
+static void torture_server_sftp_mkdir(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ sftp_session sftp;
+ ssh_session session;
+ sftp_file new_file = NULL;
+ char tmp_dir[PATH_MAX] = {0};
+ char tmp_file[PATH_MAX] = {0};
+ sftp_attributes a = NULL;
+ struct stat sb;
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ tsftp = s->ssh.tsftp;
+ assert_non_null(tsftp);
+
+ sftp = tsftp->sftp;
+ assert_non_null(sftp);
+
+ snprintf(tmp_dir, sizeof(tmp_dir), "%s/newdir", tss->temp_dir);
+
+ /* create a test dir */
+ rc = sftp_mkdir(sftp, tmp_dir, 0751);
+ assert_ssh_return_code(session, rc);
+
+ /* try the same path again -- we should get an error */
+ rc = sftp_mkdir(sftp, tmp_dir, 0751);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* Verify locally the mode is correct */
+ rc = stat(tmp_dir, &sb);
+ assert_int_equal(rc, 0);
+ assert_int_equal(sb.st_mode, S_IFDIR | 0751);
+
+ /* Remote stat */
+ a = sftp_stat(sftp, tmp_dir);
+ assert_non_null(a);
+ assert_int_equal(a->permissions, S_IFDIR | 0751);
+ assert_int_equal(a->type, SSH_FILEXFER_TYPE_DIRECTORY);
+ sftp_attributes_free(a);
+
+ snprintf(tmp_file, sizeof(tmp_file), "%s/newdir/newfile", tss->temp_dir);
+
+ /* create a file in there */
+ new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0700);
+ assert_non_null(new_file);
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /* remove of non-empty directory fails */
+ rc = sftp_rmdir(sftp, tmp_dir);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* Unlink can not remove directory either */
+ rc = sftp_unlink(sftp, tmp_dir);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* Remove the file */
+ rc = sftp_unlink(sftp, tmp_file);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Now it should work */
+ rc = sftp_rmdir(sftp, tmp_dir);
+ assert_ssh_return_code(session, rc);
+}
+
+static void torture_server_sftp_realpath(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ sftp_session sftp;
+ ssh_session session;
+ char path[PATH_MAX] = {0};
+ char exp_path[PATH_MAX] = {0};
+ char *new_path = NULL;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ tsftp = s->ssh.tsftp;
+ assert_non_null(tsftp);
+
+ sftp = tsftp->sftp;
+ assert_non_null(sftp);
+
+ /* first try with the empty string, which should be equivalent to CWD */
+ new_path = sftp_canonicalize_path(sftp, path);
+ assert_non_null(new_path);
+ assert_string_equal(new_path, tss->cwd);
+ ssh_string_free_char(new_path);
+
+ /* now, lets try some more complicated paths relative to the CWD */
+ snprintf(path, sizeof(path), "%s/.././%s",
+ tss->temp_dir, tss->temp_dir);
+ new_path = sftp_canonicalize_path(sftp, path);
+ assert_non_null(new_path);
+ snprintf(exp_path, sizeof(exp_path), "%s/%s",
+ tss->cwd, tss->temp_dir);
+ assert_string_equal(new_path, exp_path);
+ ssh_string_free_char(new_path);
+
+ /* and this one does not exists, which is an error */
+ snprintf(path, sizeof(path), "%s/.././%s/nodir",
+ tss->temp_dir, tss->temp_dir);
+ new_path = sftp_canonicalize_path(sftp, path);
+ assert_null(new_path);
+ ssh_string_free_char(new_path);
+}
+
+static void torture_server_sftp_symlink(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ sftp_session sftp;
+ ssh_session session;
+ sftp_file new_file = NULL;
+ char tmp_dir[PATH_MAX] = {0};
+ char tmp_file[PATH_MAX] = {0};
+ char path[PATH_MAX] = {0};
+ char abs_path[PATH_MAX] = {0};
+ char data[42] = "012345678901234567890123456789012345678901";
+ char *new_path = NULL;
+ sftp_attributes a = NULL;
+ sftp_dir dir;
+ int write_len, num_files = 0;
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ tsftp = s->ssh.tsftp;
+ assert_non_null(tsftp);
+
+ sftp = tsftp->sftp;
+ assert_non_null(sftp);
+
+ /* create a test dir */
+ snprintf(tmp_dir, sizeof(tmp_dir), "%s/newdir", tss->temp_dir);
+ rc = sftp_mkdir(sftp, tmp_dir, 0751);
+ assert_ssh_return_code(session, rc);
+
+ /* create a file in there */
+ snprintf(tmp_file, sizeof(tmp_file), "%s/%s/newdir/newfile",
+ tss->cwd, tss->temp_dir);
+ new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0700);
+ assert_non_null(new_file);
+ write_len = sftp_write(new_file, data, sizeof(data));
+ assert_int_equal(write_len, sizeof(data));
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /* now, lets create a (relative) symlink to the new file */
+ snprintf(path, sizeof(path), "%s/newdir/linkname", tss->temp_dir);
+ rc = sftp_symlink(sftp, tmp_file, path);
+ assert_ssh_return_code(session, rc);
+
+ /* when the destination exists, it should fail */
+ rc = sftp_symlink(sftp, tmp_dir, tmp_file);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* now, there are different versions of stat that follow symlinks or not */
+ /* lstat should not follow the symlink and show information about the link
+ * itself */
+ a = sftp_lstat(sftp, path);
+ assert_non_null(a);
+ assert_int_not_equal(a->size, sizeof(data));
+ assert_int_equal(a->type, SSH_FILEXFER_TYPE_SYMLINK);
+ sftp_attributes_free(a);
+
+ /* readlink should give us more information about the target of the symlink
+ */
+ new_path = sftp_readlink(sftp, path);
+ assert_non_null(new_path);
+ snprintf(abs_path, sizeof(abs_path), "%s/%s/newdir/newfile",
+ tss->cwd, tss->temp_dir);
+ assert_string_equal(new_path, abs_path);
+ ssh_string_free_char(new_path);
+
+ /* stat should follow the symlink and show information about the link
+ * target */
+ a = sftp_stat(sftp, path);
+ assert_non_null(a);
+ assert_int_equal(a->size, sizeof(data));
+ assert_int_equal(a->permissions, S_IFREG | 0700);
+ assert_int_equal(a->type, SSH_FILEXFER_TYPE_REGULAR);
+ sftp_attributes_free(a);
+
+ /* on non-existing path, they fail */
+ a = sftp_lstat(sftp, "non-existing");
+ assert_null(a);
+ a = sftp_stat(sftp, "non-existing");
+ assert_null(a);
+
+ /**** readdir ****/
+ dir = sftp_opendir(sftp, tmp_dir);
+ assert_non_null(dir);
+ while ((a = sftp_readdir(sftp, dir))) {
+ if (strcmp(a->name, ".") != 0 &&
+ strcmp(a->name, "..") != 0 &&
+ strcmp(a->name, "newfile") != 0 &&
+ strcmp(a->name, "linkname") != 0) {
+ /* There is a file we did not create */
+ assert_true(false);
+ }
+
+ num_files++;
+ sftp_attributes_free(a);
+ }
+ assert_int_equal(num_files, 4);
+ rc = sftp_dir_eof(dir);
+ assert_int_equal(rc, 1);
+ rc = sftp_closedir(dir);
+ assert_ssh_return_code(session, rc);
+
+ /* now, remove the target of the link, the stat should not handle that,
+ * while lstat should keep working */
+ rc = sftp_unlink(sftp, tmp_file);
+ assert_int_equal(rc, SSH_OK);
+
+ a = sftp_lstat(sftp, path);
+ assert_non_null(a);
+ assert_int_not_equal(a->size, sizeof(data));
+ sftp_attributes_free(a);
+
+ a = sftp_stat(sftp, path);
+ assert_null(a);
+
+ /* readlink works ok on broken symlinks */
+ new_path = sftp_readlink(sftp, path);
+ assert_non_null(new_path);
+ snprintf(abs_path, sizeof(abs_path), "%s/%s/newdir/newfile",
+ tss->cwd, tss->temp_dir);
+ assert_string_equal(new_path, abs_path);
+ ssh_string_free_char(new_path);
+
+ /* readlink should fail on directories */
+ new_path = sftp_readlink(sftp, tmp_dir);
+ assert_null(new_path);
+ /* readlink should fail on or on non-existing files */
+ new_path = sftp_readlink(sftp, tmp_file);
+ assert_null(new_path);
+
+ /* Clean up symlink */
+ rc = sftp_unlink(sftp, path);
+ assert_int_equal(rc, SSH_OK);
+ /* Clean up temporary directory */
+ rc = sftp_rmdir(sftp, tmp_dir);
+ assert_int_equal(rc, SSH_OK);
+}
+
+static void torture_server_sftp_extended(void **state)
+{
+ struct test_server_st *tss = *state;
+ struct torture_state *s;
+ struct torture_sftp *tsftp;
+ sftp_session sftp;
+ ssh_session session;
+ sftp_file new_file = NULL;
+ char tmp_dir[PATH_MAX] = {0};
+ char tmp_file[PATH_MAX] = {0};
+ sftp_statvfs_t st = NULL;
+ int rc;
+
+ assert_non_null(tss);
+
+ s = tss->state;
+ assert_non_null(s);
+
+ session = s->ssh.session;
+ assert_non_null(session);
+
+ tsftp = s->ssh.tsftp;
+ assert_non_null(tsftp);
+
+ sftp = tsftp->sftp;
+ assert_non_null(sftp);
+
+ /* create a test dir */
+ snprintf(tmp_dir, sizeof(tmp_dir), "%s/newdir", tss->temp_dir);
+ rc = sftp_mkdir(sftp, tmp_dir, 0751);
+ assert_ssh_return_code(session, rc);
+
+ /* create a file in there */
+ snprintf(tmp_file, sizeof(tmp_file), "%s/%s/newdir/newfile",
+ tss->cwd, tss->temp_dir);
+ new_file = sftp_open(sftp, tmp_file, O_WRONLY | O_CREAT, 0700);
+ assert_non_null(new_file);
+
+ /* extended fstatvsf is not advertised nor supported now but calling this
+ * message will keep hanging the server. The extension protocol says that
+ * the clients can not request extension that are not supported by the
+ * server so before doing this, we should use sftp_extension_supported()
+ * anyway */
+ /* st = sftp_fstatvfs(new_file);
+ assert_null(st); */
+
+ /* close */
+ rc = sftp_close(new_file);
+ assert_ssh_return_code(session, rc);
+
+ /* extended statvsf */
+ st = sftp_statvfs(sftp, tmp_file);
+ assert_non_null(st);
+ /* probably hard to check more */
+ sftp_statvfs_free(st);
+
+ /* Clean up temporary directory */
+ rc = sftp_unlink(sftp, tmp_file);
+ assert_int_equal(rc, SSH_OK);
+ rc = sftp_rmdir(sftp, tmp_dir);
+ assert_int_equal(rc, SSH_OK);
+}
+
+int torture_run_tests(void) {
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_server_establish_sftp,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_test_sftp_function,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_sftp_open_read_write,
+ session_setup_sftp,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_sftp_mkdir,
+ session_setup_sftp,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_sftp_realpath,
+ session_setup_sftp,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_sftp_symlink,
+ session_setup_sftp,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_server_sftp_extended,
+ session_setup_sftp,
+ session_teardown),
+ };
+
+ ssh_init();
+
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests,
+ setup_default_server,
+ teardown_default_server);
+
+ ssh_finalize();
+
+ return rc;
+}
diff --git a/tests/ssh_ping.c b/tests/ssh_ping.c
index bdb3cea0..01754590 100644
--- a/tests/ssh_ping.c
+++ b/tests/ssh_ping.c
@@ -60,7 +60,7 @@ int main(int argc, char **argv)
goto out;
}
- /* Enable all supported algorithms (including DSA) */
+ /* Enable all supported algorithms */
hostkeys = ssh_kex_get_supported_method(SSH_HOSTKEYS);
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, hostkeys);
if (rc < 0) {
diff --git a/tests/suppressions/lsan.supp b/tests/suppressions/lsan.supp
new file mode 100644
index 00000000..42a17e35
--- /dev/null
+++ b/tests/suppressions/lsan.supp
@@ -0,0 +1 @@
+leak:libcrypto.so
diff --git a/tests/tests_config.h.cmake b/tests/tests_config.h.cmake
index 13815ec6..3b6bde0e 100644
--- a/tests/tests_config.h.cmake
+++ b/tests/tests_config.h.cmake
@@ -52,20 +52,21 @@
#cmakedefine OPENSSH_SSH_ED25519 1
#cmakedefine OPENSSH_SSH_ED25519_CERT_V01_OPENSSH_COM 1
#cmakedefine OPENSSH_SSH_RSA 1
-#cmakedefine OPENSSH_SSH_DSS 1
#cmakedefine OPENSSH_ECDSA_SHA2_NISTP256 1
#cmakedefine OPENSSH_ECDSA_SHA2_NISTP384 1
#cmakedefine OPENSSH_ECDSA_SHA2_NISTP521 1
#cmakedefine OPENSSH_SSH_RSA_CERT_V01_OPENSSH_COM 1
-#cmakedefine OPENSSH_SSH_DSS_CERT_V01_OPENSSH_COM 1
#cmakedefine OPENSSH_ECDSA_SHA2_NISTP256_CERT_V01_OPENSSH_COM 1
#cmakedefine OPENSSH_ECDSA_SHA2_NISTP384_CERT_V01_OPENSSH_COM 1
#cmakedefine OPENSSH_ECDSA_SHA2_NISTP521_CERT_V01_OPENSSH_COM 1
/* Available programs */
-#cmakedefine NC_EXECUTABLE "${NC_EXECUTABLE}"
+#cmakedefine NCAT_EXECUTABLE "${NCAT_EXECUTABLE}"
#cmakedefine SSHD_EXECUTABLE "${SSHD_EXECUTABLE}"
#cmakedefine SSH_EXECUTABLE "${SSH_EXECUTABLE}"
#cmakedefine WITH_TIMEOUT ${WITH_TIMEOUT}
#cmakedefine TIMEOUT_EXECUTABLE "${TIMEOUT_EXECUTABLE}"
+#cmakedefine SOFTHSM2_LIBRARY "${SOFTHSM2_LIBRARY}"
+#cmakedefine P11_KIT_CLIENT "${P11_KIT_CLIENT}"
+#cmakedefine PKCS11SPY "${PKCS11SPY}"
diff --git a/tests/torture.c b/tests/torture.c
index 2d21b3b9..78edaae1 100644
--- a/tests/torture.c
+++ b/tests/torture.c
@@ -40,11 +40,6 @@
#include <unistd.h>
#elif (defined _WIN32) || (defined _WIN64)
#include <direct.h>
-#include <io.h>
-#define read _read
-#define open _open
-#define write _write
-#define close _close
#define chdir _chdir
#endif
@@ -53,6 +48,10 @@
#include "libssh/misc.h"
#include "libssh/token.h"
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
+
#define TORTURE_SSHD_SRV_IPV4 "127.0.0.10"
/* socket wrapper IPv6 prefix fd00::5357:5fxx */
#define TORTURE_SSHD_SRV_IPV6 "fd00::5357:5f0a"
@@ -77,6 +76,7 @@ static const char *pattern = NULL;
#ifndef _WIN32
+/* TODO missing code coverage */
static int _torture_auth_kbdint(ssh_session session,
const char *password) {
const char *prompt;
@@ -244,6 +244,13 @@ int torture_terminate_process(const char *pidfile)
/* 10 ms */
usleep(10 * 1000);
+#ifdef HAVE_VALGRIND_VALGRIND_H
+ if (RUNNING_ON_VALGRIND) {
+ SSH_LOG(SSH_LOG_INFO, "Running within Valgrind, wait one more "
+ "second for the server to clean up.");
+ usleep(1000 * 1000);
+ }
+#endif /* HAVE_VALGRIND_VALGRIND_H */
rc = kill(pid, 0);
if (rc != 0) {
@@ -383,18 +390,12 @@ ssh_bind torture_ssh_bind(const char *addr,
}
switch (key_type) {
-#ifdef HAVE_DSA
- case SSH_KEYTYPE_DSS:
- opts = SSH_BIND_OPTIONS_DSAKEY;
- break;
-#endif /* HAVE_DSA */
case SSH_KEYTYPE_RSA:
- opts = SSH_BIND_OPTIONS_RSAKEY;
- break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
- opts = SSH_BIND_OPTIONS_ECDSAKEY;
+ case SSH_KEYTYPE_ED25519:
+ opts = SSH_BIND_OPTIONS_HOSTKEY;
break;
default:
goto out_free;
@@ -631,9 +632,6 @@ void torture_setup_create_libssh_config(void **state)
{
struct torture_state *s = *state;
char ed25519_hostkey[1024] = {0};
-#ifdef HAVE_DSA
- char dsa_hostkey[1024];
-#endif /* HAVE_DSA */
char rsa_hostkey[1024];
char ecdsa_hostkey[1024];
char sshd_config[2048];
@@ -647,9 +645,6 @@ void torture_setup_create_libssh_config(void **state)
"%s %s\n"
"%s %s\n"
"%s %s\n"
-#ifdef HAVE_DSA
- "%s %s\n"
-#endif /* HAVE_DSA */
"%s\n"; /* The space for test-specific options */
bool written = false;
int rc;
@@ -686,13 +681,6 @@ void torture_setup_create_libssh_config(void **state)
"%s/sshd/ssh_host_ecdsa_key",
s->socket_dir);
-#ifdef HAVE_DSA
- snprintf(dsa_hostkey,
- sizeof(dsa_hostkey),
- "%s/sshd/ssh_host_dsa_key",
- s->socket_dir);
-#endif /* HAVE_DSA */
-
if (!written) {
torture_write_file(ed25519_hostkey,
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
@@ -700,10 +688,6 @@ void torture_setup_create_libssh_config(void **state)
torture_get_testkey(SSH_KEYTYPE_RSA, 0));
torture_write_file(ecdsa_hostkey,
torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
-#ifdef HAVE_DSA
- torture_write_file(dsa_hostkey,
- torture_get_testkey(SSH_KEYTYPE_DSS, 0));
-#endif /* HAVE_DSA */
}
additional_config = (s->srv_additional_config != NULL ?
@@ -714,21 +698,16 @@ void torture_setup_create_libssh_config(void **state)
"HostKey", ed25519_hostkey,
"HostKey", rsa_hostkey,
"HostKey", ecdsa_hostkey,
-#ifdef HAVE_DSA
- "HostKey", dsa_hostkey,
-#endif /* HAVE_DSA */
additional_config);
torture_write_file(s->srv_config, sshd_config);
}
+#ifdef SSHD_EXECUTABLE
static void torture_setup_create_sshd_config(void **state, bool pam)
{
struct torture_state *s = *state;
char ed25519_hostkey[1024] = {0};
-#ifdef HAVE_DSA
- char dsa_hostkey[1024];
-#endif /* HAVE_DSA */
char rsa_hostkey[1024];
char ecdsa_hostkey[1024];
char trusted_ca_pubkey[1024];
@@ -746,10 +725,8 @@ static void torture_setup_create_sshd_config(void **state, bool pam)
const char config_string[]=
"Port 22\n"
"ListenAddress 127.0.0.10\n"
+ "ListenAddress fd00::5357:5f0a\n"
"%s %s\n" /* ed25519 HostKey */
-#ifdef HAVE_DSA
- "%s %s\n" /* DSA HostKey */
-#endif /* HAVE_DSA */
"%s %s\n" /* RSA HostKey */
"%s %s\n" /* ECDSA HostKey */
"\n"
@@ -784,6 +761,7 @@ static void torture_setup_create_sshd_config(void **state, bool pam)
const char fips_config_string[]=
"Port 22\n"
"ListenAddress 127.0.0.10\n"
+ "ListenAddress fd00::5357:5f0a\n"
"%s %s\n" /* RSA HostKey */
"%s %s\n" /* ECDSA HostKey */
"\n"
@@ -869,13 +847,6 @@ static void torture_setup_create_sshd_config(void **state, bool pam)
"%s/sshd/ssh_host_ed25519_key",
s->socket_dir);
-#ifdef HAVE_DSA
- snprintf(dsa_hostkey,
- sizeof(dsa_hostkey),
- "%s/sshd/ssh_host_dsa_key",
- s->socket_dir);
-#endif /* HAVE_DSA */
-
snprintf(rsa_hostkey,
sizeof(rsa_hostkey),
"%s/sshd/ssh_host_rsa_key",
@@ -894,10 +865,6 @@ static void torture_setup_create_sshd_config(void **state, bool pam)
if (!written) {
torture_write_file(ed25519_hostkey,
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
-#ifdef HAVE_DSA
- torture_write_file(dsa_hostkey,
- torture_get_testkey(SSH_KEYTYPE_DSS, 0));
-#endif /* HAVE_DSA */
torture_write_file(rsa_hostkey,
torture_get_testkey(SSH_KEYTYPE_RSA, 0));
torture_write_file(ecdsa_hostkey,
@@ -934,9 +901,6 @@ static void torture_setup_create_sshd_config(void **state, bool pam)
snprintf(sshd_config, sizeof(sshd_config),
config_string,
"HostKey", ed25519_hostkey,
-#ifdef HAVE_DSA
- "HostKey", dsa_hostkey,
-#endif /* HAVE_DSA */
"HostKey", rsa_hostkey,
"HostKey", ecdsa_hostkey,
trusted_ca_pubkey,
@@ -949,7 +913,7 @@ static void torture_setup_create_sshd_config(void **state, bool pam)
torture_write_file(s->srv_config, sshd_config);
}
-static int torture_wait_for_daemon(unsigned int seconds)
+int torture_wait_for_daemon(unsigned int seconds)
{
struct ssh_timestamp start;
int rc;
@@ -1032,11 +996,13 @@ void torture_setup_libssh_server(void **state, const char *server_path)
}
/* Write the environment setting */
+ /* OPENSSL variable is needed to enable SHA1 */
printed = snprintf(env, sizeof(env),
"SOCKET_WRAPPER_DIR=%s "
"SOCKET_WRAPPER_DEFAULT_IFACE=10 "
"LD_PRELOAD=%s "
- "%s",
+ "%s "
+ "OPENSSL_ENABLE_SHA1_SIGNATURES=1",
s->socket_dir, ld_preload, force_fips);
if (printed < 0) {
fail_msg("Failed to print env!");
@@ -1145,23 +1111,14 @@ void torture_setup_sshd_server(void **state, bool pam)
assert_int_equal(rc, 0);
}
-void torture_setup_tokens(const char *temp_dir,
- const char *filename,
- const char object_name[],
- const char *load_public)
+void torture_free_state(struct torture_state *s)
{
- char token_setup_start_cmd[1024] = {0};
- int rc;
-
- snprintf(token_setup_start_cmd, sizeof(token_setup_start_cmd),
- "%s/tests/pkcs11/setup-softhsm-tokens.sh %s %s %s %s",
- BINARYDIR,
- temp_dir,
- filename,
- object_name, load_public);
-
- rc = system(token_setup_start_cmd);
- assert_return_code(rc, errno);
+ free(s->srv_config);
+ free(s->socket_dir);
+ free(s->pcap_file);
+ free(s->srv_pidfile);
+ free(s->srv_additional_config);
+ free(s);
}
void torture_teardown_socket_dir(void **state)
@@ -1187,13 +1144,7 @@ void torture_teardown_socket_dir(void **state)
}
s->plain_pcap = NULL;
#endif /* WITH_PCAP */
-
- free(s->srv_config);
- free(s->socket_dir);
- free(s->pcap_file);
- free(s->srv_pidfile);
- free(s->srv_additional_config);
- free(s);
+ torture_free_state(s);
}
static int
@@ -1245,6 +1196,82 @@ void torture_teardown_sshd_server(void **state)
torture_teardown_socket_dir(state);
}
+#endif /* SSHD_EXECUTABLE */
+
+#ifdef WITH_PKCS11_URI
+void torture_setup_tokens(const char *temp_dir,
+ const char *filename,
+ const char object_name[],
+ const char *load_public)
+{
+ char token_setup_start_cmd[1024] = {0};
+ char socket_path[1204] = {0};
+ char conf_path[1024] = {0};
+ char *env = NULL;
+ int rc;
+
+ rc = snprintf(token_setup_start_cmd,
+ sizeof(token_setup_start_cmd),
+ "%s/tests/pkcs11/setup-softhsm-tokens.sh %s %s %s %s %s %s",
+ BINARYDIR,
+ temp_dir,
+ filename,
+ object_name,
+ load_public,
+ SOFTHSM2_LIBRARY,
+#ifdef WITH_PKCS11_PROVIDER
+ P11_KIT_CLIENT
+#else
+ ""
+#endif
+ );
+ assert_int_not_equal(rc, sizeof(token_setup_start_cmd));
+
+ rc = system(token_setup_start_cmd);
+ assert_return_code(rc, errno);
+
+#ifdef WITH_PKCS11_PROVIDER
+ rc = snprintf(socket_path,
+ sizeof(socket_path),
+ "unix:path=%s/p11-kit-server.socket",
+ temp_dir);
+ assert_int_not_equal(rc, sizeof(socket_path));
+ setenv("P11_KIT_SERVER_ADDRESS", socket_path, 1);
+
+ setenv("PKCS11_PROVIDER_MODULE", P11_KIT_CLIENT, 1);
+ /* This is useful for debugging PKCS#11 calls */
+
+ env = getenv("TORTURE_PKCS11");
+ if (env != NULL && env[0] != '\0') {
+#ifdef PKCS11SPY
+ setenv("PKCS11SPY", P11_KIT_CLIENT, 1);
+ setenv("PKCS11_PROVIDER_MODULE", PKCS11SPY, 1);
+#else
+ fprintf(stderr, "[ TORTURE ] >>> pkcs11-spy not found\n");
+#endif
+ }
+#else
+ (void)env;
+
+ snprintf(conf_path, sizeof(conf_path), "%s/softhsm.conf", temp_dir);
+ setenv("SOFTHSM2_CONF", conf_path, 1);
+#endif /* WITH_PKCS11_PROVIDER */
+}
+
+void torture_cleanup_tokens(const char *temp_dir)
+{
+ char pidfile[1024] = {0};
+ int rc;
+ pid_t pid;
+
+#ifdef WITH_PKCS11_PROVIDER
+ snprintf(pidfile, sizeof(pidfile), "%s/p11-kit-server.pid", temp_dir);
+ torture_terminate_process(pidfile);
+#else
+ unsetenv("SOFTHSM2_CONF");
+#endif /* WITH_PKCS11_PROVIDER */
+}
+#endif /* WITH_PKCS11_URI */
char *torture_make_temp_dir(const char *template)
{
@@ -1625,12 +1652,14 @@ void torture_reset_config(ssh_session session)
memset(session->opts.options_seen, 0, sizeof(session->opts.options_seen));
}
-#if ((defined _WIN32) || (defined _WIN64)) && (defined USE_ATTRIBUTE_WEAK)
+#if defined(HAVE_WEAK_ATTRIBUTE) && defined(TORTURE_SHARED)
__attribute__((weak)) int torture_run_tests(void)
{
- fail();
+ fail_msg("torture_run_tests from shared library called");
+
+ return -1;
}
-#endif
+#endif /* defined(HAVE_WEAK_ATTRIBUTE) && defined(TORTURE_SHARED) */
int main(int argc, char **argv) {
struct argument_s arguments;
diff --git a/tests/torture.h b/tests/torture.h
index 599b53ea..eb2765da 100644
--- a/tests/torture.h
+++ b/tests/torture.h
@@ -24,10 +24,6 @@
#ifndef _TORTURE_H
#define _TORTURE_H
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -50,6 +46,7 @@
#endif /* assert_return_code */
#define TORTURE_SSH_SERVER "127.0.0.10"
+#define TORTURE_SSH_SERVER_IP6 "fd00::5357:5f0a"
#define TORTURE_SSH_USER_BOB "bob"
#define TORTURE_SSH_USER_BOB_PASSWORD "secret"
@@ -124,17 +121,25 @@ void _torture_filter_tests(struct CMUnitTest *tests, size_t ntests);
const char *torture_server_address(int domain);
int torture_server_port(void);
+int torture_wait_for_daemon(unsigned int seconds);
+
+#ifdef SSHD_EXECUTABLE
void torture_setup_socket_dir(void **state);
void torture_setup_sshd_server(void **state, bool pam);
-void torture_setup_tokens(const char *temp_dir,
- const char *filename,
- const char object_name[],
- const char *load_public);
void torture_teardown_socket_dir(void **state);
void torture_teardown_sshd_server(void **state);
int torture_update_sshd_config(void **state, const char *config);
+#endif /* SSHD_EXECUTABLE */
+
+#ifdef WITH_PKCS11_URI
+void torture_setup_tokens(const char *temp_dir,
+ const char *filename,
+ const char object_name[],
+ const char *load_public);
+void torture_cleanup_tokens(const char *temp_dir);
+#endif /* WITH_PKCS11_URI */
void torture_reset_config(ssh_session session);
@@ -142,15 +147,17 @@ void torture_setup_create_libssh_config(void **state);
void torture_setup_libssh_server(void **state, const char *server_path);
+#if defined(HAVE_WEAK_ATTRIBUTE) && defined(TORTURE_SHARED)
+__attribute__((weak)) int torture_run_tests(void);
+#else
/*
* This function must be defined in every unit test file.
*/
-#if ((defined _WIN32) || (defined _WIN64)) && (defined USE_ATTRIBUTE_WEAK)
-__attribute__((weak)) int torture_run_tests(void);
-#else
int torture_run_tests(void);
#endif
+void torture_free_state(struct torture_state *s);
+
char *torture_make_temp_dir(const char *template);
char *torture_create_temp_file(const char *template);
diff --git a/tests/torture_key.c b/tests/torture_key.c
index c4fb08ea..9bf71995 100644
--- a/tests/torture_key.c
+++ b/tests/torture_key.c
@@ -28,840 +28,1004 @@
#include "torture.h"
#include "torture_key.h"
+enum torture_format_e {
+ FORMAT_PEM = 0,
+ FORMAT_OPENSSH,
+ FORMAT_PKCS8,
+};
+
/****************************************************************************
- * DSA KEYS
+ * RSA KEYS
****************************************************************************/
static const char torture_rsa_private_testkey[] =
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "MIIEowIBAAKCAQEArAOREUWlBXJAKZ5hABYyxnRayDZP1bJeLbPVK+npxemrhHyZ\n"
- "gjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZWwjMKu/QYcycYp5HL01goxOxuusZb\n"
- "i+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDjUDniWabXQJge8ksGXGTiFeAJ/687\n"
- "uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqeGDzyeBZ1NLIuaiOORyLGSW4duHLD\n"
- "N78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9Uw4XQFTsWugUDEY1AU4c5g11nhzHz\n"
- "Bi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHrGQIDAQABAoIBAFJTaqy/jllq8vZ4\n"
- "TKiD900wBvrns5HtSlHJTe80hqQoT+Sa1cWSxPR0eekL32Hjy9igbMzZ83uWzh7I\n"
- "mtgNODy9vRdznfgO8CfTCaBfAzQsjFpr8QikMT6EUI/LpiRL1UaGsNOlSEvnSS0Z\n"
- "b1uDzAdrjL+nsEHEDJud+K9jwSkCRifVMy7fLfaum+YKpdeEz7K2Mgm5pJ/Vg+9s\n"
- "vI2V1q7HAOI4eUVTgJNHXy5ediRJlajQHf/lNUzHKqn7iH+JRl01gt62X8roG62b\n"
- "TbFylbheqMm9awuSF2ucOcx+guuwhkPir8BEMb08j3hiK+TfwPdY0F6QH4OhiKK7\n"
- "MTqTVgECgYEA0vmmu5GOBtwRmq6gVNCHhdLDQWaxAZqQRmRbzxVhFpbv0GjbQEF7\n"
- "tttq3fjDrzDf6CE9RtZWw2BUSXVq+IXB/bXb1kgWU2xWywm+OFDk9OXQs8ui+MY7\n"
- "FiP3yuq3YJob2g5CCsVQWl2CHvWGmTLhE1ODll39t7Y1uwdcDobJN+ECgYEA0LlR\n"
- "hfMjydWmwqooU9TDjXNBmwufyYlNFTH351amYgFUDpNf35SMCP4hDosUw/zCTDpc\n"
- "+1w04BJJfkH1SNvXSOilpdaYRTYuryDvGmWC66K2KX1nLErhlhs17CwzV997nYgD\n"
- "H3OOU4HfqIKmdGbjvWlkmY+mLHyG10bbpOTbujkCgYAc68xHejSWDCT9p2KjPdLW\n"
- "LYZGuOUa6y1L+QX85Vlh118Ymsczj8Z90qZbt3Zb1b9b+vKDe255agMj7syzNOLa\n"
- "/MseHNOyq+9Z9gP1hGFekQKDIy88GzCOYG/fiT2KKJYY1kuHXnUdbiQgSlghODBS\n"
- "jehD/K6DOJ80/FVKSH/dAQKBgQDJ+apTzpZhJ2f5k6L2jDq3VEK2ACedZEm9Kt9T\n"
- "c1wKFnL6r83kkuB3i0L9ycRMavixvwBfFDjuY4POs5Dh8ip/mPFCa0hqISZHvbzi\n"
- "dDyePJO9zmXaTJPDJ42kfpkofVAnfohXFQEy+cguTk848J+MmMIKfyE0h0QMabr9\n"
- "86BUsQKBgEVgoi4RXwmtGovtMew01ORPV9MOX3v+VnsCgD4/56URKOAngiS70xEP\n"
- "ONwNbTCWuuv43HGzJoVFiAMGnQP1BAJ7gkHkjSegOGKkiw12EPUWhFcMg+GkgPhc\n"
- "pOqNt/VMBPjJ/ysHJqmLfQK9A35JV6Cmdphe+OIl28bcKhAOz8Dw\n"
- "-----END RSA PRIVATE KEY-----\n";
+ "-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIIEowIBAAKCAQEArAOREUWlBXJAKZ5hABYyxnRayDZP1bJeLbPVK+npxemrhHyZ\n"
+ "gjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZWwjMKu/QYcycYp5HL01goxOxuusZb\n"
+ "i+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDjUDniWabXQJge8ksGXGTiFeAJ/687\n"
+ "uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqeGDzyeBZ1NLIuaiOORyLGSW4duHLD\n"
+ "N78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9Uw4XQFTsWugUDEY1AU4c5g11nhzHz\n"
+ "Bi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHrGQIDAQABAoIBAFJTaqy/jllq8vZ4\n"
+ "TKiD900wBvrns5HtSlHJTe80hqQoT+Sa1cWSxPR0eekL32Hjy9igbMzZ83uWzh7I\n"
+ "mtgNODy9vRdznfgO8CfTCaBfAzQsjFpr8QikMT6EUI/LpiRL1UaGsNOlSEvnSS0Z\n"
+ "b1uDzAdrjL+nsEHEDJud+K9jwSkCRifVMy7fLfaum+YKpdeEz7K2Mgm5pJ/Vg+9s\n"
+ "vI2V1q7HAOI4eUVTgJNHXy5ediRJlajQHf/lNUzHKqn7iH+JRl01gt62X8roG62b\n"
+ "TbFylbheqMm9awuSF2ucOcx+guuwhkPir8BEMb08j3hiK+TfwPdY0F6QH4OhiKK7\n"
+ "MTqTVgECgYEA0vmmu5GOBtwRmq6gVNCHhdLDQWaxAZqQRmRbzxVhFpbv0GjbQEF7\n"
+ "tttq3fjDrzDf6CE9RtZWw2BUSXVq+IXB/bXb1kgWU2xWywm+OFDk9OXQs8ui+MY7\n"
+ "FiP3yuq3YJob2g5CCsVQWl2CHvWGmTLhE1ODll39t7Y1uwdcDobJN+ECgYEA0LlR\n"
+ "hfMjydWmwqooU9TDjXNBmwufyYlNFTH351amYgFUDpNf35SMCP4hDosUw/zCTDpc\n"
+ "+1w04BJJfkH1SNvXSOilpdaYRTYuryDvGmWC66K2KX1nLErhlhs17CwzV997nYgD\n"
+ "H3OOU4HfqIKmdGbjvWlkmY+mLHyG10bbpOTbujkCgYAc68xHejSWDCT9p2KjPdLW\n"
+ "LYZGuOUa6y1L+QX85Vlh118Ymsczj8Z90qZbt3Zb1b9b+vKDe255agMj7syzNOLa\n"
+ "/MseHNOyq+9Z9gP1hGFekQKDIy88GzCOYG/fiT2KKJYY1kuHXnUdbiQgSlghODBS\n"
+ "jehD/K6DOJ80/FVKSH/dAQKBgQDJ+apTzpZhJ2f5k6L2jDq3VEK2ACedZEm9Kt9T\n"
+ "c1wKFnL6r83kkuB3i0L9ycRMavixvwBfFDjuY4POs5Dh8ip/mPFCa0hqISZHvbzi\n"
+ "dDyePJO9zmXaTJPDJ42kfpkofVAnfohXFQEy+cguTk848J+MmMIKfyE0h0QMabr9\n"
+ "86BUsQKBgEVgoi4RXwmtGovtMew01ORPV9MOX3v+VnsCgD4/56URKOAngiS70xEP\n"
+ "ONwNbTCWuuv43HGzJoVFiAMGnQP1BAJ7gkHkjSegOGKkiw12EPUWhFcMg+GkgPhc\n"
+ "pOqNt/VMBPjJ/ysHJqmLfQK9A35JV6Cmdphe+OIl28bcKhAOz8Dw\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+static const char torture_rsa_private_pkcs8_testkey[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsA5ERRaUFckAp\n"
+ "nmEAFjLGdFrINk/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34lHJaO+WXbDYYj7duW3\n"
+ "SP7H9lbCMwq79BhzJxinkcvTWCjE7G66xluL4qIdEYHrPQQx1cztTzZTuUD+P/8f\n"
+ "JmmnIONQOeJZptdAmB7ySwZcZOIV4An/rzu5X4klyMY/EAYVDHPKOK1/8Wsv1LRY\n"
+ "YplvKp4YPPJ4FnU0si5qI45HIsZJbh24csM3vwSawmfCqDaAlCZFJoPgE1kyO1t+\n"
+ "IVxIv1TDhdAVOxa6BQMRjUBThzmDXWeHMfMGL2ow63kPOtlCkPiPSADYs4ekeGg5\n"
+ "2DVm4esZAgMBAAECggEAUlNqrL+OWWry9nhMqIP3TTAG+uezke1KUclN7zSGpChP\n"
+ "5JrVxZLE9HR56QvfYePL2KBszNnze5bOHsia2A04PL29F3Od+A7wJ9MJoF8DNCyM\n"
+ "WmvxCKQxPoRQj8umJEvVRoaw06VIS+dJLRlvW4PMB2uMv6ewQcQMm534r2PBKQJG\n"
+ "J9UzLt8t9q6b5gql14TPsrYyCbmkn9WD72y8jZXWrscA4jh5RVOAk0dfLl52JEmV\n"
+ "qNAd/+U1TMcqqfuIf4lGXTWC3rZfyugbrZtNsXKVuF6oyb1rC5IXa5w5zH6C67CG\n"
+ "Q+KvwEQxvTyPeGIr5N/A91jQXpAfg6GIorsxOpNWAQKBgQDS+aa7kY4G3BGarqBU\n"
+ "0IeF0sNBZrEBmpBGZFvPFWEWlu/QaNtAQXu222rd+MOvMN/oIT1G1lbDYFRJdWr4\n"
+ "hcH9tdvWSBZTbFbLCb44UOT05dCzy6L4xjsWI/fK6rdgmhvaDkIKxVBaXYIe9YaZ\n"
+ "MuETU4OWXf23tjW7B1wOhsk34QKBgQDQuVGF8yPJ1abCqihT1MONc0GbC5/JiU0V\n"
+ "MffnVqZiAVQOk1/flIwI/iEOixTD/MJMOlz7XDTgEkl+QfVI29dI6KWl1phFNi6v\n"
+ "IO8aZYLrorYpfWcsSuGWGzXsLDNX33udiAMfc45Tgd+ogqZ0ZuO9aWSZj6YsfIbX\n"
+ "Rtuk5Nu6OQKBgBzrzEd6NJYMJP2nYqM90tYthka45RrrLUv5BfzlWWHXXxiaxzOP\n"
+ "xn3Splu3dlvVv1v68oN7bnlqAyPuzLM04tr8yx4c07Kr71n2A/WEYV6RAoMjLzwb\n"
+ "MI5gb9+JPYoolhjWS4dedR1uJCBKWCE4MFKN6EP8roM4nzT8VUpIf90BAoGBAMn5\n"
+ "qlPOlmEnZ/mTovaMOrdUQrYAJ51kSb0q31NzXAoWcvqvzeSS4HeLQv3JxExq+LG/\n"
+ "AF8UOO5jg86zkOHyKn+Y8UJrSGohJke9vOJ0PJ48k73OZdpMk8MnjaR+mSh9UCd+\n"
+ "iFcVATL5yC5OTzjwn4yYwgp/ITSHRAxpuv3zoFSxAoGARWCiLhFfCa0ai+0x7DTU\n"
+ "5E9X0w5fe/5WewKAPj/npREo4CeCJLvTEQ843A1tMJa66/jccbMmhUWIAwadA/UE\n"
+ "AnuCQeSNJ6A4YqSLDXYQ9RaEVwyD4aSA+Fyk6o239UwE+Mn/KwcmqYt9Ar0DfklX\n"
+ "oKZ2mF744iXbxtwqEA7PwPA=\n"
+ "-----END PRIVATE KEY-----\n";
static const char torture_rsa_private_testkey_passphrase[] =
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "Proc-Type: 4,ENCRYPTED\n"
- "DEK-Info: AES-128-CBC,5375534F40903DD66B3851A0DA03F6FA\n"
- "\n"
- "m5YYTNOMd1xCKfifwCX4R1iLJoAc4cn1aFiL7f2kBbfE2jF1LTQBJV1h1CqYZfAB\n"
- "WtM/7FkQPnKXqsMndP+v+1Xc+PYigE3AezJj/0g7xn/zIBwGjkLAp435AdL5i6Fg\n"
- "OhOL8LyolRrcGn17jE4S4iGbzw8PVyfzNzdj0Emwql5F6M7pgLbInRNKM/TF4z2h\n"
- "b6Pi9Bw43dwaJ7wiiy/vo/v4MyXsJBoeKbc4VCmxiYFvAYCvVFlDkyIw/QnR3MKQ\n"
- "g/Zsk7Pw3aOioxk6LJpZ5x0tO23nXDG1aOZHWykI0BpJV+LIpD2oSYOHJyVO83XT\n"
- "RQUMSTXc2K2+ejs0XQoLt/GxDDHe+8W8fWQK3C7Lyvl9oKjmb5sTWi3mdSv0C+zR\n"
- "n5KSVbUKNXrjix7qPKkv5rWqb84CKVnCMb7tWaPLR19nQqKVYBIs6v0OTTvS6Le7\n"
- "lz4lxBkcUy6vi0tWH9MvLuT+ugdHLJZ4UXBthCgV58pM1o+L+WMIl+SZXckiCAO3\n"
- "7ercA57695IA6iHskmr3eazJsYFEVFdR/cm+IDy2FPkKmJMjXeIWuh3yASBk7LBR\n"
- "EQq3CC7AioO+Vj8m/fEIiNZJSQ6p0NmgnPoO3rTYT/IobmE99/Ht6oNLmFX4Pr7e\n"
- "F4CGWKzwxWpCnw2vVolCFByASmZycbJvrIonZBKY1toU28lRm4tCM6eCNISVLMeE\n"
- "VtQ+1PH9/2KZspZl+SX/kjV3egggy0TFKRU8EcYPJFC3Vpy+shEai35KBVo44Z18\n"
- "apza7exm3igNEqOqe07hLs3Bjhvk1oS+WhMbAG9ARTOKuyBOJh/ZV9tFMNZ6v+q5\n"
- "TofgNcIhNYNascymU1io18xTW9c3RRcmRKqIWnj4EH8o7Aojv/l+zvdV7/GVlR4W\n"
- "pR9cuJEiyiEjS46axoc6dSOtdnvag+BpFQb+lGY97F9nNGyBdtLD5ASVh5OVG4fu\n"
- "Pf0O7Bdj1kIuBhV8axE/slf6UHANiodeqkR9B24+0Cy+miPiHazzUkbdSJ4r03g5\n"
- "J1Y5S8qbl9++sqhQMLMUkeK4pDWh1aocA9bDA2RcBNuXGiZeRFUiqxcBS+iO418n\n"
- "DFyWz4UfI/m1IRSjoo/PEpgu5GmosUzs3Dl4nAcf/REBEX6M/kKKxHTLjE8DxDsz\n"
- "fn/vfsXV3s0tbN7YyJdP8aU+ApZntw1OF2TS2qS8CPWHTcCGGTab5WEGC3xFXKp0\n"
- "uyonCxV7vNLOiIiHdQX+1bLu7ps7GBH92xGkPg7FrNNcMc07soP7jjjB578n9Gpl\n"
- "cIDBdgovTRFHiWu3yRspVt0zPfMJB/hqn+IAp98wfvjl8OZM1ZZkejnwXnQil5ZU\n"
- "wjEBEtx+nX56vdxipzKoHh5yDXmPbNajBYkg3rXJrLFh3Tsf0CzHcLdHNz/qJ9LO\n"
- "wH16grjR1Q0CzCW3FAv0Q0euqkXac+TfuIg3HiTPrBPnJQW1uivrx1F5tpO/uboG\n"
- "h28LwqJLYh+1T0V//uiy3SMATpYKvzg2byGct9VUib8QVop8LvVF/n42RaxtTCfw\n"
- "JSvUyxoaZUjQkT7iF94HsF+FVVJdI55UjgnMiZ0d5vKffWyTHYcYHkFYaSloAMWN\n"
- "-----END RSA PRIVATE KEY-----\n";
+ "-----BEGIN RSA PRIVATE KEY-----\n"
+ "Proc-Type: 4,ENCRYPTED\n"
+ "DEK-Info: AES-128-CBC,5375534F40903DD66B3851A0DA03F6FA\n"
+ "\n"
+ "m5YYTNOMd1xCKfifwCX4R1iLJoAc4cn1aFiL7f2kBbfE2jF1LTQBJV1h1CqYZfAB\n"
+ "WtM/7FkQPnKXqsMndP+v+1Xc+PYigE3AezJj/0g7xn/zIBwGjkLAp435AdL5i6Fg\n"
+ "OhOL8LyolRrcGn17jE4S4iGbzw8PVyfzNzdj0Emwql5F6M7pgLbInRNKM/TF4z2h\n"
+ "b6Pi9Bw43dwaJ7wiiy/vo/v4MyXsJBoeKbc4VCmxiYFvAYCvVFlDkyIw/QnR3MKQ\n"
+ "g/Zsk7Pw3aOioxk6LJpZ5x0tO23nXDG1aOZHWykI0BpJV+LIpD2oSYOHJyVO83XT\n"
+ "RQUMSTXc2K2+ejs0XQoLt/GxDDHe+8W8fWQK3C7Lyvl9oKjmb5sTWi3mdSv0C+zR\n"
+ "n5KSVbUKNXrjix7qPKkv5rWqb84CKVnCMb7tWaPLR19nQqKVYBIs6v0OTTvS6Le7\n"
+ "lz4lxBkcUy6vi0tWH9MvLuT+ugdHLJZ4UXBthCgV58pM1o+L+WMIl+SZXckiCAO3\n"
+ "7ercA57695IA6iHskmr3eazJsYFEVFdR/cm+IDy2FPkKmJMjXeIWuh3yASBk7LBR\n"
+ "EQq3CC7AioO+Vj8m/fEIiNZJSQ6p0NmgnPoO3rTYT/IobmE99/Ht6oNLmFX4Pr7e\n"
+ "F4CGWKzwxWpCnw2vVolCFByASmZycbJvrIonZBKY1toU28lRm4tCM6eCNISVLMeE\n"
+ "VtQ+1PH9/2KZspZl+SX/kjV3egggy0TFKRU8EcYPJFC3Vpy+shEai35KBVo44Z18\n"
+ "apza7exm3igNEqOqe07hLs3Bjhvk1oS+WhMbAG9ARTOKuyBOJh/ZV9tFMNZ6v+q5\n"
+ "TofgNcIhNYNascymU1io18xTW9c3RRcmRKqIWnj4EH8o7Aojv/l+zvdV7/GVlR4W\n"
+ "pR9cuJEiyiEjS46axoc6dSOtdnvag+BpFQb+lGY97F9nNGyBdtLD5ASVh5OVG4fu\n"
+ "Pf0O7Bdj1kIuBhV8axE/slf6UHANiodeqkR9B24+0Cy+miPiHazzUkbdSJ4r03g5\n"
+ "J1Y5S8qbl9++sqhQMLMUkeK4pDWh1aocA9bDA2RcBNuXGiZeRFUiqxcBS+iO418n\n"
+ "DFyWz4UfI/m1IRSjoo/PEpgu5GmosUzs3Dl4nAcf/REBEX6M/kKKxHTLjE8DxDsz\n"
+ "fn/vfsXV3s0tbN7YyJdP8aU+ApZntw1OF2TS2qS8CPWHTcCGGTab5WEGC3xFXKp0\n"
+ "uyonCxV7vNLOiIiHdQX+1bLu7ps7GBH92xGkPg7FrNNcMc07soP7jjjB578n9Gpl\n"
+ "cIDBdgovTRFHiWu3yRspVt0zPfMJB/hqn+IAp98wfvjl8OZM1ZZkejnwXnQil5ZU\n"
+ "wjEBEtx+nX56vdxipzKoHh5yDXmPbNajBYkg3rXJrLFh3Tsf0CzHcLdHNz/qJ9LO\n"
+ "wH16grjR1Q0CzCW3FAv0Q0euqkXac+TfuIg3HiTPrBPnJQW1uivrx1F5tpO/uboG\n"
+ "h28LwqJLYh+1T0V//uiy3SMATpYKvzg2byGct9VUib8QVop8LvVF/n42RaxtTCfw\n"
+ "JSvUyxoaZUjQkT7iF94HsF+FVVJdI55UjgnMiZ0d5vKffWyTHYcYHkFYaSloAMWN\n"
+ "-----END RSA PRIVATE KEY-----\n";
static const char torture_rsa_private_pkcs8_testkey_passphrase[] =
- "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
- "MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI0RSm1ZXOBD8CAggA\n"
- "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBBS+59quuIVuxN/H9Wltk8TBIIE\n"
- "0J7OhRw35ANRyTU2qhlhS8NATcguoD1J4IMXpXpv38iCBWd2bjxvuWnEu4aBX7iU\n"
- "desfz9n6AoTVqURaOMLsv6EFV0tycf+mZsmdUmrD2270Wyj6TtQD8LO/7ibifCeL\n"
- "XCCKjxciueSggHp5lnfogZwn8wjSEDP7OqNVRTwm8QKNrE7J5m5giFrjXoyqKM7r\n"
- "DBa35UIZAXXY8z9CkI+GsyRtaZik3VD+xHShwUriOYg4x4VGZQLj24tjoUnqU4ml\n"
- "iRMhGyYpxN7CnfaIwHJr3T0dmbT/BIXOQ2B6sWakioZeUuA6OTBHbFTUN9TUHaF0\n"
- "rDMVmjL6BQcEiWwjvtw/3NLdkcKFjMiLTWA2GL71KPGCecpMmAMjo+ijnxeVhqpQ\n"
- "dnhowG92DhCSf/XZI0vaaYflrV54U9PgcSPDFWmTOVe5151Mi8eR9qrCanfyHmX1\n"
- "MLXs8Mw6xWedNj8AWLV3JGiWEeAEATuTAQfTqmBZbzaFKfSKp5PZjWxa5bZIomzS\n"
- "Q0AsONTeYmKK+Pv95RYlgR2kKqhwy3OmcOuepwnzSeAGh1BdBzd2raoipkq1fpY5\n"
- "8e75dJnTGvWfqfh0VXz/Wud+hMz/98Mh6Bnp9l+Ddxpp4RioWB2aH0HM8ZGTlbhf\n"
- "r5qFmDY7k+RfDDp7K7UYMA+2hHCxY1aFSHVYGRQKdYdKIugLtKx6YKLeGVCR7Gbm\n"
- "l/88qiGshF/qhdFbPb4K0Tz2Ug5uklveOQSkKX6RSZ30IW+N3E4nH/wvyOwbCPk7\n"
- "u+iHB2zzk2Hws4O52a0Gqj+RbeGzzhl1D9jH35GMHUsfhDSA3/mmrVC7hiN/Aplt\n"
- "2OmKFAkobZh/1UJAHBY9feIhLmQUy9dwy0E8G/0LEyyZYEizDC76jsvbh2cPg3jM\n"
- "JsI31qUaGggwh3wB034BvsYIf/ZqLCt8hAXF9U5U7T5y3r6FNNBla8zlj25ILog6\n"
- "t/bhOwFKYXamAVYMhhvUiA3YIYuBxT7MrgL7gDtKh3N/DleS/pLjmOFfMI3dfCd0\n"
- "KSQX46uw7aFbV0Has9uUuGle9Foq52QFvYnDHWJuIyOvJ5st1Hd3Mjjsl9t3JFVM\n"
- "I1aDZ17Z4LoThdezNQKGaAe5z7gGFMKKsm55CMT/7FxvConALeQKGAV6jA5xZzl4\n"
- "+QB14YlxlZTxYnXd/69KGV56wP8sb6uMVDC/f5Vd3oHsamJKpPgts8WCn11f9wFn\n"
- "Mx8YY/vBVVLQMw1aB+82Vk+Ix8YDYIPj5bJk2BkyCCUnMYkKswUOVzsdUq0xssEp\n"
- "PASw0YvQ9mY2aQ9exme99JuAj5t4qIXoYTSrX5iv6NXtzDHgTR1pl9gQQVQ0zAUO\n"
- "ZHKZXYAv5rLZKRcyeCLw0LkuthY2QtN3PsBlaRtfwZTaqUbBGbvEkcx5fxdEsasS\n"
- "yQkZKBBvIi42LUN9ZzywYNGbOanCZ04p/+QscmmnVGuDMZJyaDRaapW6f0nJQ+lQ\n"
- "CaVPRzLKGnHV5hWQDjTaPIh2s9rJSZJ3HyE8qshETHW/vQoYIcVB9TX5TnOY02Ak\n"
- "IINKfSZGgz/NBeJItjk30UuTcISk65ekoXZIHHgdxD9iHy9D0w6FXcPNLLsWQn7n\n"
- "jS4Bvt0VZ9zVAiyyVO4yAaMgP+saitYpjMgI8g67geD3\n"
- "-----END ENCRYPTED PRIVATE KEY-----\n";
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
+ "MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI0RSm1ZXOBD8CAggA\n"
+ "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBBS+59quuIVuxN/H9Wltk8TBIIE\n"
+ "0J7OhRw35ANRyTU2qhlhS8NATcguoD1J4IMXpXpv38iCBWd2bjxvuWnEu4aBX7iU\n"
+ "desfz9n6AoTVqURaOMLsv6EFV0tycf+mZsmdUmrD2270Wyj6TtQD8LO/7ibifCeL\n"
+ "XCCKjxciueSggHp5lnfogZwn8wjSEDP7OqNVRTwm8QKNrE7J5m5giFrjXoyqKM7r\n"
+ "DBa35UIZAXXY8z9CkI+GsyRtaZik3VD+xHShwUriOYg4x4VGZQLj24tjoUnqU4ml\n"
+ "iRMhGyYpxN7CnfaIwHJr3T0dmbT/BIXOQ2B6sWakioZeUuA6OTBHbFTUN9TUHaF0\n"
+ "rDMVmjL6BQcEiWwjvtw/3NLdkcKFjMiLTWA2GL71KPGCecpMmAMjo+ijnxeVhqpQ\n"
+ "dnhowG92DhCSf/XZI0vaaYflrV54U9PgcSPDFWmTOVe5151Mi8eR9qrCanfyHmX1\n"
+ "MLXs8Mw6xWedNj8AWLV3JGiWEeAEATuTAQfTqmBZbzaFKfSKp5PZjWxa5bZIomzS\n"
+ "Q0AsONTeYmKK+Pv95RYlgR2kKqhwy3OmcOuepwnzSeAGh1BdBzd2raoipkq1fpY5\n"
+ "8e75dJnTGvWfqfh0VXz/Wud+hMz/98Mh6Bnp9l+Ddxpp4RioWB2aH0HM8ZGTlbhf\n"
+ "r5qFmDY7k+RfDDp7K7UYMA+2hHCxY1aFSHVYGRQKdYdKIugLtKx6YKLeGVCR7Gbm\n"
+ "l/88qiGshF/qhdFbPb4K0Tz2Ug5uklveOQSkKX6RSZ30IW+N3E4nH/wvyOwbCPk7\n"
+ "u+iHB2zzk2Hws4O52a0Gqj+RbeGzzhl1D9jH35GMHUsfhDSA3/mmrVC7hiN/Aplt\n"
+ "2OmKFAkobZh/1UJAHBY9feIhLmQUy9dwy0E8G/0LEyyZYEizDC76jsvbh2cPg3jM\n"
+ "JsI31qUaGggwh3wB034BvsYIf/ZqLCt8hAXF9U5U7T5y3r6FNNBla8zlj25ILog6\n"
+ "t/bhOwFKYXamAVYMhhvUiA3YIYuBxT7MrgL7gDtKh3N/DleS/pLjmOFfMI3dfCd0\n"
+ "KSQX46uw7aFbV0Has9uUuGle9Foq52QFvYnDHWJuIyOvJ5st1Hd3Mjjsl9t3JFVM\n"
+ "I1aDZ17Z4LoThdezNQKGaAe5z7gGFMKKsm55CMT/7FxvConALeQKGAV6jA5xZzl4\n"
+ "+QB14YlxlZTxYnXd/69KGV56wP8sb6uMVDC/f5Vd3oHsamJKpPgts8WCn11f9wFn\n"
+ "Mx8YY/vBVVLQMw1aB+82Vk+Ix8YDYIPj5bJk2BkyCCUnMYkKswUOVzsdUq0xssEp\n"
+ "PASw0YvQ9mY2aQ9exme99JuAj5t4qIXoYTSrX5iv6NXtzDHgTR1pl9gQQVQ0zAUO\n"
+ "ZHKZXYAv5rLZKRcyeCLw0LkuthY2QtN3PsBlaRtfwZTaqUbBGbvEkcx5fxdEsasS\n"
+ "yQkZKBBvIi42LUN9ZzywYNGbOanCZ04p/+QscmmnVGuDMZJyaDRaapW6f0nJQ+lQ\n"
+ "CaVPRzLKGnHV5hWQDjTaPIh2s9rJSZJ3HyE8qshETHW/vQoYIcVB9TX5TnOY02Ak\n"
+ "IINKfSZGgz/NBeJItjk30UuTcISk65ekoXZIHHgdxD9iHy9D0w6FXcPNLLsWQn7n\n"
+ "jS4Bvt0VZ9zVAiyyVO4yAaMgP+saitYpjMgI8g67geD3\n"
+ "-----END ENCRYPTED PRIVATE KEY-----\n";
static const char torture_rsa_private_openssh_testkey_passphrase[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDX\n"
- "ClCBeHgYyOEqmWpAanz9AAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAAB\n"
- "AQDXvXuawzaArEwkLIXTz/EWywLOCtqQL3P9yKkrhz6AplXP2PhOh5pyxa1VfGKe\n"
- "453jNeYBJ0ROto3BshXgZXbo86oLXTkbe0gO5xi3r5WjXxjOFvRRTLot5fPLNDOv\n"
- "9+TnsPmkNn0iIeyPnfrcPIyjWt5zSWUfkNC8oNHxsiSshjpbJvTXSDipukpUy41d\n"
- "7jg4uWGuonMTF7yu7HfuHqq7lhb0WlwSpfbqAbfYARBddcdcARyhix4RMWZZqVY2\n"
- "0H3Vsjq8bjKC+NJXFce1PRg+qcOWQdlXEei4dkzAvHvfQRx1TjzkrBZ6B6thmZty\n"
- "eb9IsiB0tg2g0JN2VTAGkxqpAAADwG8gm8jZpx+GIKdhV+igcvYvIhzA+fz6UdXf\n"
- "d/8wnYzMXtg+Ys7XsKUsxtMD8HGPiuwYsTrd/YGiol7SpkJV0STqtW+UZrcKamJ5\n"
- "reFaDoIU8hhWTXCe/ogplTxH/zNNK7Xx5OAGnNWE3zsR1vbZaCv+Vwwa27eUCbpv\n"
- "V1+92nBwkah3FCKCbwYDvTVRn1TZHQwnuNxDCRrlwaMjf8eX2ssqLLX7jqrb3j1u\n"
- "c28GR3fNJ8ENaWshZ77tqexUQCnCx14/qtT434CMvENXnCP5BP/cRmbOlCFQ6Id7\n"
- "nLMW0uDIy/q3xBsAcdMyV0LJW7sJNXIjTnS4lyXd0XescXrqTAKxTkqd1E0VIBpc\n"
- "37+7vqv9A9Xxq74jy//L9L4Yrbijc9Vt+oNWFgOuakZGBLIQvm36Oqb0z0oWJcUt\n"
- "VdZcvkCNMeixBqCnrQ8egO3x0pnZwo6cwH586Me8FgFacOnzWjzuQT6vYJ4EK5ch\n"
- "YNRQpjtz5+T3rZK7eIF1ZUobM4S6di7A6lW9tycQVhjo5XlhalMfCfajhazgcIrY\n"
- "Qdaq8+AguP8H+3bvXPZmitL8/mv5uVjqxy1lYh2xLzViTmFnvfdbZ92BWI9C6JBI\n"
- "+mRWzXeEY71MjfeEaPStwBm5OYBMFwYrXPL7E3JjAXRxbB+LKUksj/lRk3K7aQp4\n"
- "IDKCzAACgkOixfP39BgKQkrLjAoi6mEDqu5Ajc3GoljXsJEkcbu0j+0tVth+41nV\n"
- "8yCkP5SVUQTCSKzoduE+0pk6oYO6vrwKLM62cQRPXLl/XNoUqETIe8dklIKojYo6\n"
- "3ho1RaHgYr9/NAS0029CFt/rGmONWF9ihKON6wMavJRcofZ25FeylKiP2rrqdDIb\n"
- "EiWULZi3MUJfKBwSeZMwaYYmSpaOZF1U/MgvEfeRkE1UmDp3FmBLSNHBYhAxNazH\n"
- "R393BTr1zk7h+8s7QK986ZtcKkyUNXEK1NkLLuKlqMwFnjiOdeAIGwz9NEn+Tj60\n"
- "jE5IcCE06B6ze/MOZcsPp1SoZv4kKmgWY5Gdqv/9O9SyFQ0Yh4MvBSD8l4x0epId\n"
- "8Xm54ISVWP1SZ1x3Oe8yvtwOGqDkZeOVjnP7EQ7R0+1PZzW5P/x47skACqadGChN\n"
- "ahbngIl+EhPOqhx+wIfDbtzTmGABgNhcI/d02b8py5MXFnA+uzeSucDREYRdm2TO\n"
- "TQQ2CtxB6lcatIYG4AhyouQbujLd/AwpZJ05S1i/Qt6NenTgK3YyTWdXLQnjZSMx\n"
- "FBRkf+Jj9eVXieT4PJKtWuvxNNrJVA==\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDX\n"
+ "ClCBeHgYyOEqmWpAanz9AAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAAB\n"
+ "AQDXvXuawzaArEwkLIXTz/EWywLOCtqQL3P9yKkrhz6AplXP2PhOh5pyxa1VfGKe\n"
+ "453jNeYBJ0ROto3BshXgZXbo86oLXTkbe0gO5xi3r5WjXxjOFvRRTLot5fPLNDOv\n"
+ "9+TnsPmkNn0iIeyPnfrcPIyjWt5zSWUfkNC8oNHxsiSshjpbJvTXSDipukpUy41d\n"
+ "7jg4uWGuonMTF7yu7HfuHqq7lhb0WlwSpfbqAbfYARBddcdcARyhix4RMWZZqVY2\n"
+ "0H3Vsjq8bjKC+NJXFce1PRg+qcOWQdlXEei4dkzAvHvfQRx1TjzkrBZ6B6thmZty\n"
+ "eb9IsiB0tg2g0JN2VTAGkxqpAAADwG8gm8jZpx+GIKdhV+igcvYvIhzA+fz6UdXf\n"
+ "d/8wnYzMXtg+Ys7XsKUsxtMD8HGPiuwYsTrd/YGiol7SpkJV0STqtW+UZrcKamJ5\n"
+ "reFaDoIU8hhWTXCe/ogplTxH/zNNK7Xx5OAGnNWE3zsR1vbZaCv+Vwwa27eUCbpv\n"
+ "V1+92nBwkah3FCKCbwYDvTVRn1TZHQwnuNxDCRrlwaMjf8eX2ssqLLX7jqrb3j1u\n"
+ "c28GR3fNJ8ENaWshZ77tqexUQCnCx14/qtT434CMvENXnCP5BP/cRmbOlCFQ6Id7\n"
+ "nLMW0uDIy/q3xBsAcdMyV0LJW7sJNXIjTnS4lyXd0XescXrqTAKxTkqd1E0VIBpc\n"
+ "37+7vqv9A9Xxq74jy//L9L4Yrbijc9Vt+oNWFgOuakZGBLIQvm36Oqb0z0oWJcUt\n"
+ "VdZcvkCNMeixBqCnrQ8egO3x0pnZwo6cwH586Me8FgFacOnzWjzuQT6vYJ4EK5ch\n"
+ "YNRQpjtz5+T3rZK7eIF1ZUobM4S6di7A6lW9tycQVhjo5XlhalMfCfajhazgcIrY\n"
+ "Qdaq8+AguP8H+3bvXPZmitL8/mv5uVjqxy1lYh2xLzViTmFnvfdbZ92BWI9C6JBI\n"
+ "+mRWzXeEY71MjfeEaPStwBm5OYBMFwYrXPL7E3JjAXRxbB+LKUksj/lRk3K7aQp4\n"
+ "IDKCzAACgkOixfP39BgKQkrLjAoi6mEDqu5Ajc3GoljXsJEkcbu0j+0tVth+41nV\n"
+ "8yCkP5SVUQTCSKzoduE+0pk6oYO6vrwKLM62cQRPXLl/XNoUqETIe8dklIKojYo6\n"
+ "3ho1RaHgYr9/NAS0029CFt/rGmONWF9ihKON6wMavJRcofZ25FeylKiP2rrqdDIb\n"
+ "EiWULZi3MUJfKBwSeZMwaYYmSpaOZF1U/MgvEfeRkE1UmDp3FmBLSNHBYhAxNazH\n"
+ "R393BTr1zk7h+8s7QK986ZtcKkyUNXEK1NkLLuKlqMwFnjiOdeAIGwz9NEn+Tj60\n"
+ "jE5IcCE06B6ze/MOZcsPp1SoZv4kKmgWY5Gdqv/9O9SyFQ0Yh4MvBSD8l4x0epId\n"
+ "8Xm54ISVWP1SZ1x3Oe8yvtwOGqDkZeOVjnP7EQ7R0+1PZzW5P/x47skACqadGChN\n"
+ "ahbngIl+EhPOqhx+wIfDbtzTmGABgNhcI/d02b8py5MXFnA+uzeSucDREYRdm2TO\n"
+ "TQQ2CtxB6lcatIYG4AhyouQbujLd/AwpZJ05S1i/Qt6NenTgK3YyTWdXLQnjZSMx\n"
+ "FBRkf+Jj9eVXieT4PJKtWuvxNNrJVA==\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_rsa_private_openssh_testkey[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdz\n"
- "c2gtcnNhAAAAAwEAAQAAAQEA1717msM2gKxMJCyF08/xFssCzgrakC9z/cipK4c+\n"
- "gKZVz9j4ToeacsWtVXxinuOd4zXmASdETraNwbIV4GV26POqC105G3tIDucYt6+V\n"
- "o18Yzhb0UUy6LeXzyzQzr/fk57D5pDZ9IiHsj5363DyMo1rec0llH5DQvKDR8bIk\n"
- "rIY6Wyb010g4qbpKVMuNXe44OLlhrqJzExe8rux37h6qu5YW9FpcEqX26gG32AEQ\n"
- "XXXHXAEcoYseETFmWalWNtB91bI6vG4ygvjSVxXHtT0YPqnDlkHZVxHouHZMwLx7\n"
- "30EcdU485KwWegerYZmbcnm/SLIgdLYNoNCTdlUwBpMaqQAAA7iQHqVWkB6lVgAA\n"
- "AAdzc2gtcnNhAAABAQDXvXuawzaArEwkLIXTz/EWywLOCtqQL3P9yKkrhz6AplXP\n"
- "2PhOh5pyxa1VfGKe453jNeYBJ0ROto3BshXgZXbo86oLXTkbe0gO5xi3r5WjXxjO\n"
- "FvRRTLot5fPLNDOv9+TnsPmkNn0iIeyPnfrcPIyjWt5zSWUfkNC8oNHxsiSshjpb\n"
- "JvTXSDipukpUy41d7jg4uWGuonMTF7yu7HfuHqq7lhb0WlwSpfbqAbfYARBddcdc\n"
- "ARyhix4RMWZZqVY20H3Vsjq8bjKC+NJXFce1PRg+qcOWQdlXEei4dkzAvHvfQRx1\n"
- "TjzkrBZ6B6thmZtyeb9IsiB0tg2g0JN2VTAGkxqpAAAAAwEAAQAAAQAdjR3uQAkq\n"
- "LO+tENAwCE680YgL0x7HG0jnHWJWzQq5so8UjmLM1vRH/l3U1Nnpa8JHyi08QTWx\n"
- "Fn5qZstqVluoYyAKuHVHF2bya6NOHeYAX9lU+X3z2O+zs8jmL7tYwjr/pZU8ch5H\n"
- "25+8uGYRXtXg1mScJBSO81Y0UE8RrVYqr2Os583yB657kYiVYYYSZlRGd9wmfXnJ\n"
- "w0t8LaYcTn+i/lOvrJGa0Q0iV6+4rYmjwYd/D/vyNzF31hUEFrn3vDSgTnJdShgH\n"
- "VqW0OwNuEDe/4p8KkKR1EVVj6xv4zicwouY7aQI+zT3MwAzvNdvYwytsIj6bhT9x\n"
- "oyeAAIW0vaKVAAAAgQD6pPfu6tb7DiTlaH3/IPdGh3PTIf0zXHZ/ygxORXBZdoLY\n"
- "Fq2h/YnBd2Hs8vARAjGJYs78gTPP0FVXPV8ut38xct4DQ2hbPMrjWv5gdhDazq8Q\n"
- "qaFEa0+DeYONej8ItKwpsV2Rskkv5Pfm7M6EffVty1uzOpIcT8RYDAYUlc5D/wAA\n"
- "AIEA+44ykLho3BDWnUzshVEm6iNoqlZqcDVcNSpCuYDnCy5UrTDk0zj+OUG9M0Zx\n"
- "4c7kAmu/poXSimgAgMh9GNCzy3+a70WvH+fBqvG5tXLaSOQCswSdQjltANAnlt5L\n"
- "YDHzGGJBsS4pYxoz22MKhFbpYUCQJvotXnZJpTQU6hdFRX8AAACBANuNSlFq/vG8\n"
- "Vf9c2YsPiITmOrYxpUDMiMLvUGQOdyIIc45EAggOFHNF3AdPZEhinpD92EK+LiJc\n"
- "WYJ26muVcicZoddgmpcHRt2gByC+ckWOM4sLpih6EyQLFZfqTx2X+KOI0ZTt7zEi\n"
- "zfm1MJUNDFOr3DM0VBIf34Bn1hU/isPXAAAAAAEC\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
-
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdz\n"
+ "c2gtcnNhAAAAAwEAAQAAAQEA1717msM2gKxMJCyF08/xFssCzgrakC9z/cipK4c+\n"
+ "gKZVz9j4ToeacsWtVXxinuOd4zXmASdETraNwbIV4GV26POqC105G3tIDucYt6+V\n"
+ "o18Yzhb0UUy6LeXzyzQzr/fk57D5pDZ9IiHsj5363DyMo1rec0llH5DQvKDR8bIk\n"
+ "rIY6Wyb010g4qbpKVMuNXe44OLlhrqJzExe8rux37h6qu5YW9FpcEqX26gG32AEQ\n"
+ "XXXHXAEcoYseETFmWalWNtB91bI6vG4ygvjSVxXHtT0YPqnDlkHZVxHouHZMwLx7\n"
+ "30EcdU485KwWegerYZmbcnm/SLIgdLYNoNCTdlUwBpMaqQAAA7iQHqVWkB6lVgAA\n"
+ "AAdzc2gtcnNhAAABAQDXvXuawzaArEwkLIXTz/EWywLOCtqQL3P9yKkrhz6AplXP\n"
+ "2PhOh5pyxa1VfGKe453jNeYBJ0ROto3BshXgZXbo86oLXTkbe0gO5xi3r5WjXxjO\n"
+ "FvRRTLot5fPLNDOv9+TnsPmkNn0iIeyPnfrcPIyjWt5zSWUfkNC8oNHxsiSshjpb\n"
+ "JvTXSDipukpUy41d7jg4uWGuonMTF7yu7HfuHqq7lhb0WlwSpfbqAbfYARBddcdc\n"
+ "ARyhix4RMWZZqVY20H3Vsjq8bjKC+NJXFce1PRg+qcOWQdlXEei4dkzAvHvfQRx1\n"
+ "TjzkrBZ6B6thmZtyeb9IsiB0tg2g0JN2VTAGkxqpAAAAAwEAAQAAAQAdjR3uQAkq\n"
+ "LO+tENAwCE680YgL0x7HG0jnHWJWzQq5so8UjmLM1vRH/l3U1Nnpa8JHyi08QTWx\n"
+ "Fn5qZstqVluoYyAKuHVHF2bya6NOHeYAX9lU+X3z2O+zs8jmL7tYwjr/pZU8ch5H\n"
+ "25+8uGYRXtXg1mScJBSO81Y0UE8RrVYqr2Os583yB657kYiVYYYSZlRGd9wmfXnJ\n"
+ "w0t8LaYcTn+i/lOvrJGa0Q0iV6+4rYmjwYd/D/vyNzF31hUEFrn3vDSgTnJdShgH\n"
+ "VqW0OwNuEDe/4p8KkKR1EVVj6xv4zicwouY7aQI+zT3MwAzvNdvYwytsIj6bhT9x\n"
+ "oyeAAIW0vaKVAAAAgQD6pPfu6tb7DiTlaH3/IPdGh3PTIf0zXHZ/ygxORXBZdoLY\n"
+ "Fq2h/YnBd2Hs8vARAjGJYs78gTPP0FVXPV8ut38xct4DQ2hbPMrjWv5gdhDazq8Q\n"
+ "qaFEa0+DeYONej8ItKwpsV2Rskkv5Pfm7M6EffVty1uzOpIcT8RYDAYUlc5D/wAA\n"
+ "AIEA+44ykLho3BDWnUzshVEm6iNoqlZqcDVcNSpCuYDnCy5UrTDk0zj+OUG9M0Zx\n"
+ "4c7kAmu/poXSimgAgMh9GNCzy3+a70WvH+fBqvG5tXLaSOQCswSdQjltANAnlt5L\n"
+ "YDHzGGJBsS4pYxoz22MKhFbpYUCQJvotXnZJpTQU6hdFRX8AAACBANuNSlFq/vG8\n"
+ "Vf9c2YsPiITmOrYxpUDMiMLvUGQOdyIIc45EAggOFHNF3AdPZEhinpD92EK+LiJc\n"
+ "WYJ26muVcicZoddgmpcHRt2gByC+ckWOM4sLpih6EyQLFZfqTx2X+KOI0ZTt7zEi\n"
+ "zfm1MJUNDFOr3DM0VBIf34Bn1hU/isPXAAAAAAEC\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_rsa_public_testkey[] =
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsA5ERRaUFckApnmEAFjLGdFrIN"
- "k/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34lHJaO+WXbDYYj7duW3SP7H9lbCMwq79B"
- "hzJxinkcvTWCjE7G66xluL4qIdEYHrPQQx1cztTzZTuUD+P/8fJmmnIONQOeJZptd"
- "AmB7ySwZcZOIV4An/rzu5X4klyMY/EAYVDHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0"
- "si5qI45HIsZJbh24csM3vwSawmfCqDaAlCZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6B"
- "QMRjUBThzmDXWeHMfMGL2ow63kPOtlCkPiPSADYs4ekeGg52DVm4esZ "
- "aris@aris-air\n";
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsA5ERRaUFckApnmEAFjLGdFrIN"
+ "k/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34lHJaO+WXbDYYj7duW3SP7H9lbCMwq79B"
+ "hzJxinkcvTWCjE7G66xluL4qIdEYHrPQQx1cztTzZTuUD+P/8fJmmnIONQOeJZptd"
+ "AmB7ySwZcZOIV4An/rzu5X4klyMY/EAYVDHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0"
+ "si5qI45HIsZJbh24csM3vwSawmfCqDaAlCZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6B"
+ "QMRjUBThzmDXWeHMfMGL2ow63kPOtlCkPiPSADYs4ekeGg52DVm4esZ "
+ "aris@aris-air\n";
static const char torture_rsa_public_testkey_pem[] =
- "-----BEGIN PUBLIC KEY-----\n"
- "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAOREUWlBXJAKZ5hABYy\n"
- "xnRayDZP1bJeLbPVK+npxemrhHyZgjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZW\n"
- "wjMKu/QYcycYp5HL01goxOxuusZbi+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDj\n"
- "UDniWabXQJge8ksGXGTiFeAJ/687uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqe\n"
- "GDzyeBZ1NLIuaiOORyLGSW4duHLDN78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9U\n"
- "w4XQFTsWugUDEY1AU4c5g11nhzHzBi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHr\n"
- "GQIDAQAB\n"
- "-----END PUBLIC KEY-----\n";
-
+ "-----BEGIN PUBLIC KEY-----\n"
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAOREUWlBXJAKZ5hABYy\n"
+ "xnRayDZP1bJeLbPVK+npxemrhHyZgjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZW\n"
+ "wjMKu/QYcycYp5HL01goxOxuusZbi+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDj\n"
+ "UDniWabXQJge8ksGXGTiFeAJ/687uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqe\n"
+ "GDzyeBZ1NLIuaiOORyLGSW4duHLDN78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9U\n"
+ "w4XQFTsWugUDEY1AU4c5g11nhzHzBi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHr\n"
+ "GQIDAQAB\n"
+ "-----END PUBLIC KEY-----\n";
static const char torture_rsa_testkey_cert[] =
- "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNz"
- "aC5jb20AAAAgL77S/SgY969FbEtNBsbLvvtGFgnEHaPb+V7ajwuf+R0AAAADAQABA"
- "AABAQCsA5ERRaUFckApnmEAFjLGdFrINk/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34"
- "lHJaO+WXbDYYj7duW3SP7H9lbCMwq79BhzJxinkcvTWCjE7G66xluL4qIdEYHrPQQ"
- "x1cztTzZTuUD+P/8fJmmnIONQOeJZptdAmB7ySwZcZOIV4An/rzu5X4klyMY/EAYV"
- "DHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0si5qI45HIsZJbh24csM3vwSawmfCqDaAl"
- "CZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6BQMRjUBThzmDXWeHMfMGL2ow63kPOtlCkP"
- "iPSADYs4ekeGg52DVm4esZAAAAAAAAAAAAAAABAAAADmxpYnNzaF90b3J0dXJlAAA"
- "AAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRp"
- "bmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtc"
- "G9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdX"
- "Nlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAoowcv2Gn8tO"
- "eDyw/lgdMpoBsLtHTTdVVOOo5HwMFvj/lFkbZlb6J2n9GIE64HNPE45vSnIdJZwz4"
- "UYfTvtnNKNHp1MgMrjK1Z6EjyZsGqDZ+BhmvcKA6IckkhBJnDV7U9dMrovAWha61Z"
- "9GpDqB1naRfbwqJQwSRHF1p71Cnf0fZKxOhAVx0ophmYGz3x3qq4PeOZv3Yl0AHTV"
- "dRmqmeELDUxeuXN2bgSyb881zEgdaKHH5oWySykP4uwjn6T7ETuL2MsDdG3HZHDhn"
- "LzLmfzOZ/cNadMCrgauMluQKc5dYF2TSeDaUxwun/NPMQBVZdETHLAMBgkGmhRUku"
- "flVDIQAAAQ8AAAAHc3NoLXJzYQAAAQADSp4b/Zta8zs6v47iwmxV2Gbucvt1kDrvT"
- "vKAKSbGN0+zoMyXiNfMHM/OvZObDS/WWGs4GMRqbJavwO3ja/dQY17oJss23lZ+Rc"
- "Lw4Rqsi3/ZEPCnX6ficiRS/yRN/LAkoXvx9vBx9QHfxlzF6JXq07wTt21zxW0tntd"
- "8dL+JI9ZZ9YylnxF3gHqfRFe2ahJpiywmxm0yOZgDmimOhep59i6BH5zHiPALvpge"
- "Mbk075oA5K9XKsHTflCcsQRQH+pXqaNQGL37z2CFz9oezxQYvIqqKF0w/eeRIARoA"
- "neB6OdgTpKFsmgPZVtqrvhjw+b5T8a4W4iWSl+6wg6gowAm "
- "rsa_privkey.pub\n";
+ "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNz"
+ "aC5jb20AAAAgL77S/SgY969FbEtNBsbLvvtGFgnEHaPb+V7ajwuf+R0AAAADAQABA"
+ "AABAQCsA5ERRaUFckApnmEAFjLGdFrINk/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34"
+ "lHJaO+WXbDYYj7duW3SP7H9lbCMwq79BhzJxinkcvTWCjE7G66xluL4qIdEYHrPQQ"
+ "x1cztTzZTuUD+P/8fJmmnIONQOeJZptdAmB7ySwZcZOIV4An/rzu5X4klyMY/EAYV"
+ "DHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0si5qI45HIsZJbh24csM3vwSawmfCqDaAl"
+ "CZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6BQMRjUBThzmDXWeHMfMGL2ow63kPOtlCkP"
+ "iPSADYs4ekeGg52DVm4esZAAAAAAAAAAAAAAABAAAADmxpYnNzaF90b3J0dXJlAAA"
+ "AAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRp"
+ "bmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtc"
+ "G9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdX"
+ "Nlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAoowcv2Gn8tO"
+ "eDyw/lgdMpoBsLtHTTdVVOOo5HwMFvj/lFkbZlb6J2n9GIE64HNPE45vSnIdJZwz4"
+ "UYfTvtnNKNHp1MgMrjK1Z6EjyZsGqDZ+BhmvcKA6IckkhBJnDV7U9dMrovAWha61Z"
+ "9GpDqB1naRfbwqJQwSRHF1p71Cnf0fZKxOhAVx0ophmYGz3x3qq4PeOZv3Yl0AHTV"
+ "dRmqmeELDUxeuXN2bgSyb881zEgdaKHH5oWySykP4uwjn6T7ETuL2MsDdG3HZHDhn"
+ "LzLmfzOZ/cNadMCrgauMluQKc5dYF2TSeDaUxwun/NPMQBVZdETHLAMBgkGmhRUku"
+ "flVDIQAAAQ8AAAAHc3NoLXJzYQAAAQADSp4b/Zta8zs6v47iwmxV2Gbucvt1kDrvT"
+ "vKAKSbGN0+zoMyXiNfMHM/OvZObDS/WWGs4GMRqbJavwO3ja/dQY17oJss23lZ+Rc"
+ "Lw4Rqsi3/ZEPCnX6ficiRS/yRN/LAkoXvx9vBx9QHfxlzF6JXq07wTt21zxW0tntd"
+ "8dL+JI9ZZ9YylnxF3gHqfRFe2ahJpiywmxm0yOZgDmimOhep59i6BH5zHiPALvpge"
+ "Mbk075oA5K9XKsHTflCcsQRQH+pXqaNQGL37z2CFz9oezxQYvIqqKF0w/eeRIARoA"
+ "neB6OdgTpKFsmgPZVtqrvhjw+b5T8a4W4iWSl+6wg6gowAm "
+ "rsa_privkey.pub\n";
/****************************************************************************
* DSA KEYS
****************************************************************************/
static const char torture_dsa_private_testkey[] =
- "-----BEGIN DSA PRIVATE KEY-----\n"
- "MIIBuwIBAAKBgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n"
- "wvH3yjkfoFhhREvoY7IPnwAu5bcxw8TkISq7YROQ409PqwwPvy0N3GUp/+kKS268\n"
- "BIJ+VKN513XRf7eL1e4aHUJ+al9x1JxTmc6T0GBq1lyu+CTUUyh25aNDFwIVAK84\n"
- "j20GmU+zewjQwsIXuVb6C/PHAoGAXhuIVsJxUQJ5nWQRLf7o3XEGQ+EcVmHOzMB1\n"
- "xCsHjYnpEhhco+r/HDZSD31kzDeAZUycz31WqGL8yXr+OZRLqEsGC7dwEAzPiXDu\n"
- "l0zHcl0yiKPrRrLgNJHeKcT6JflBngK7jQRIVUg3F3104fbVa2rwaniLl4GSBZPX\n"
- "MpUdng8CgYB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7\n"
- "n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2u\n"
- "ADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIgIVAI1Hd8/i\n"
- "Pzsg7bTzoNvjQL+Noyiy\n"
- "-----END DSA PRIVATE KEY-----\n";
+ "-----BEGIN DSA PRIVATE KEY-----\n"
+ "MIIBuwIBAAKBgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n"
+ "wvH3yjkfoFhhREvoY7IPnwAu5bcxw8TkISq7YROQ409PqwwPvy0N3GUp/+kKS268\n"
+ "BIJ+VKN513XRf7eL1e4aHUJ+al9x1JxTmc6T0GBq1lyu+CTUUyh25aNDFwIVAK84\n"
+ "j20GmU+zewjQwsIXuVb6C/PHAoGAXhuIVsJxUQJ5nWQRLf7o3XEGQ+EcVmHOzMB1\n"
+ "xCsHjYnpEhhco+r/HDZSD31kzDeAZUycz31WqGL8yXr+OZRLqEsGC7dwEAzPiXDu\n"
+ "l0zHcl0yiKPrRrLgNJHeKcT6JflBngK7jQRIVUg3F3104fbVa2rwaniLl4GSBZPX\n"
+ "MpUdng8CgYB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7\n"
+ "n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2u\n"
+ "ADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIgIVAI1Hd8/i\n"
+ "Pzsg7bTzoNvjQL+Noyiy\n"
+ "-----END DSA PRIVATE KEY-----\n";
+
+static const char torture_dsa_private_pkcs8_testkey[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIBSwIBADCCASsGByqGSM44BAEwggEeAoGBAJTK9U8SSfdSdkOPMLNLNIelOW3O\n"
+ "vQRz7WbP8kAKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fAC7ltzHDxOQhKrthE5Dj\n"
+ "T0+rDA+/LQ3cZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5qX3HUnFOZzpPQYGrW\n"
+ "XK74JNRTKHblo0MXAhUArziPbQaZT7N7CNDCwhe5VvoL88cCgYBeG4hWwnFRAnmd\n"
+ "ZBEt/ujdcQZD4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWTMN4BlTJzPfVaoYvzJ\n"
+ "ev45lEuoSwYLt3AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+UGeAruNBEhVSDcX\n"
+ "fXTh9tVravBqeIuXgZIFk9cylR2eDwQXAhUAjUd3z+I/OyDttPOg2+NAv42jKLI=\n"
+ "-----END PRIVATE KEY-----\n";
static const char torture_dsa_private_testkey_passphrase[] =
- "-----BEGIN DSA PRIVATE KEY-----\n"
- "Proc-Type: 4,ENCRYPTED\n"
- "DEK-Info: AES-128-CBC,266023B64B1B814BCD0D0E477257F06D\n"
- "\n"
- "QJQErZrvYsfeMNMnU+6yVHH5Zze/zUFdPip7Bon4T1wCGlVasn4x/GQcMm1+mgmb\n"
- "PCK/qJ5qw9nCepLYJq2xh8gohbwF/XKxeaNGcRA2+ancTooDUjeRTlk1WRtS1+bq\n"
- "LBkwhxLXW26lIuQUHzfi93rRqQI2LC4McngY7L7WVJer7sH7hk5//4Gf6zHtPEl+\n"
- "Tr2ub1zNrVbh6e1Bitw7DaGZNX6XEWpyTTsAd42sQWh6o23MC6GyfS1YFsPGHzGe\n"
- "WYQbWn2AZ1mK32z2mLZfVg41qu9RKG20iCyaczZ2YmuYyOkoLHijOAHC8vZbHwYC\n"
- "+lN9Yc8/BoMuMMwDTMDaJD0TsBX02hi9YI7Gu88PMCJO+SRe5400MonUMXTwCa91\n"
- "Tt3RhYpBzx2XGOq5199+oLdTJAaXHJcuB6viKNdSLBuhx6RAEJXZnVexchaHs4Q6\n"
- "HweIv6Et8MjVoqwkaQDmcIGA73qZ0lbUJFZAu2YDJ6TpHc1lHZes763HoMYfuvkX\n"
- "HTSuHZ7edjoWqwnl/vkc3+nG//IEj8LqAacx0i4krDcQpGuQ6BnPfwPFco2NQQpw\n"
- "wHBOL6HrOnD+gGs6DUFwzA==\n"
- "-----END DSA PRIVATE KEY-----\n";
+ "-----BEGIN DSA PRIVATE KEY-----\n"
+ "Proc-Type: 4,ENCRYPTED\n"
+ "DEK-Info: AES-128-CBC,266023B64B1B814BCD0D0E477257F06D\n"
+ "\n"
+ "QJQErZrvYsfeMNMnU+6yVHH5Zze/zUFdPip7Bon4T1wCGlVasn4x/GQcMm1+mgmb\n"
+ "PCK/qJ5qw9nCepLYJq2xh8gohbwF/XKxeaNGcRA2+ancTooDUjeRTlk1WRtS1+bq\n"
+ "LBkwhxLXW26lIuQUHzfi93rRqQI2LC4McngY7L7WVJer7sH7hk5//4Gf6zHtPEl+\n"
+ "Tr2ub1zNrVbh6e1Bitw7DaGZNX6XEWpyTTsAd42sQWh6o23MC6GyfS1YFsPGHzGe\n"
+ "WYQbWn2AZ1mK32z2mLZfVg41qu9RKG20iCyaczZ2YmuYyOkoLHijOAHC8vZbHwYC\n"
+ "+lN9Yc8/BoMuMMwDTMDaJD0TsBX02hi9YI7Gu88PMCJO+SRe5400MonUMXTwCa91\n"
+ "Tt3RhYpBzx2XGOq5199+oLdTJAaXHJcuB6viKNdSLBuhx6RAEJXZnVexchaHs4Q6\n"
+ "HweIv6Et8MjVoqwkaQDmcIGA73qZ0lbUJFZAu2YDJ6TpHc1lHZes763HoMYfuvkX\n"
+ "HTSuHZ7edjoWqwnl/vkc3+nG//IEj8LqAacx0i4krDcQpGuQ6BnPfwPFco2NQQpw\n"
+ "wHBOL6HrOnD+gGs6DUFwzA==\n"
+ "-----END DSA PRIVATE KEY-----\n";
static const char torture_dsa_private_pkcs8_testkey_passphrase[] =
- "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
- "MIIBrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI8001emUNAOECAggA\n"
- "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBDgXXvQsVxY6zaAQVwzUwvDBIIB\n"
- "UOBQqqJs4rYK6R0rXFitkdUodOK3CdFAKodyCkSC5cgoW2+ht2ndRCepxuKB2X14\n"
- "Lvt1CIxPvu1k7bGnd25kePmNF85cJxG9wf0/+6vpptO3fTUdsUKyLcRKDqvxxOMB\n"
- "OSqQK1MLgvUxB5uBSGCsKqFkVUPYs46uihfozjqHH2IghHSQr+VczhFDoWtzgcgp\n"
- "nRNZiyXN5Thob5WOrL849TSlcaMyI3ssErEVP1G2t3ax5bLQ4AqDddumoRBed/XY\n"
- "lad5QGAS2XlwMFj8tR/Spi1fEWfamIsvh23ba5ksb35TT3SUJd2gf2NC7QEz3dUK\n"
- "YDSSeRSF24c4nXBsJ94TkVuUujo4X3QSaWQ2anYYBBwfQtrddVNVu95QS2sQGLov\n"
- "UWIhq1xXbnL/SGC6E5T1VGnAx3qwfDEZX5tTNzkwqeTZfkrb6vRk+O+Lxt67iP+n\n"
- "nw==\n"
- "-----END ENCRYPTED PRIVATE KEY-----\n";
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
+ "MIIBrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI8001emUNAOECAggA\n"
+ "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBDgXXvQsVxY6zaAQVwzUwvDBIIB\n"
+ "UOBQqqJs4rYK6R0rXFitkdUodOK3CdFAKodyCkSC5cgoW2+ht2ndRCepxuKB2X14\n"
+ "Lvt1CIxPvu1k7bGnd25kePmNF85cJxG9wf0/+6vpptO3fTUdsUKyLcRKDqvxxOMB\n"
+ "OSqQK1MLgvUxB5uBSGCsKqFkVUPYs46uihfozjqHH2IghHSQr+VczhFDoWtzgcgp\n"
+ "nRNZiyXN5Thob5WOrL849TSlcaMyI3ssErEVP1G2t3ax5bLQ4AqDddumoRBed/XY\n"
+ "lad5QGAS2XlwMFj8tR/Spi1fEWfamIsvh23ba5ksb35TT3SUJd2gf2NC7QEz3dUK\n"
+ "YDSSeRSF24c4nXBsJ94TkVuUujo4X3QSaWQ2anYYBBwfQtrddVNVu95QS2sQGLov\n"
+ "UWIhq1xXbnL/SGC6E5T1VGnAx3qwfDEZX5tTNzkwqeTZfkrb6vRk+O+Lxt67iP+n\n"
+ "nw==\n"
+ "-----END ENCRYPTED PRIVATE KEY-----\n";
static const char torture_dsa_private_openssh_testkey_passphrase[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBC\n"
- "UZK61oXs3uKMs4l7G0cpAAAAEAAAAAEAAAGxAAAAB3NzaC1kc3MAAACBAJTK9U8S\n"
- "SfdSdkOPMLNLNIelOW3OvQRz7WbP8kAKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+f\n"
- "AC7ltzHDxOQhKrthE5DjT0+rDA+/LQ3cZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hod\n"
- "Qn5qX3HUnFOZzpPQYGrWXK74JNRTKHblo0MXAAAAFQCvOI9tBplPs3sI0MLCF7lW\n"
- "+gvzxwAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD4RxWYc7MwHXEKweNiekSGFyj6v8c\n"
- "NlIPfWTMN4BlTJzPfVaoYvzJev45lEuoSwYLt3AQDM+JcO6XTMdyXTKIo+tGsuA0\n"
- "kd4pxPol+UGeAruNBEhVSDcXfXTh9tVravBqeIuXgZIFk9cylR2eDwAAAIB4roDQ\n"
- "Bfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7n2mw3iqZG0xnG3iv\n"
- "1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2uADmhirI6dRZUVO+/\n"
- "iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIgAAAeAtGFEW6JZTeSumizZJI4T2\n"
- "Kha05Ze3juTeW+BMjqTcf77yAL2jvsljogCtu4+5CWWO4g+cr80vyVytji6IYTNM\n"
- "MPn1qe6dHXnfmgtiegHXxrjr5v5/i1cvD32Bxffy+yjR9kbV9GJYF+K5pfYVpQBa\n"
- "XVmq6AJUPd/yxKw6jRGZJi8GTcrKbCZAL+VYSPwc0veCrmGPjeeMCgYcEXPvhSui\n"
- "P0JnG1Ap12FeK+61rIbZBAr7qbTGJi5Z5HlDlgon2tmMZOkIuL1Oytgut4MpmYjP\n"
- "ph+qrzgwfSwOsjVIuHlb1L0phWRlgbT8lmysEE7McGKWiCOabxgl3NF9lClhDBb9\n"
- "nzupkK1cg/4p17USYMOdeNhTmJ0DkQT+8UenfBOmzV7kamLlEYXJdDZBN//dZ8UR\n"
- "KEzAzpaAVIyJQ+wvCUIh/VO8sJP+3q4XQUkv0QcIRlc0+r9qbW2Tqv3vajFcFtK6\n"
- "nrTmIJVL0pG+z/93Ncpy5susD+JvhJ4yfl7Jet3jy4fWwm3qkLl0WsobJ7Om+GyH\n"
- "DzHH9RgDk3XuUHS/fz+kTwmtyIH/Rq1jIt+s+T8iA9CzKSX6sBu2yfMo1w2/LbCx\n"
- "Xy1rHS42TePw28m1cQuUfjqdOC3IBgQ1m3x2f1on7hk=\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBC\n"
+ "UZK61oXs3uKMs4l7G0cpAAAAEAAAAAEAAAGxAAAAB3NzaC1kc3MAAACBAJTK9U8S\n"
+ "SfdSdkOPMLNLNIelOW3OvQRz7WbP8kAKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+f\n"
+ "AC7ltzHDxOQhKrthE5DjT0+rDA+/LQ3cZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hod\n"
+ "Qn5qX3HUnFOZzpPQYGrWXK74JNRTKHblo0MXAAAAFQCvOI9tBplPs3sI0MLCF7lW\n"
+ "+gvzxwAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD4RxWYc7MwHXEKweNiekSGFyj6v8c\n"
+ "NlIPfWTMN4BlTJzPfVaoYvzJev45lEuoSwYLt3AQDM+JcO6XTMdyXTKIo+tGsuA0\n"
+ "kd4pxPol+UGeAruNBEhVSDcXfXTh9tVravBqeIuXgZIFk9cylR2eDwAAAIB4roDQ\n"
+ "Bfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7n2mw3iqZG0xnG3iv\n"
+ "1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2uADmhirI6dRZUVO+/\n"
+ "iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIgAAAeAtGFEW6JZTeSumizZJI4T2\n"
+ "Kha05Ze3juTeW+BMjqTcf77yAL2jvsljogCtu4+5CWWO4g+cr80vyVytji6IYTNM\n"
+ "MPn1qe6dHXnfmgtiegHXxrjr5v5/i1cvD32Bxffy+yjR9kbV9GJYF+K5pfYVpQBa\n"
+ "XVmq6AJUPd/yxKw6jRGZJi8GTcrKbCZAL+VYSPwc0veCrmGPjeeMCgYcEXPvhSui\n"
+ "P0JnG1Ap12FeK+61rIbZBAr7qbTGJi5Z5HlDlgon2tmMZOkIuL1Oytgut4MpmYjP\n"
+ "ph+qrzgwfSwOsjVIuHlb1L0phWRlgbT8lmysEE7McGKWiCOabxgl3NF9lClhDBb9\n"
+ "nzupkK1cg/4p17USYMOdeNhTmJ0DkQT+8UenfBOmzV7kamLlEYXJdDZBN//dZ8UR\n"
+ "KEzAzpaAVIyJQ+wvCUIh/VO8sJP+3q4XQUkv0QcIRlc0+r9qbW2Tqv3vajFcFtK6\n"
+ "nrTmIJVL0pG+z/93Ncpy5susD+JvhJ4yfl7Jet3jy4fWwm3qkLl0WsobJ7Om+GyH\n"
+ "DzHH9RgDk3XuUHS/fz+kTwmtyIH/Rq1jIt+s+T8iA9CzKSX6sBu2yfMo1w2/LbCx\n"
+ "Xy1rHS42TePw28m1cQuUfjqdOC3IBgQ1m3x2f1on7hk=\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_dsa_private_openssh_testkey[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdz\n"
- "c2gtZHNzAAAAgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n"
- "wvH3yjkfoFhhREvoY7IPnwAu5bcxw8TkISq7YROQ409PqwwPvy0N3GUp/+kKS268\n"
- "BIJ+VKN513XRf7eL1e4aHUJ+al9x1JxTmc6T0GBq1lyu+CTUUyh25aNDFwAAABUA\n"
- "rziPbQaZT7N7CNDCwhe5VvoL88cAAACAXhuIVsJxUQJ5nWQRLf7o3XEGQ+EcVmHO\n"
- "zMB1xCsHjYnpEhhco+r/HDZSD31kzDeAZUycz31WqGL8yXr+OZRLqEsGC7dwEAzP\n"
- "iXDul0zHcl0yiKPrRrLgNJHeKcT6JflBngK7jQRIVUg3F3104fbVa2rwaniLl4GS\n"
- "BZPXMpUdng8AAACAeK6A0AX4H/AKEgCQG+8vDlb8beXE+4qmkTEF9l4LVtPTbvUW\n"
- "1CFUu59psN4qmRtMZxt4r9aC/YMDVzC5X/oC5rKlN+/PI2hGfSIWfFXYBwKB5ulF\n"
- "jO29rgA5oYqyOnUWVFTvv4jBlLw8WuujiOIQ1dOeaW8EIbcbqVGrU4cg8yIAAAHY\n"
- "tbI937WyPd8AAAAHc3NoLWRzcwAAAIEAlMr1TxJJ91J2Q48ws0s0h6U5bc69BHPt\n"
- "Zs/yQAoxzASfQv1vz8Lx98o5H6BYYURL6GOyD58ALuW3McPE5CEqu2ETkONPT6sM\n"
- "D78tDdxlKf/pCktuvASCflSjedd10X+3i9XuGh1CfmpfcdScU5nOk9BgatZcrvgk\n"
- "1FModuWjQxcAAAAVAK84j20GmU+zewjQwsIXuVb6C/PHAAAAgF4biFbCcVECeZ1k\n"
- "ES3+6N1xBkPhHFZhzszAdcQrB42J6RIYXKPq/xw2Ug99ZMw3gGVMnM99Vqhi/Ml6\n"
- "/jmUS6hLBgu3cBAMz4lw7pdMx3JdMoij60ay4DSR3inE+iX5QZ4Cu40ESFVINxd9\n"
- "dOH21Wtq8Gp4i5eBkgWT1zKVHZ4PAAAAgHiugNAF+B/wChIAkBvvLw5W/G3lxPuK\n"
- "ppExBfZeC1bT0271FtQhVLufabDeKpkbTGcbeK/Wgv2DA1cwuV/6AuaypTfvzyNo\n"
- "Rn0iFnxV2AcCgebpRYztva4AOaGKsjp1FlRU77+IwZS8PFrro4jiENXTnmlvBCG3\n"
- "G6lRq1OHIPMiAAAAFQCNR3fP4j87IO2086Db40C/jaMosgAAAAABAg==\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdz\n"
+ "c2gtZHNzAAAAgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n"
+ "wvH3yjkfoFhhREvoY7IPnwAu5bcxw8TkISq7YROQ409PqwwPvy0N3GUp/+kKS268\n"
+ "BIJ+VKN513XRf7eL1e4aHUJ+al9x1JxTmc6T0GBq1lyu+CTUUyh25aNDFwAAABUA\n"
+ "rziPbQaZT7N7CNDCwhe5VvoL88cAAACAXhuIVsJxUQJ5nWQRLf7o3XEGQ+EcVmHO\n"
+ "zMB1xCsHjYnpEhhco+r/HDZSD31kzDeAZUycz31WqGL8yXr+OZRLqEsGC7dwEAzP\n"
+ "iXDul0zHcl0yiKPrRrLgNJHeKcT6JflBngK7jQRIVUg3F3104fbVa2rwaniLl4GS\n"
+ "BZPXMpUdng8AAACAeK6A0AX4H/AKEgCQG+8vDlb8beXE+4qmkTEF9l4LVtPTbvUW\n"
+ "1CFUu59psN4qmRtMZxt4r9aC/YMDVzC5X/oC5rKlN+/PI2hGfSIWfFXYBwKB5ulF\n"
+ "jO29rgA5oYqyOnUWVFTvv4jBlLw8WuujiOIQ1dOeaW8EIbcbqVGrU4cg8yIAAAHY\n"
+ "tbI937WyPd8AAAAHc3NoLWRzcwAAAIEAlMr1TxJJ91J2Q48ws0s0h6U5bc69BHPt\n"
+ "Zs/yQAoxzASfQv1vz8Lx98o5H6BYYURL6GOyD58ALuW3McPE5CEqu2ETkONPT6sM\n"
+ "D78tDdxlKf/pCktuvASCflSjedd10X+3i9XuGh1CfmpfcdScU5nOk9BgatZcrvgk\n"
+ "1FModuWjQxcAAAAVAK84j20GmU+zewjQwsIXuVb6C/PHAAAAgF4biFbCcVECeZ1k\n"
+ "ES3+6N1xBkPhHFZhzszAdcQrB42J6RIYXKPq/xw2Ug99ZMw3gGVMnM99Vqhi/Ml6\n"
+ "/jmUS6hLBgu3cBAMz4lw7pdMx3JdMoij60ay4DSR3inE+iX5QZ4Cu40ESFVINxd9\n"
+ "dOH21Wtq8Gp4i5eBkgWT1zKVHZ4PAAAAgHiugNAF+B/wChIAkBvvLw5W/G3lxPuK\n"
+ "ppExBfZeC1bT0271FtQhVLufabDeKpkbTGcbeK/Wgv2DA1cwuV/6AuaypTfvzyNo\n"
+ "Rn0iFnxV2AcCgebpRYztva4AOaGKsjp1FlRU77+IwZS8PFrro4jiENXTnmlvBCG3\n"
+ "G6lRq1OHIPMiAAAAFQCNR3fP4j87IO2086Db40C/jaMosgAAAAABAg==\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_dsa_public_testkey[] =
- "ssh-dss AAAAB3NzaC1kc3MAAACBAJTK9U8SSfdSdkOPMLNLNIelOW3OvQRz7WbP8k"
- "AKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fAC7ltzHDxOQhKrthE5DjT0+rDA+/LQ3c"
- "ZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5qX3HUnFOZzpPQYGrWXK74JNRTKHblo0"
- "MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzxwAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD"
- "4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWTMN4BlTJzPfVaoYvzJev45lEuoSwYLt3"
- "AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+UGeAruNBEhVSDcXfXTh9tVravBqeIuX"
- "gZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9R"
- "bUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM"
- "7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIg==\n";
+ "ssh-dss AAAAB3NzaC1kc3MAAACBAJTK9U8SSfdSdkOPMLNLNIelOW3OvQRz7WbP8k"
+ "AKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fAC7ltzHDxOQhKrthE5DjT0+rDA+/LQ3c"
+ "ZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5qX3HUnFOZzpPQYGrWXK74JNRTKHblo0"
+ "MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzxwAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD"
+ "4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWTMN4BlTJzPfVaoYvzJev45lEuoSwYLt3"
+ "AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+UGeAruNBEhVSDcXfXTh9tVravBqeIuX"
+ "gZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9R"
+ "bUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM"
+ "7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIg==\n";
static const char torture_dsa_testkey_cert[] =
- "ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNza"
- "C5jb20AAAAgKAd9MpIBrzctQyJvCYYJ2WUD5fyWlXMSv1G/3VihbCAAAACBAJTK9U8"
- "SSfdSdkOPMLNLNIelOW3OvQRz7WbP8kAKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fA"
- "C7ltzHDxOQhKrthE5DjT0+rDA+/LQ3cZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5"
- "qX3HUnFOZzpPQYGrWXK74JNRTKHblo0MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzx"
- "wAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWT"
- "MN4BlTJzPfVaoYvzJev45lEuoSwYLt3AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+"
- "UGeAruNBEhVSDcXfXTh9tVravBqeIuXgZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJA"
- "b7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+"
- "gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hD"
- "V055pbwQhtxupUatThyDzIgAAAAAAAAAAAAAAAQAAAA5saWJzc2hfdG9ydHVyZQAAA"
- "AAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5"
- "nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvc"
- "nQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXI"
- "tcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKKMHL9hp/LTng8sP"
- "5YHTKaAbC7R003VVTjqOR8DBb4/5RZG2ZW+idp/RiBOuBzTxOOb0pyHSWcM+FGH077"
- "ZzSjR6dTIDK4ytWehI8mbBqg2fgYZr3CgOiHJJIQSZw1e1PXTK6LwFoWutWfRqQ6gd"
- "Z2kX28KiUMEkRxdae9Qp39H2SsToQFcdKKYZmBs98d6quD3jmb92JdAB01XUZqpnhC"
- "w1MXrlzdm4Esm/PNcxIHWihx+aFskspD+LsI5+k+xE7i9jLA3Rtx2Rw4Zy8y5n8zmf"
- "3DWnTAq4GrjJbkCnOXWBdk0ng2lMcLp/zTzEAVWXRExywDAYJBpoUVJLn5VQyEAAAE"
- "PAAAAB3NzaC1yc2EAAAEAAt4V9aGqeahOfUvhG7M8/Mn26aLB/HXbICYFJF7dY6urm"
- "SIoS2KBqISCFGXTituiwGlZeAJ+pVgCMYo07Nxtd6oqIjsgKfJqDNx7e4pGw/YJnkm"
- "BqMO/k/ygu2mLmQF0lnpmG2KyjKEljMibHaKlFkcVNbwfOb4p8N3OHm66g5mbCUTRZ"
- "DHqMSJb3YtnObLexD13RydwxkG5AfCnOWxy5O4agXGEYwr/48AQBHYg9obGtpD1qyF"
- "4mMXgzaLViFtcwah6wHGlW0UPQMvrq/RqigAkyUszSccfibkIXJ+wGAgsRYhVAMwME"
- "JqPZ6GHOEIjLBKUegsclHb7Pk0YO8Auaw== "
- "aris@aris-air\n";
+ "ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNza"
+ "C5jb20AAAAgKAd9MpIBrzctQyJvCYYJ2WUD5fyWlXMSv1G/3VihbCAAAACBAJTK9U8"
+ "SSfdSdkOPMLNLNIelOW3OvQRz7WbP8kAKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fA"
+ "C7ltzHDxOQhKrthE5DjT0+rDA+/LQ3cZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5"
+ "qX3HUnFOZzpPQYGrWXK74JNRTKHblo0MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzx"
+ "wAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWT"
+ "MN4BlTJzPfVaoYvzJev45lEuoSwYLt3AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+"
+ "UGeAruNBEhVSDcXfXTh9tVravBqeIuXgZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJA"
+ "b7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+"
+ "gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hD"
+ "V055pbwQhtxupUatThyDzIgAAAAAAAAAAAAAAAQAAAA5saWJzc2hfdG9ydHVyZQAAA"
+ "AAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5"
+ "nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvc"
+ "nQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXI"
+ "tcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKKMHL9hp/LTng8sP"
+ "5YHTKaAbC7R003VVTjqOR8DBb4/5RZG2ZW+idp/RiBOuBzTxOOb0pyHSWcM+FGH077"
+ "ZzSjR6dTIDK4ytWehI8mbBqg2fgYZr3CgOiHJJIQSZw1e1PXTK6LwFoWutWfRqQ6gd"
+ "Z2kX28KiUMEkRxdae9Qp39H2SsToQFcdKKYZmBs98d6quD3jmb92JdAB01XUZqpnhC"
+ "w1MXrlzdm4Esm/PNcxIHWihx+aFskspD+LsI5+k+xE7i9jLA3Rtx2Rw4Zy8y5n8zmf"
+ "3DWnTAq4GrjJbkCnOXWBdk0ng2lMcLp/zTzEAVWXRExywDAYJBpoUVJLn5VQyEAAAE"
+ "PAAAAB3NzaC1yc2EAAAEAAt4V9aGqeahOfUvhG7M8/Mn26aLB/HXbICYFJF7dY6urm"
+ "SIoS2KBqISCFGXTituiwGlZeAJ+pVgCMYo07Nxtd6oqIjsgKfJqDNx7e4pGw/YJnkm"
+ "BqMO/k/ygu2mLmQF0lnpmG2KyjKEljMibHaKlFkcVNbwfOb4p8N3OHm66g5mbCUTRZ"
+ "DHqMSJb3YtnObLexD13RydwxkG5AfCnOWxy5O4agXGEYwr/48AQBHYg9obGtpD1qyF"
+ "4mMXgzaLViFtcwah6wHGlW0UPQMvrq/RqigAkyUszSccfibkIXJ+wGAgsRYhVAMwME"
+ "JqPZ6GHOEIjLBKUegsclHb7Pk0YO8Auaw== "
+ "aris@aris-air\n";
/****************************************************************************
* ECDSA KEYS
****************************************************************************/
static const char torture_ecdsa256_private_testkey[] =
- "-----BEGIN EC PRIVATE KEY-----\n"
- "MHcCAQEEIBCDeeYYAtX3EnsP0ratwVpNTaA/4K1N6VvHMiUZlVdhoAoGCCqGSM49\n"
- "AwEHoUQDQgAEx+9ud88Q5GWtLd+yMtYaapC85g+2ZLp7VtFHA0EbNHqBUQxoh+Ik\n"
- "89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQ==\n"
- "-----END EC PRIVATE KEY-----\n";
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "MHcCAQEEIBCDeeYYAtX3EnsP0ratwVpNTaA/4K1N6VvHMiUZlVdhoAoGCCqGSM49\n"
+ "AwEHoUQDQgAEx+9ud88Q5GWtLd+yMtYaapC85g+2ZLp7VtFHA0EbNHqBUQxoh+Ik\n"
+ "89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQ==\n"
+ "-----END EC PRIVATE KEY-----\n";
+
+static const char torture_ecdsa256_private_pkcs8_testkey[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEIN55hgC1fcSew/S\n"
+ "tq3BWk1NoD/grU3pW8cyJRmVV2GhRANCAATH7253zxDkZa0t37Iy1hpqkLzmD7Zk\n"
+ "untW0UcDQRs0eoFRDGiH4iTz0yWvsBTFwU936QKj40TrKr+Y1BwvsTqJ\n"
+ "-----END PRIVATE KEY-----\n";
static const char torture_ecdsa256_private_testkey_passphrase[] =
- "-----BEGIN EC PRIVATE KEY-----\n"
- "Proc-Type: 4,ENCRYPTED\n"
- "DEK-Info: AES-128-CBC,5C825E6FE821D0DE99D8403F4B4020CB\n"
- "\n"
- "TaUq8Qenb52dKAYcQGIYfdT7Z2DroySk38w51kw/gd8o79ZHaAQv60GtaNoy0203\n"
- "2X1o29E6c0WsY9DKhSHKm/zzvZmL+ChZYqqh3sd1gp55aJsHNN4axiIu2YCbCavh\n"
- "8VZn2VJDaitLy8ARqA/lMGQfqHSa3EOqti9FzWG/P6s=\n"
- "-----END EC PRIVATE KEY-----\n";
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "Proc-Type: 4,ENCRYPTED\n"
+ "DEK-Info: AES-128-CBC,5C825E6FE821D0DE99D8403F4B4020CB\n"
+ "\n"
+ "TaUq8Qenb52dKAYcQGIYfdT7Z2DroySk38w51kw/gd8o79ZHaAQv60GtaNoy0203\n"
+ "2X1o29E6c0WsY9DKhSHKm/zzvZmL+ChZYqqh3sd1gp55aJsHNN4axiIu2YCbCavh\n"
+ "8VZn2VJDaitLy8ARqA/lMGQfqHSa3EOqti9FzWG/P6s=\n"
+ "-----END EC PRIVATE KEY-----\n";
static const char torture_ecdsa256_private_pkcs8_testkey_passphrase[] =
- "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
- "MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhvndbkbElTnAICCAAw\n"
- "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEOu4ierPcQpcA9RJNHUbTCoEgZBe\n"
- "iusOkUYp4JZJEIpi98VlqnROzDXHpTTpEGiUDC/k+cuKvoPop5+Jx0qXp+A1NJxu\n"
- "kx3j+U0ISGY7J6b2Pqt1msC/FzqpeFM7ybuHDRz+c5ZBONTp8wrs52d5NdjrYguz\n"
- "UO6n9+yydSsO0FqbwPaqNZ6goBN0TfhYnToG4ZPJxlHa7gf7Su4KSMYKZdOtfx4=\n"
- "-----END ENCRYPTED PRIVATE KEY-----\n";
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
+ "MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhvndbkbElTnAICCAAw\n"
+ "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEOu4ierPcQpcA9RJNHUbTCoEgZBe\n"
+ "iusOkUYp4JZJEIpi98VlqnROzDXHpTTpEGiUDC/k+cuKvoPop5+Jx0qXp+A1NJxu\n"
+ "kx3j+U0ISGY7J6b2Pqt1msC/FzqpeFM7ybuHDRz+c5ZBONTp8wrs52d5NdjrYguz\n"
+ "UO6n9+yydSsO0FqbwPaqNZ6goBN0TfhYnToG4ZPJxlHa7gf7Su4KSMYKZdOtfx4=\n"
+ "-----END ENCRYPTED PRIVATE KEY-----\n";
static const char torture_ecdsa256_private_openssh_testkey[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNl\n"
- "Y2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTH7253zxDkZa0t37Iy\n"
- "1hpqkLzmD7ZkuntW0UcDQRs0eoFRDGiH4iTz0yWvsBTFwU936QKj40TrKr+Y1Bwv\n"
- "sTqJAAAAmOuDchHrg3IRAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy\n"
- "NTYAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNBGzR6gVEMaIfiJPPT\n"
- "Ja+wFMXBT3fpAqPjROsqv5jUHC+xOokAAAAgEIN55hgC1fcSew/Stq3BWk1NoD/g\n"
- "rU3pW8cyJRmVV2EAAAAA\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNl\n"
+ "Y2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTH7253zxDkZa0t37Iy\n"
+ "1hpqkLzmD7ZkuntW0UcDQRs0eoFRDGiH4iTz0yWvsBTFwU936QKj40TrKr+Y1Bwv\n"
+ "sTqJAAAAmOuDchHrg3IRAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy\n"
+ "NTYAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNBGzR6gVEMaIfiJPPT\n"
+ "Ja+wFMXBT3fpAqPjROsqv5jUHC+xOokAAAAgEIN55hgC1fcSew/Stq3BWk1NoD/g\n"
+ "rU3pW8cyJRmVV2EAAAAA\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa256_private_openssh_testkey_pasphrase[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABA+\n"
- "O0w3yPZF2q0FjVBhQjn2AAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAy\n"
- "NTYAAAAIbmlzdHAyNTYAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNB\n"
- "GzR6gVEMaIfiJPPTJa+wFMXBT3fpAqPjROsqv5jUHC+xOokAAACghvb4EX8M06UB\n"
- "zigxOn9bg5cZkZ2yWY8jzxtOWH4YJXsuhON/jePDJuI2ro5u4iKFD1u2JLfcshdh\n"
- "vKZyjixU9KdewykQQt/wFkrCfNUyCH8jFiQsAqhBfopRFyDJV9pmcUBL/3fJqwut\n"
- "ZeBSfA7tXORp3xrwFI1tXiiUCM+/nhxiCsFaCJXeiM3tN+kFtwQ8kamINqwaC8Vj\n"
- "lFLKHDfwJQ==\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABA+\n"
+ "O0w3yPZF2q0FjVBhQjn2AAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAy\n"
+ "NTYAAAAIbmlzdHAyNTYAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNB\n"
+ "GzR6gVEMaIfiJPPTJa+wFMXBT3fpAqPjROsqv5jUHC+xOokAAACghvb4EX8M06UB\n"
+ "zigxOn9bg5cZkZ2yWY8jzxtOWH4YJXsuhON/jePDJuI2ro5u4iKFD1u2JLfcshdh\n"
+ "vKZyjixU9KdewykQQt/wFkrCfNUyCH8jFiQsAqhBfopRFyDJV9pmcUBL/3fJqwut\n"
+ "ZeBSfA7tXORp3xrwFI1tXiiUCM+/nhxiCsFaCJXeiM3tN+kFtwQ8kamINqwaC8Vj\n"
+ "lFLKHDfwJQ==\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa256_public_testkey[] =
- "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNT"
- "YAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNBGzR6gVEMaIfiJPPTJa+w"
- "FMXBT3fpAqPjROsqv5jUHC+xOok= aris@kalix86\n";
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNT"
+ "YAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNBGzR6gVEMaIfiJPPTJa+w"
+ "FMXBT3fpAqPjROsqv5jUHC+xOok= aris@kalix86\n";
static const char torture_ecdsa256_public_testkey_pem[] =
- "-----BEGIN PUBLIC KEY-----\n"
- "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+9ud88Q5GWtLd+yMtYaapC85g+2\n"
- "ZLp7VtFHA0EbNHqBUQxoh+Ik89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQ==\n"
- "-----END PUBLIC KEY-----\n";
+ "-----BEGIN PUBLIC KEY-----\n"
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+9ud88Q5GWtLd+yMtYaapC85g+2\n"
+ "ZLp7VtFHA0EbNHqBUQxoh+Ik89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQ==\n"
+ "-----END PUBLIC KEY-----\n";
static const char torture_ecdsa256_testkey_cert[] =
- "ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzd"
- "HAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgHvXWcdSrQeZL2/Z68V8ntbL7rDo"
- "Qwrsc+ps6HbMGZrkAAAAIbmlzdHAyNTYAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPt"
- "mS6e1bRRwNBGzR6gVEMaIfiJPPTJa+wFMXBT3fpAqPjROsqv5jUHC+xOokAAAAAAAA"
- "AAAAAAAEAAAAHbXlpZGVudAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVc"
- "GVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGl"
- "uZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0e"
- "QAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAGgAAAATZWNkc2Etc2hhMi1"
- "uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEEx+9ud88Q5GWtLd+yMtYaapC85g+2ZLp7V"
- "tFHA0EbNHqBUQxoh+Ik89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQAAAGQAAAATZWN"
- "kc2Etc2hhMi1uaXN0cDI1NgAAAEkAAAAhALDSBnmFF59tgTKDQ4meTJEI7/BP2Zgf1"
- "AKg1H3kIijQAAAAIFYrqSg6GI03ohXqUVsZ3lCB/XIism2aV5Vz2bg1d9zo "
- "./ec256.pub";
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzd"
+ "HAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgHvXWcdSrQeZL2/Z68V8ntbL7rDo"
+ "Qwrsc+ps6HbMGZrkAAAAIbmlzdHAyNTYAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPt"
+ "mS6e1bRRwNBGzR6gVEMaIfiJPPTJa+wFMXBT3fpAqPjROsqv5jUHC+xOokAAAAAAAA"
+ "AAAAAAAEAAAAHbXlpZGVudAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVc"
+ "GVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGl"
+ "uZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0e"
+ "QAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAGgAAAATZWNkc2Etc2hhMi1"
+ "uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEEx+9ud88Q5GWtLd+yMtYaapC85g+2ZLp7V"
+ "tFHA0EbNHqBUQxoh+Ik89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQAAAGQAAAATZWN"
+ "kc2Etc2hhMi1uaXN0cDI1NgAAAEkAAAAhALDSBnmFF59tgTKDQ4meTJEI7/BP2Zgf1"
+ "AKg1H3kIijQAAAAIFYrqSg6GI03ohXqUVsZ3lCB/XIism2aV5Vz2bg1d9zo "
+ "./ec256.pub";
static const char torture_ecdsa384_private_testkey[] =
- "-----BEGIN EC PRIVATE KEY-----\n"
- "MIGkAgEBBDBY8jEa5DtRy4AVeTWhPJ/TK257behiC3uafEi6YA2oHORibqX55EDN\n"
- "wz29MT40mQSgBwYFK4EEACKhZANiAARXc4BN6BrVo1QMi3+i/B85Lu7SMuzBi+1P\n"
- "bJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdztJ0bYoyT4V3B3ZqR9RyGq6mYC\n"
- "jkXlc5YbYHjueBbp0oeNXqsXHNAWQZo=\n"
- "-----END EC PRIVATE KEY-----\n";
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "MIGkAgEBBDBY8jEa5DtRy4AVeTWhPJ/TK257behiC3uafEi6YA2oHORibqX55EDN\n"
+ "wz29MT40mQSgBwYFK4EEACKhZANiAARXc4BN6BrVo1QMi3+i/B85Lu7SMuzBi+1P\n"
+ "bJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdztJ0bYoyT4V3B3ZqR9RyGq6mYC\n"
+ "jkXlc5YbYHjueBbp0oeNXqsXHNAWQZo=\n"
+ "-----END EC PRIVATE KEY-----\n";
+
+static const char torture_ecdsa384_private_pkcs8_testkey[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBY8jEa5DtRy4AVeTWh\n"
+ "PJ/TK257behiC3uafEi6YA2oHORibqX55EDNwz29MT40mQShZANiAARXc4BN6BrV\n"
+ "o1QMi3+i/B85Lu7SMuzBi+1PbJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdzt\n"
+ "J0bYoyT4V3B3ZqR9RyGq6mYCjkXlc5YbYHjueBbp0oeNXqsXHNAWQZo=\n"
+ "-----END PRIVATE KEY-----\n";
static const char torture_ecdsa384_private_testkey_passphrase[] =
- "-----BEGIN EC PRIVATE KEY-----\n"
- "Proc-Type: 4,ENCRYPTED\n"
- "DEK-Info: AES-128-CBC,5C825E6FE821D0DE99D8403F4B4020CB\n"
- "\n"
- "TaUq8Qenb52dKAYcQGIYfdT7Z2DroySk38w51kw/gd8o79ZHaAQv60GtaNoy0203\n"
- "2X1o29E6c0WsY9DKhSHKm/zzvZmL+ChZYqqh3sd1gp55aJsHNN4axiIu2YCbCavh\n"
- "8VZn2VJDaitLy8ARqA/lMGQfqHSa3EOqti9FzWG/P6s=\n"
- "-----END EC PRIVATE KEY-----\n";
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "Proc-Type: 4,ENCRYPTED\n"
+ "DEK-Info: AES-128-CBC,5C825E6FE821D0DE99D8403F4B4020CB\n"
+ "\n"
+ "TaUq8Qenb52dKAYcQGIYfdT7Z2DroySk38w51kw/gd8o79ZHaAQv60GtaNoy0203\n"
+ "2X1o29E6c0WsY9DKhSHKm/zzvZmL+ChZYqqh3sd1gp55aJsHNN4axiIu2YCbCavh\n"
+ "8VZn2VJDaitLy8ARqA/lMGQfqHSa3EOqti9FzWG/P6s=\n"
+ "-----END EC PRIVATE KEY-----\n";
static const char torture_ecdsa384_private_pkcs8_testkey_passphrase[] =
- "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
- "MIIBHDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIEuMnFkuHkDkCAggA\n"
- "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBA/fjhqXxV/Dk7cg8XgPxzuBIHA\n"
- "TbiloDCPfKKlkm9ZguahtfJOxcVBbMtrFAK2vA/jMXGnbB9Qe13uLl8fTd6QB4tE\n"
- "Zbyucq4OA0L2HyhuEsJiLvf0ICX8APrBajNv3B8F7ZStrXx7hcJUg8qTlsbdovYq\n"
- "nCjOKoq/F6ax/r1F9Rr5PlXQDoSKDJ3mQkZc4n8VNKFfXOPQ7C4rEYzglSyzGwyQ\n"
- "2EwRwnkkJqcYotRyH4JWtXCRak7znLVDeGbavhpP6paSVsK8OpycAoJstfQb0L4q\n"
- "-----END ENCRYPTED PRIVATE KEY-----\n";
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
+ "MIIBHDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIEuMnFkuHkDkCAggA\n"
+ "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBA/fjhqXxV/Dk7cg8XgPxzuBIHA\n"
+ "TbiloDCPfKKlkm9ZguahtfJOxcVBbMtrFAK2vA/jMXGnbB9Qe13uLl8fTd6QB4tE\n"
+ "Zbyucq4OA0L2HyhuEsJiLvf0ICX8APrBajNv3B8F7ZStrXx7hcJUg8qTlsbdovYq\n"
+ "nCjOKoq/F6ax/r1F9Rr5PlXQDoSKDJ3mQkZc4n8VNKFfXOPQ7C4rEYzglSyzGwyQ\n"
+ "2EwRwnkkJqcYotRyH4JWtXCRak7znLVDeGbavhpP6paSVsK8OpycAoJstfQb0L4q\n"
+ "-----END ENCRYPTED PRIVATE KEY-----\n";
static const char torture_ecdsa384_private_openssh_testkey[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNl\n"
- "Y2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQRXc4BN6BrVo1QMi3+i\n"
- "/B85Lu7SMuzBi+1PbJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdztJ0bYoyT4\n"
- "V3B3ZqR9RyGq6mYCjkXlc5YbYHjueBbp0oeNXqsXHNAWQZoAAADIITfDfiE3w34A\n"
- "AAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAAhuaXN0cDM4NAAAAGEEV3OATega1aNU\n"
- "DIt/ovwfOS7u0jLswYvtT2ybYvMc/ks4KuuIGgRjivaPljnSwHf8O6ewyS3c7SdG\n"
- "2KMk+Fdwd2akfUchqupmAo5F5XOWG2B47ngW6dKHjV6rFxzQFkGaAAAAMFjyMRrk\n"
- "O1HLgBV5NaE8n9Mrbntt6GILe5p8SLpgDagc5GJupfnkQM3DPb0xPjSZBAAAAAA=\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNl\n"
+ "Y2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQRXc4BN6BrVo1QMi3+i\n"
+ "/B85Lu7SMuzBi+1PbJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdztJ0bYoyT4\n"
+ "V3B3ZqR9RyGq6mYCjkXlc5YbYHjueBbp0oeNXqsXHNAWQZoAAADIITfDfiE3w34A\n"
+ "AAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAAhuaXN0cDM4NAAAAGEEV3OATega1aNU\n"
+ "DIt/ovwfOS7u0jLswYvtT2ybYvMc/ks4KuuIGgRjivaPljnSwHf8O6ewyS3c7SdG\n"
+ "2KMk+Fdwd2akfUchqupmAo5F5XOWG2B47ngW6dKHjV6rFxzQFkGaAAAAMFjyMRrk\n"
+ "O1HLgBV5NaE8n9Mrbntt6GILe5p8SLpgDagc5GJupfnkQM3DPb0xPjSZBAAAAAA=\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa384_private_openssh_testkey_passphrase[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB4N\n"
- "dKGEoxFeg6dqiR2vTl6AAAAEAAAAAEAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzOD\n"
- "QAAAAIbmlzdHAzODQAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7MGL7U9sm2LzHP5\n"
- "LOCrriBoEY4r2j5Y50sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIarqZgKOReVzlhtg\n"
- "eO54FunSh41eqxcc0BZBmgAAANDOL7sWcylFf8SsjGVFvr36mpyUBpAJ/e7o4RbQg\n"
- "H8FDu1IxscOfbLDoB3CV7UEIgG58nVsDamfL6rXV/tzWnPxYxi6jUHcKT1BugO/Jt\n"
- "/ncelMeoAS6MAZhElaGKzU1cJMlMTV9ofmuKuAwllQULG7L8lwHs9whBK4JmWPaGL\n"
- "pU3i9ZoT33/g6pcvA83vicCNqj7ggl6Vb9MeO/zGW1+oV2HC3WiLTqBsYxEJu4YCM\n"
- "ewfx9pWeWaCllNy/F1rCBu3cxqzcge9hqIlNtpT7Dq3k\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB4N\n"
+ "dKGEoxFeg6dqiR2vTl6AAAAEAAAAAEAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzOD\n"
+ "QAAAAIbmlzdHAzODQAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7MGL7U9sm2LzHP5\n"
+ "LOCrriBoEY4r2j5Y50sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIarqZgKOReVzlhtg\n"
+ "eO54FunSh41eqxcc0BZBmgAAANDOL7sWcylFf8SsjGVFvr36mpyUBpAJ/e7o4RbQg\n"
+ "H8FDu1IxscOfbLDoB3CV7UEIgG58nVsDamfL6rXV/tzWnPxYxi6jUHcKT1BugO/Jt\n"
+ "/ncelMeoAS6MAZhElaGKzU1cJMlMTV9ofmuKuAwllQULG7L8lwHs9whBK4JmWPaGL\n"
+ "pU3i9ZoT33/g6pcvA83vicCNqj7ggl6Vb9MeO/zGW1+oV2HC3WiLTqBsYxEJu4YCM\n"
+ "ewfx9pWeWaCllNy/F1rCBu3cxqzcge9hqIlNtpT7Dq3k\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa384_public_testkey[] =
- "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD"
- "QAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7MGL7U9sm2LzHP5LOCrriBoEY4r2j5Y5"
- "0sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIarqZgKOReVzlhtgeO54FunSh41eqxcc0B"
- "ZBmg== aris@kalix86";
+ "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD"
+ "QAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7MGL7U9sm2LzHP5LOCrriBoEY4r2j5Y5"
+ "0sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIarqZgKOReVzlhtgeO54FunSh41eqxcc0B"
+ "ZBmg== aris@kalix86";
static const char torture_ecdsa384_public_testkey_pem[] =
- "-----BEGIN PUBLIC KEY-----\n"
- "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEV3OATega1aNUDIt/ovwfOS7u0jLswYvt\n"
- "T2ybYvMc/ks4KuuIGgRjivaPljnSwHf8O6ewyS3c7SdG2KMk+Fdwd2akfUchqupm\n"
- "Ao5F5XOWG2B47ngW6dKHjV6rFxzQFkGa\n"
- "-----END PUBLIC KEY-----\n";
+ "-----BEGIN PUBLIC KEY-----\n"
+ "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEV3OATega1aNUDIt/ovwfOS7u0jLswYvt\n"
+ "T2ybYvMc/ks4KuuIGgRjivaPljnSwHf8O6ewyS3c7SdG2KMk+Fdwd2akfUchqupm\n"
+ "Ao5F5XOWG2B47ngW6dKHjV6rFxzQFkGa\n"
+ "-----END PUBLIC KEY-----\n";
static const char torture_ecdsa384_testkey_cert[] =
- "ecdsa-sha2-nistp384-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzd"
- "HAzODQtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgvggfi3v98HjOiqVi1O5aPy7JvMd"
- "rTZe68GZ0qCaAN5MAAAAIbmlzdHAzODQAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7"
- "MGL7U9sm2LzHP5LOCrriBoEY4r2j5Y50sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIar"
- "qZgKOReVzlhtgeO54FunSh41eqxcc0BZBmgAAAAAAAAAAAAAAAQAAAAdteWlkZW50A"
- "AAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmR"
- "pbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtc"
- "G9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXN"
- "lci1yYwAAAAAAAAAAAAAAiAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwM"
- "zg0AAAAYQRXc4BN6BrVo1QMi3+i/B85Lu7SMuzBi+1PbJti8xz+Szgq64gaBGOK9o+"
- "WOdLAd/w7p7DJLdztJ0bYoyT4V3B3ZqR9RyGq6mYCjkXlc5YbYHjueBbp0oeNXqsXH"
- "NAWQZoAAACEAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMQD5f0pF6U6eeBO"
- "PrOV7Y3w5NuTzvuyDAq0kTv6VYNMp83TYpIJw16+tMAplOSzPTvwAAAAwWD9StvMEP"
- "b+SDH2G5qqkMk+F5IaHI9fev8zcFzzdOlilLc/+CFM0NKMAFtOrrhv0 "
- "./ec384.pub";
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzd"
+ "HAzODQtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgvggfi3v98HjOiqVi1O5aPy7JvMd"
+ "rTZe68GZ0qCaAN5MAAAAIbmlzdHAzODQAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7"
+ "MGL7U9sm2LzHP5LOCrriBoEY4r2j5Y50sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIar"
+ "qZgKOReVzlhtgeO54FunSh41eqxcc0BZBmgAAAAAAAAAAAAAAAQAAAAdteWlkZW50A"
+ "AAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmR"
+ "pbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtc"
+ "G9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXN"
+ "lci1yYwAAAAAAAAAAAAAAiAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwM"
+ "zg0AAAAYQRXc4BN6BrVo1QMi3+i/B85Lu7SMuzBi+1PbJti8xz+Szgq64gaBGOK9o+"
+ "WOdLAd/w7p7DJLdztJ0bYoyT4V3B3ZqR9RyGq6mYCjkXlc5YbYHjueBbp0oeNXqsXH"
+ "NAWQZoAAACEAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMQD5f0pF6U6eeBO"
+ "PrOV7Y3w5NuTzvuyDAq0kTv6VYNMp83TYpIJw16+tMAplOSzPTvwAAAAwWD9StvMEP"
+ "b+SDH2G5qqkMk+F5IaHI9fev8zcFzzdOlilLc/+CFM0NKMAFtOrrhv0 "
+ "./ec384.pub";
static const char torture_ecdsa521_private_testkey[] =
- "-----BEGIN EC PRIVATE KEY-----\n"
- "MIHbAgEBBEG83nSJ2SLoiBvEku1JteQKWx/Xt6THksgC7rrIaTUmNzk+60f0sCCm\n"
- "Gll0dgrZLmeIw+TtnG1E20VZflCKq+IdkaAHBgUrgQQAI6GBiQOBhgAEAc6D728d\n"
- "baQkHnSPtztaRwJw63CBl15cykB4SXXuwWdNOtPzBijUULMTTvBXbra8gL4ATd9d\n"
- "Qnuwn8KQUh2T/z+BARjWPKhcHcGx57XpXCEkawzMYaHUUnRdeFEmNRsbXypsf0mJ\n"
- "KATU3h8gzTMkbrx8DJTFHEIjXBShs44HsSYVl3Xy\n"
- "-----END EC PRIVATE KEY-----\n";
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "MIHbAgEBBEG83nSJ2SLoiBvEku1JteQKWx/Xt6THksgC7rrIaTUmNzk+60f0sCCm\n"
+ "Gll0dgrZLmeIw+TtnG1E20VZflCKq+IdkaAHBgUrgQQAI6GBiQOBhgAEAc6D728d\n"
+ "baQkHnSPtztaRwJw63CBl15cykB4SXXuwWdNOtPzBijUULMTTvBXbra8gL4ATd9d\n"
+ "Qnuwn8KQUh2T/z+BARjWPKhcHcGx57XpXCEkawzMYaHUUnRdeFEmNRsbXypsf0mJ\n"
+ "KATU3h8gzTMkbrx8DJTFHEIjXBShs44HsSYVl3Xy\n"
+ "-----END EC PRIVATE KEY-----\n";
+
+static const char torture_ecdsa521_private_pkcs8_testkey[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAvN50idki6IgbxJLt\n"
+ "SbXkClsf17ekx5LIAu66yGk1Jjc5PutH9LAgphpZdHYK2S5niMPk7ZxtRNtFWX5Q\n"
+ "iqviHZGhgYkDgYYABAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl17sFnTTrT\n"
+ "8wYo1FCzE07wV262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee16VwhJGsM\n"
+ "zGGh1FJ0XXhRJjUbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOOB7EmFZd1\n"
+ "8g==\n"
+ "-----END PRIVATE KEY-----\n";
static const char torture_ecdsa521_private_testkey_passphrase[] =
- "-----BEGIN EC PRIVATE KEY-----\n"
- "Proc-Type: 4,ENCRYPTED\n"
- "DEK-Info: AES-128-CBC,24C4F383915BC07D9C63209BF6AD3DEE\n"
- "\n"
- "M+JGfpGfoH3Wn6XWSoHrGGevaS6p2vJGQdkFEIgUfh16s+U/LcRhAhRnhX/MV6Ds\n"
- "OZTpusrjInlZXNUR97fJbmjr/600qUlh4y3U9ikiX3IXE+RI80TPNdishOOjKRF7\n"
- "aWDW8UxTlFfU2Zc1Ew0pTvMXXcuTpozW1NNVY+6S9uWfHwq1/EcR35dbnEmG0gId\n"
- "qsiEdVKh7p+9Qto8jcVWzMh7ANMcIwmxQ4zbvnqypwgAgpMbamWqBZ9q4egsVZKd\n"
- "uRzL95L05ctOBGYNYqpPNIX3UdQU07kzwNC+yaHOb2s=\n"
- "-----END EC PRIVATE KEY-----\n";
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "Proc-Type: 4,ENCRYPTED\n"
+ "DEK-Info: AES-128-CBC,24C4F383915BC07D9C63209BF6AD3DEE\n"
+ "\n"
+ "M+JGfpGfoH3Wn6XWSoHrGGevaS6p2vJGQdkFEIgUfh16s+U/LcRhAhRnhX/MV6Ds\n"
+ "OZTpusrjInlZXNUR97fJbmjr/600qUlh4y3U9ikiX3IXE+RI80TPNdishOOjKRF7\n"
+ "aWDW8UxTlFfU2Zc1Ew0pTvMXXcuTpozW1NNVY+6S9uWfHwq1/EcR35dbnEmG0gId\n"
+ "qsiEdVKh7p+9Qto8jcVWzMh7ANMcIwmxQ4zbvnqypwgAgpMbamWqBZ9q4egsVZKd\n"
+ "uRzL95L05ctOBGYNYqpPNIX3UdQU07kzwNC+yaHOb2s=\n"
+ "-----END EC PRIVATE KEY-----\n";
static const char torture_ecdsa521_private_pkcs8_testkey_passphrase[] =
- "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
- "MIIBXTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIY6X14D05Q7gCAggA\n"
- "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCmngDUX2/kg+45m4qoCBLiBIIB\n"
- "ANHV+GC6Hnend9cVScT5oNtOS2a/TD82N1h+9cYmxn953IRNk2rF7LFYFFeZzcZi\n"
- "e840YFYFRiTScm1GbKgwyFLYzYguvpUpS3qz3yZMygoX3xlvFw0l8FWsfeUmOzG1\n"
- "uQQPGeoFCus43D3k1iQCOafEe0DPbyfcF/IxajZ+P0N8A5ikgPsOfpTLAdWiYgFt\n"
- "wkafVfXx5ZH1u8S34+kmoKRhf5zBFQI1BHD6bCQDANPBkbP4KEjH5mHRO99nHK9r\n"
- "EhdLDBEXRo9xb1BhgPLdQA0AdPPqZ6Wugy3KyxkEiH/GB/oBoIpg0oALnowL129g\n"
- "BV6jZHwXHuO4/CLJ9rN2tdE=\n"
- "-----END ENCRYPTED PRIVATE KEY-----\n";
+ "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
+ "MIIBXTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIY6X14D05Q7gCAggA\n"
+ "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCmngDUX2/kg+45m4qoCBLiBIIB\n"
+ "ANHV+GC6Hnend9cVScT5oNtOS2a/TD82N1h+9cYmxn953IRNk2rF7LFYFFeZzcZi\n"
+ "e840YFYFRiTScm1GbKgwyFLYzYguvpUpS3qz3yZMygoX3xlvFw0l8FWsfeUmOzG1\n"
+ "uQQPGeoFCus43D3k1iQCOafEe0DPbyfcF/IxajZ+P0N8A5ikgPsOfpTLAdWiYgFt\n"
+ "wkafVfXx5ZH1u8S34+kmoKRhf5zBFQI1BHD6bCQDANPBkbP4KEjH5mHRO99nHK9r\n"
+ "EhdLDBEXRo9xb1BhgPLdQA0AdPPqZ6Wugy3KyxkEiH/GB/oBoIpg0oALnowL129g\n"
+ "BV6jZHwXHuO4/CLJ9rN2tdE=\n"
+ "-----END ENCRYPTED PRIVATE KEY-----\n";
static const char torture_ecdsa521_private_openssh_testkey[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNl\n"
- "Y2RzYS1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBzoPvbx1tpCQedI+3\n"
- "O1pHAnDrcIGXXlzKQHhJde7BZ0060/MGKNRQsxNO8FdutryAvgBN311Ce7CfwpBS\n"
- "HZP/P4EBGNY8qFwdwbHntelcISRrDMxhodRSdF14USY1GxtfKmx/SYkoBNTeHyDN\n"
- "MyRuvHwMlMUcQiNcFKGzjgexJhWXdfIAAAEAt6sYz7erGM8AAAATZWNkc2Etc2hh\n"
- "Mi1uaXN0cDUyMQAAAAhuaXN0cDUyMQAAAIUEAc6D728dbaQkHnSPtztaRwJw63CB\n"
- "l15cykB4SXXuwWdNOtPzBijUULMTTvBXbra8gL4ATd9dQnuwn8KQUh2T/z+BARjW\n"
- "PKhcHcGx57XpXCEkawzMYaHUUnRdeFEmNRsbXypsf0mJKATU3h8gzTMkbrx8DJTF\n"
- "HEIjXBShs44HsSYVl3XyAAAAQgC83nSJ2SLoiBvEku1JteQKWx/Xt6THksgC7rrI\n"
- "aTUmNzk+60f0sCCmGll0dgrZLmeIw+TtnG1E20VZflCKq+IdkQAAAAABAg==\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNl\n"
+ "Y2RzYS1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBzoPvbx1tpCQedI+3\n"
+ "O1pHAnDrcIGXXlzKQHhJde7BZ0060/MGKNRQsxNO8FdutryAvgBN311Ce7CfwpBS\n"
+ "HZP/P4EBGNY8qFwdwbHntelcISRrDMxhodRSdF14USY1GxtfKmx/SYkoBNTeHyDN\n"
+ "MyRuvHwMlMUcQiNcFKGzjgexJhWXdfIAAAEAt6sYz7erGM8AAAATZWNkc2Etc2hh\n"
+ "Mi1uaXN0cDUyMQAAAAhuaXN0cDUyMQAAAIUEAc6D728dbaQkHnSPtztaRwJw63CB\n"
+ "l15cykB4SXXuwWdNOtPzBijUULMTTvBXbra8gL4ATd9dQnuwn8KQUh2T/z+BARjW\n"
+ "PKhcHcGx57XpXCEkawzMYaHUUnRdeFEmNRsbXypsf0mJKATU3h8gzTMkbrx8DJTF\n"
+ "HEIjXBShs44HsSYVl3XyAAAAQgC83nSJ2SLoiBvEku1JteQKWx/Xt6THksgC7rrI\n"
+ "aTUmNzk+60f0sCCmGll0dgrZLmeIw+TtnG1E20VZflCKq+IdkQAAAAABAg==\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa521_private_openssh_testkey_passphrase[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\n"
- "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAj\n"
- "9WBFa/piJcPFEE4CGZTKAAAAEAAAAAEAAACsAAAAE2VjZHNhLXNoYTItbmlzdHA1\n"
- "MjEAAAAIbmlzdHA1MjEAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl1\n"
- "7sFnTTrT8wYo1FCzE07wV262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee1\n"
- "6VwhJGsMzGGh1FJ0XXhRJjUbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOO\n"
- "B7EmFZd18gAAAQDLjaKp+DLEHFb98f5WnVFg6LgDN847sfeuPZVfVjeSAiIv016O\n"
- "ld7DXb137B2xYVsuce6sHbypr10dJOvgMTLdzTl+crYNJL+8UufJP0rOIFaDenzQ\n"
- "RW8wydwiQxwt1ZqtD8ASqFmadxngufJKZzPLGfjCbCz3uATKa2sXN66nRXRZJbVA\n"
- "IlNYDY8ivAStNhfItUMqyM6PkYlKJECtJw7w7TYKpvts7t72JmtgqVjS45JI/YZ+\n"
- "kitIG0YmG8rzL9d1vBB5m+MH/fnFz2uJqbQYCH9Ctc8HZodAVoTNDzXHU2mYF9PE\n"
- "Z6+gi3jd+kOyUk3NifHcre9K6ie7LL33JayM\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
-
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAj\n"
+ "9WBFa/piJcPFEE4CGZTKAAAAEAAAAAEAAACsAAAAE2VjZHNhLXNoYTItbmlzdHA1\n"
+ "MjEAAAAIbmlzdHA1MjEAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl1\n"
+ "7sFnTTrT8wYo1FCzE07wV262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee1\n"
+ "6VwhJGsMzGGh1FJ0XXhRJjUbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOO\n"
+ "B7EmFZd18gAAAQDLjaKp+DLEHFb98f5WnVFg6LgDN847sfeuPZVfVjeSAiIv016O\n"
+ "ld7DXb137B2xYVsuce6sHbypr10dJOvgMTLdzTl+crYNJL+8UufJP0rOIFaDenzQ\n"
+ "RW8wydwiQxwt1ZqtD8ASqFmadxngufJKZzPLGfjCbCz3uATKa2sXN66nRXRZJbVA\n"
+ "IlNYDY8ivAStNhfItUMqyM6PkYlKJECtJw7w7TYKpvts7t72JmtgqVjS45JI/YZ+\n"
+ "kitIG0YmG8rzL9d1vBB5m+MH/fnFz2uJqbQYCH9Ctc8HZodAVoTNDzXHU2mYF9PE\n"
+ "Z6+gi3jd+kOyUk3NifHcre9K6ie7LL33JayM\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ecdsa521_public_testkey[] =
- "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj"
- "EAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl17sFnTTrT8wYo1FCzE07w"
- "V262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee16VwhJGsMzGGh1FJ0XXhRJj"
- "UbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOOB7EmFZd18g== aris@kalix86";
+ "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj"
+ "EAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl17sFnTTrT8wYo1FCzE07w"
+ "V262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee16VwhJGsMzGGh1FJ0XXhRJj"
+ "UbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOOB7EmFZd18g== aris@kalix86";
static const char torture_ecdsa521_public_testkey_pem[] =
- "-----BEGIN PUBLIC KEY-----\n"
- "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBzoPvbx1tpCQedI+3O1pHAnDrcIGX\n"
- "XlzKQHhJde7BZ0060/MGKNRQsxNO8FdutryAvgBN311Ce7CfwpBSHZP/P4EBGNY8\n"
- "qFwdwbHntelcISRrDMxhodRSdF14USY1GxtfKmx/SYkoBNTeHyDNMyRuvHwMlMUc\n"
- "QiNcFKGzjgexJhWXdfI=\n"
- "-----END PUBLIC KEY-----\n";
+ "-----BEGIN PUBLIC KEY-----\n"
+ "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBzoPvbx1tpCQedI+3O1pHAnDrcIGX\n"
+ "XlzKQHhJde7BZ0060/MGKNRQsxNO8FdutryAvgBN311Ce7CfwpBSHZP/P4EBGNY8\n"
+ "qFwdwbHntelcISRrDMxhodRSdF14USY1GxtfKmx/SYkoBNTeHyDNMyRuvHwMlMUc\n"
+ "QiNcFKGzjgexJhWXdfI=\n"
+ "-----END PUBLIC KEY-----\n";
static const char torture_ecdsa521_testkey_cert[] =
- "ecdsa-sha2-nistp521-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzd"
- "HA1MjEtY2VydC12MDFAb3BlbnNzaC5jb20AAAAggFIwlsx63C++kmCBDF4O14fvu5j"
- "Icsm8uMbMp0smOVwAAAAIbmlzdHA1MjEAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwg"
- "ZdeXMpAeEl17sFnTTrT8wYo1FCzE07wV262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jy"
- "oXB3Bsee16VwhJGsMzGGh1FJ0XXhRJjUbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI"
- "1wUobOOB7EmFZd18gAAAAAAAAAAAAAAAQAAAAdteWlkZW50AAAAAAAAAAAAAAAA///"
- "///////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blc"
- "m1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5"
- "nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAA"
- "AAArAAAABNlY2RzYS1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBzoPvbx1"
- "tpCQedI+3O1pHAnDrcIGXXlzKQHhJde7BZ0060/MGKNRQsxNO8FdutryAvgBN311Ce"
- "7CfwpBSHZP/P4EBGNY8qFwdwbHntelcISRrDMxhodRSdF14USY1GxtfKmx/SYkoBNT"
- "eHyDNMyRuvHwMlMUcQiNcFKGzjgexJhWXdfIAAACnAAAAE2VjZHNhLXNoYTItbmlzd"
- "HA1MjEAAACMAAAAQgCJzTxw/hz2qE8Qkd4XW9Qn7fPxML6Ebtttg9C18AguyGyE6Nk"
- "YH1NcToYxwQxrgzDXowXYm9eCbq9JEvaXDEtIfAAAAEIBk06LmKAYR2HDwwt4f5wVI"
- "PKJ0pHVLZEx3FMZI3SfwS9mVm+oojLkZ2hr8X0xn28zbN045d8daB7BB1mHMGNT+YA"
- "= ./ec521.pub";
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzd"
+ "HA1MjEtY2VydC12MDFAb3BlbnNzaC5jb20AAAAggFIwlsx63C++kmCBDF4O14fvu5j"
+ "Icsm8uMbMp0smOVwAAAAIbmlzdHA1MjEAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwg"
+ "ZdeXMpAeEl17sFnTTrT8wYo1FCzE07wV262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jy"
+ "oXB3Bsee16VwhJGsMzGGh1FJ0XXhRJjUbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI"
+ "1wUobOOB7EmFZd18gAAAAAAAAAAAAAAAQAAAAdteWlkZW50AAAAAAAAAAAAAAAA///"
+ "///////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blc"
+ "m1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5"
+ "nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAA"
+ "AAArAAAABNlY2RzYS1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBzoPvbx1"
+ "tpCQedI+3O1pHAnDrcIGXXlzKQHhJde7BZ0060/MGKNRQsxNO8FdutryAvgBN311Ce"
+ "7CfwpBSHZP/P4EBGNY8qFwdwbHntelcISRrDMxhodRSdF14USY1GxtfKmx/SYkoBNT"
+ "eHyDNMyRuvHwMlMUcQiNcFKGzjgexJhWXdfIAAACnAAAAE2VjZHNhLXNoYTItbmlzd"
+ "HA1MjEAAACMAAAAQgCJzTxw/hz2qE8Qkd4XW9Qn7fPxML6Ebtttg9C18AguyGyE6Nk"
+ "YH1NcToYxwQxrgzDXowXYm9eCbq9JEvaXDEtIfAAAAEIBk06LmKAYR2HDwwt4f5wVI"
+ "PKJ0pHVLZEx3FMZI3SfwS9mVm+oojLkZ2hr8X0xn28zbN045d8daB7BB1mHMGNT+YA"
+ "= ./ec521.pub";
/****************************************************************************
* ED25519 KEYS
****************************************************************************/
static const char torture_ed25519_private_pkcs8_testkey[] =
- "-----BEGIN PRIVATE KEY-----\n"
- "MC4CAQAwBQYDK2VwBCIEIGBhcqLe61tkqVjIHKEzwB3oINasSHWGbIWXQWcLPmGN\n"
- "-----END PRIVATE KEY-----\n";
+ "-----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"
- "dQAAAAtzc2gtZWQyNTUxOQAAACAVlp8bgmIjsrzGC7ZIKBMhCpS1fpJTPgVOjYdz5gIqlw\n"
- "AAAEBgYXKi3utbZKlYyByhM8Ad6CDWrEh1hmyFl0FnCz5hjRWWnxuCYiOyvMYLtkgoEyEK\n"
- "lLV+klM+BU6Nh3PmAiqXAAAADGFyaXNAa2FsaXg4NgE=\n"
- "-----END OPENSSH PRIVATE KEY-----\n";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n"
+ "QyNTUxOQAAACAVlp8bgmIjsrzGC7ZIKBMhCpS1fpJTPgVOjYdz5gIqlwAAAJBzsDN1c7Az\n"
+ "dQAAAAtzc2gtZWQyNTUxOQAAACAVlp8bgmIjsrzGC7ZIKBMhCpS1fpJTPgVOjYdz5gIqlw\n"
+ "AAAEBgYXKi3utbZKlYyByhM8Ad6CDWrEh1hmyFl0FnCz5hjRWWnxuCYiOyvMYLtkgoEyEK\n"
+ "lLV+klM+BU6Nh3PmAiqXAAAADGFyaXNAa2FsaXg4NgE=\n"
+ "-----END OPENSSH PRIVATE KEY-----\n";
static const char torture_ed25519_private_openssh_testkey_passphrase[] =
- "-----BEGIN OPENSSH PRIVATE KEY-----\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";
+ "-----BEGIN OPENSSH PRIVATE KEY-----\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";
+ "-----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";
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCYiOyvMYLtkgoEyEKlLV+klM+"
+ "BU6Nh3PmAiqX aris@kalix86";
static const char torture_ed25519_testkey_cert[] =
- "ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQ"
- "G9wZW5zc2guY29tAAAAILrR4sPB+b6BRId/OkQha9nWwoACXqUTILz1TrmG4R9CAAA"
- "AIBWWnxuCYiOyvMYLtkgoEyEKlLV+klM+BU6Nh3PmAiqXAAAAAAAAAAAAAAABAAAAB"
- "215aWRlbnQAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTE"
- "tZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAF"
- "nBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnB"
- "lcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCY"
- "iOyvMYLtkgoEyEKlLV+klM+BU6Nh3PmAiqXAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEB"
- "d8AogGWM6njfejbazFVyfnjNiWqatx6IV3Nnqc3LjCiPY19fqIPe2YJSzytHwLTD5X"
- "IjD2bJpq2ZfjQwXpO0J ./ed.pub";
-
-static const char *torture_get_testkey_internal(enum ssh_keytypes_e type,
- bool with_passphrase,
- int pubkey,
- int format)
+ "ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQ"
+ "G9wZW5zc2guY29tAAAAILrR4sPB+b6BRId/OkQha9nWwoACXqUTILz1TrmG4R9CAAA"
+ "AIBWWnxuCYiOyvMYLtkgoEyEKlLV+klM+BU6Nh3PmAiqXAAAAAAAAAAAAAAABAAAAB"
+ "215aWRlbnQAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTE"
+ "tZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAF"
+ "nBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnB"
+ "lcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBWWnxuCY"
+ "iOyvMYLtkgoEyEKlLV+klM+BU6Nh3PmAiqXAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEB"
+ "d8AogGWM6njfejbazFVyfnjNiWqatx6IV3Nnqc3LjCiPY19fqIPe2YJSzytHwLTD5X"
+ "IjD2bJpq2ZfjQwXpO0J ./ed.pub";
+
+static const char *
+torture_get_testkey_public_internal(enum ssh_keytypes_e type,
+ enum torture_format_e format)
{
switch (type) {
- case SSH_KEYTYPE_DSS:
- if (pubkey) {
- return torture_dsa_public_testkey;
- } else if (with_passphrase) {
- if (format == 1) {
- return torture_dsa_private_openssh_testkey_passphrase;
- }
- if (format == 2) {
- return torture_dsa_private_pkcs8_testkey_passphrase;
- } else {
- return torture_dsa_private_testkey_passphrase;
- }
- }
- if (format == 1) {
- return torture_dsa_private_openssh_testkey;
- }
+ case SSH_KEYTYPE_DSS:
+ return torture_dsa_public_testkey;
+ case SSH_KEYTYPE_RSA:
+ if (format == FORMAT_OPENSSH) {
+ return torture_rsa_public_testkey;
+ }
+ return torture_rsa_public_testkey_pem;
+ case SSH_KEYTYPE_ECDSA_P521:
+ if (format == FORMAT_OPENSSH) {
+ return torture_ecdsa521_public_testkey;
+ }
+ return torture_ecdsa521_public_testkey_pem;
+ case SSH_KEYTYPE_ECDSA_P384:
+ if (format == FORMAT_OPENSSH) {
+ return torture_ecdsa384_public_testkey;
+ }
+ return torture_ecdsa384_public_testkey_pem;
+ case SSH_KEYTYPE_ECDSA_P256:
+ if (format == FORMAT_OPENSSH) {
+ return torture_ecdsa256_public_testkey;
+ }
+ return torture_ecdsa256_public_testkey_pem;
+ case SSH_KEYTYPE_ED25519:
+ if (format == FORMAT_OPENSSH) {
+ return torture_ed25519_public_testkey;
+ }
+ /* not available in other formats */
+ return NULL;
+ case SSH_KEYTYPE_DSS_CERT01:
+ return torture_dsa_testkey_cert;
+ case SSH_KEYTYPE_RSA_CERT01:
+ return torture_rsa_testkey_cert;
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ return torture_ecdsa256_testkey_cert;
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ return torture_ecdsa384_testkey_cert;
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ return torture_ecdsa521_testkey_cert;
+ case SSH_KEYTYPE_ED25519_CERT01:
+ return torture_ed25519_testkey_cert;
+ case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ case SSH_KEYTYPE_UNKNOWN:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static const char *
+torture_get_testkey_encrypted_internal(enum ssh_keytypes_e type,
+ enum torture_format_e format)
+{
+ switch (type) {
+ case SSH_KEYTYPE_DSS:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_dsa_private_openssh_testkey_passphrase;
+ case FORMAT_PKCS8:
+ return torture_dsa_private_pkcs8_testkey_passphrase;
+ case FORMAT_PEM:
+ return torture_dsa_private_testkey_passphrase;
+ }
+ return NULL;
+ case SSH_KEYTYPE_RSA:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_rsa_private_openssh_testkey_passphrase;
+ case FORMAT_PKCS8:
+ return torture_rsa_private_pkcs8_testkey_passphrase;
+ case FORMAT_PEM:
+ return torture_rsa_private_testkey_passphrase;
+ }
+ return NULL;
+ case SSH_KEYTYPE_ECDSA_P521:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ecdsa521_private_openssh_testkey_passphrase;
+ case FORMAT_PKCS8:
+ return torture_ecdsa521_private_pkcs8_testkey_passphrase;
+ case FORMAT_PEM:
+ return torture_ecdsa521_private_testkey_passphrase;
+ }
+ return NULL;
+ case SSH_KEYTYPE_ECDSA_P384:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ecdsa384_private_openssh_testkey_passphrase;
+ case FORMAT_PKCS8:
+ return torture_ecdsa384_private_pkcs8_testkey_passphrase;
+ case FORMAT_PEM:
+ return torture_ecdsa384_private_testkey_passphrase;
+ }
+ return NULL;
+ case SSH_KEYTYPE_ECDSA_P256:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ecdsa256_private_openssh_testkey_pasphrase;
+ case FORMAT_PKCS8:
+ return torture_ecdsa256_private_pkcs8_testkey_passphrase;
+ case FORMAT_PEM:
+ return torture_ecdsa256_private_testkey_passphrase;
+ }
+ return NULL;
+ case SSH_KEYTYPE_ED25519:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ed25519_private_openssh_testkey_passphrase;
+ case FORMAT_PKCS8:
+ return torture_ed25519_private_pkcs8_testkey_passphrase;
+ case FORMAT_PEM:
+ /* ed25519 keys are not available in legacy PEM format */
+ return NULL;
+ }
+ return NULL;
+ case SSH_KEYTYPE_DSS_CERT01:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ case SSH_KEYTYPE_UNKNOWN:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static const char *
+torture_get_testkey_internal(enum ssh_keytypes_e type,
+ enum torture_format_e format)
+{
+ switch (type) {
+ case SSH_KEYTYPE_DSS:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_dsa_private_openssh_testkey;
+ case FORMAT_PKCS8:
+ return torture_dsa_private_pkcs8_testkey;
+ case FORMAT_PEM:
return torture_dsa_private_testkey;
- case SSH_KEYTYPE_RSA:
- if (pubkey) {
- if (format == 1) {
- return torture_rsa_public_testkey_pem;
- } else {
- return torture_rsa_public_testkey;
- }
- } else if (with_passphrase) {
- if (format == 1) {
- return torture_rsa_private_openssh_testkey_passphrase;
- }
- if (format == 2) {
- return torture_rsa_private_pkcs8_testkey_passphrase;
- } else {
- return torture_rsa_private_testkey_passphrase;
- }
- }
- if (format == 1) {
- return torture_rsa_private_openssh_testkey;
- }
+ }
+ return NULL;
+ case SSH_KEYTYPE_RSA:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_rsa_private_openssh_testkey;
+ case FORMAT_PKCS8:
+ return torture_rsa_private_pkcs8_testkey;
+ case FORMAT_PEM:
return torture_rsa_private_testkey;
- case SSH_KEYTYPE_ECDSA_P521:
- if (pubkey) {
- if (format == 1) {
- return torture_ecdsa521_public_testkey_pem;
- } else {
- return torture_ecdsa521_public_testkey;
- }
- } else if (with_passphrase) {
- if (format == 1) {
- return torture_ecdsa521_private_openssh_testkey_passphrase;
- }
- if (format == 2) {
- return torture_ecdsa521_private_pkcs8_testkey_passphrase;
- } else {
- return torture_ecdsa521_private_testkey_passphrase;
- }
- }
- if (format == 1) {
- return torture_ecdsa521_private_openssh_testkey;
- }
+ }
+ return NULL;
+ case SSH_KEYTYPE_ECDSA_P521:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ecdsa521_private_openssh_testkey;
+ case FORMAT_PKCS8:
+ return torture_ecdsa521_private_pkcs8_testkey;
+ case FORMAT_PEM:
return torture_ecdsa521_private_testkey;
- case SSH_KEYTYPE_ECDSA_P384:
- if (pubkey) {
- if (format == 1) {
- return torture_ecdsa384_public_testkey_pem;
- } else {
- return torture_ecdsa384_public_testkey;
- }
- } else if (with_passphrase){
- if (format == 1) {
- return torture_ecdsa384_private_openssh_testkey_passphrase;
- }
- if (format == 2) {
- return torture_ecdsa384_private_pkcs8_testkey_passphrase;
- } else {
- return torture_ecdsa384_private_testkey_passphrase;
- }
- }
- if (format == 1) {
- return torture_ecdsa384_private_openssh_testkey;
- }
+ }
+ return NULL;
+ case SSH_KEYTYPE_ECDSA_P384:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ecdsa384_private_openssh_testkey;
+ case FORMAT_PKCS8:
+ return torture_ecdsa384_private_pkcs8_testkey;
+ case FORMAT_PEM:
return torture_ecdsa384_private_testkey;
- case SSH_KEYTYPE_ECDSA_P256:
- if (pubkey) {
- if (format == 1) {
- return torture_ecdsa256_public_testkey_pem;
- } else {
- return torture_ecdsa256_public_testkey;
- }
- } else if (with_passphrase){
- if (format == 1) {
- return torture_ecdsa256_private_openssh_testkey_pasphrase;
- }
- if (format == 2) {
- return torture_ecdsa256_private_pkcs8_testkey_passphrase;
- } else {
- return torture_ecdsa256_private_testkey_passphrase;
- }
- }
- if (format == 1) {
- return torture_ecdsa256_private_openssh_testkey;
- }
+ }
+ return NULL;
+ case SSH_KEYTYPE_ECDSA_P256:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ecdsa256_private_openssh_testkey;
+ case FORMAT_PKCS8:
+ return torture_ecdsa256_private_pkcs8_testkey;
+ case FORMAT_PEM:
return torture_ecdsa256_private_testkey;
- case SSH_KEYTYPE_ED25519:
- if (pubkey) {
- return torture_ed25519_public_testkey;
- } else if (with_passphrase) {
- if (format == 1) {
- 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_openssh_testkey;
- }
- /* ed25519 keys are not available in legacy PEM format */
+ }
+ return NULL;
+ case SSH_KEYTYPE_ED25519:
+ switch (format) {
+ case FORMAT_OPENSSH:
+ return torture_ed25519_private_openssh_testkey;
+ case FORMAT_PKCS8:
return torture_ed25519_private_pkcs8_testkey;
- case SSH_KEYTYPE_DSS_CERT01:
- return torture_dsa_testkey_cert;
- case SSH_KEYTYPE_RSA_CERT01:
- return torture_rsa_testkey_cert;
- case SSH_KEYTYPE_ECDSA_P256_CERT01:
- return torture_ecdsa256_testkey_cert;
- case SSH_KEYTYPE_ECDSA_P384_CERT01:
- return torture_ecdsa384_testkey_cert;
- case SSH_KEYTYPE_ECDSA_P521_CERT01:
- return torture_ecdsa521_testkey_cert;
- case SSH_KEYTYPE_ED25519_CERT01:
- return torture_ed25519_testkey_cert;
- case SSH_KEYTYPE_RSA1:
- case SSH_KEYTYPE_ECDSA:
- case SSH_KEYTYPE_SK_ECDSA:
- case SSH_KEYTYPE_SK_ECDSA_CERT01:
- case SSH_KEYTYPE_SK_ED25519:
- case SSH_KEYTYPE_SK_ED25519_CERT01:
- case SSH_KEYTYPE_UNKNOWN:
+ case FORMAT_PEM:
+ /* ed25519 keys are not available in legacy PEM format */
return NULL;
+ }
+ return NULL;
+ case SSH_KEYTYPE_DSS_CERT01:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ case SSH_KEYTYPE_UNKNOWN:
+ return NULL;
}
return NULL;
}
/* Return the encrypted private key in a new OpenSSH format */
-const char *torture_get_openssh_testkey(enum ssh_keytypes_e type,
- bool with_passphrase)
+const char *
+torture_get_openssh_testkey(enum ssh_keytypes_e type, bool with_passphrase)
{
- return torture_get_testkey_internal(type, with_passphrase, 0, 1);
+ if (with_passphrase) {
+ return torture_get_testkey_encrypted_internal(type, FORMAT_OPENSSH);
+ } else {
+ return torture_get_testkey_internal(type, FORMAT_OPENSSH);
+ }
}
/* Return the private key in PEM format */
-const char *torture_get_testkey(enum ssh_keytypes_e type,
- bool with_passphrase)
+const char *
+torture_get_testkey(enum ssh_keytypes_e type, bool with_passphrase)
{
+ enum torture_format_e format = FORMAT_PEM;
+
+ if (with_passphrase) {
+/* This is the new PKCS8 PEM format, which works only in OpenSSL */
#if defined(HAVE_LIBCRYPTO)
- return torture_get_testkey_internal(type, with_passphrase, 0, 2);
-#else
- return torture_get_testkey_internal(type, with_passphrase, 0, 0);
+ format = FORMAT_PKCS8;
#endif
+ return torture_get_testkey_encrypted_internal(type, format);
+ } else {
+/* The unencrypted format works also in mbedTLS */
+#if defined(HAVE_LIBCRYPTO) || defined(HAVE_LIBMBEDCRYPTO)
+ format = FORMAT_PKCS8;
+#endif
+ return torture_get_testkey_internal(type, format);
+ }
}
-const char *torture_get_testkey_pub(enum ssh_keytypes_e type)
+const char *
+torture_get_testkey_pub(enum ssh_keytypes_e type)
{
- return torture_get_testkey_internal(type, 0, 1, 0);
+ return torture_get_testkey_public_internal(type, FORMAT_OPENSSH);
}
-const char *torture_get_testkey_pub_pem(enum ssh_keytypes_e type)
+const char *
+torture_get_testkey_pub_pem(enum ssh_keytypes_e type)
{
- return torture_get_testkey_internal(type, 0, 1, 1);
+ return torture_get_testkey_public_internal(type, FORMAT_PEM);
}
-const char *torture_get_testkey_passphrase(void)
+const char *
+torture_get_testkey_passphrase(void)
{
return TORTURE_TESTKEY_PASSWORD;
}
diff --git a/tests/torture_pki.c b/tests/torture_pki.c
index cbf64951..f5420946 100644
--- a/tests/torture_pki.c
+++ b/tests/torture_pki.c
@@ -9,15 +9,10 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
-#elif (defined _WIN32) || (defined _WIN64)
-#include <io.h>
-#define read _read
-#define open _open
-#define write _write
-#define close _close
#endif
#include "torture_pki.h"
+#include <libssh/priv.h>
char *torture_pki_read_file(const char *filename)
{
diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt
index 3ce3f10a..c053e5b8 100644
--- a/tests/unittests/CMakeLists.txt
+++ b/tests/unittests/CMakeLists.txt
@@ -1,8 +1,7 @@
project(unittests C)
-include_directories(${OPENSSL_INCLUDE_DIR})
-
set(LIBSSH_UNIT_TESTS
+ torture_bignum
torture_buffer
torture_bytearray
torture_callbacks
@@ -30,6 +29,12 @@ set(LIBSSH_THREAD_UNIT_TESTS
torture_threads_crypto
)
+set(TORTURE_UNIT_ENVIRONMENT
+ "LSAN_OPTIONS=suppressions=${libssh-tests_SOURCE_DIR}/suppressions/lsan.supp;")
+if (OPENSSL_FOUND)
+ list(APPEND TORTURE_UNIT_ENVIRONMENT OPENSSL_ENABLE_SHA1_SIGNATURES=1)
+endif (OPENSSL_FOUND)
+
if (UNIX AND NOT WIN32)
set(LIBSSH_UNIT_TESTS
${LIBSSH_UNIT_TESTS}
@@ -39,6 +44,7 @@ if (UNIX AND NOT WIN32)
torture_keyfiles
torture_pki
torture_pki_rsa
+ torture_pki_dsa
torture_pki_ed25519
# requires /dev/null
torture_channel
@@ -56,20 +62,13 @@ if (UNIX AND NOT WIN32)
endif()
endif()
-
- if (HAVE_DSA)
- set(LIBSSH_UNIT_TESTS
- ${LIBSSH_UNIT_TESTS}
- torture_pki_dsa
- )
- endif()
-
if (WITH_PKCS11_URI)
set(LIBSSH_UNIT_TESTS
${LIBSSH_UNIT_TESTS}
torture_pki_rsa_uri
torture_pki_ecdsa_uri
)
+ list(APPEND TORTURE_UNIT_ENVIRONMENT PKCS11_PROVIDER_DEBUG=file:/tmp/p11prov-debug.log)
endif()
if (HAVE_ECC)
@@ -84,10 +83,12 @@ if (UNIX AND NOT WIN32)
# requires pthread
torture_threads_pki_rsa
)
- # Not working correctly
- #if (WITH_SERVER)
- # add_cmocka_test(torture_server_x11 torture_server_x11.c ${TEST_TARGET_LIBRARIES})
- #endif (WITH_SERVER)
+ if (WITH_SERVER)
+ set(LIBSSH_THREAD_UNIT_TESTS
+ ${LIBSSH_THREAD_UNIT_TESTS}
+ torture_unit_server
+ )
+ endif (WITH_SERVER)
endif (UNIX AND NOT WIN32)
foreach(_UNIT_TEST ${LIBSSH_UNIT_TESTS})
@@ -96,6 +97,10 @@ foreach(_UNIT_TEST ${LIBSSH_UNIT_TESTS})
COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS}
LINK_LIBRARIES ${TEST_TARGET_LIBRARIES}
)
+
+ set_property(TEST ${_UNIT_TEST}
+ PROPERTY
+ ENVIRONMENT ${TORTURE_UNIT_ENVIRONMENT})
endforeach()
if (CMAKE_USE_PTHREADS_INIT)
@@ -105,6 +110,10 @@ if (CMAKE_USE_PTHREADS_INIT)
COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS}
LINK_LIBRARIES ${TEST_TARGET_LIBRARIES} Threads::Threads
)
+
+ set_property(TEST ${_UNIT_TEST}
+ PROPERTY
+ ENVIRONMENT ${TORTURE_UNIT_ENVIRONMENT})
endforeach()
endif ()
diff --git a/tests/unittests/hello world.sh b/tests/unittests/hello world.sh
new file mode 100755
index 00000000..8f687028
--- /dev/null
+++ b/tests/unittests/hello world.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+printf '%s' "$1" 2>&1
diff --git a/tests/unittests/torture_bignum.c b/tests/unittests/torture_bignum.c
new file mode 100644
index 00000000..c36b81f8
--- /dev/null
+++ b/tests/unittests/torture_bignum.c
@@ -0,0 +1,106 @@
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include "libssh/bignum.h"
+#include "libssh/string.h"
+
+static void check_str (int n, ssh_string str)
+{
+ if (n > 0 && n <= 127) {
+ assert_int_equal(1, ntohl (str->size));
+ assert_int_equal(n, str->data[0]);
+ } else if (n > 127 && n <= 255) {
+ assert_int_equal(2, ntohl (str->size));
+ assert_int_equal(0, str->data[0]);
+ assert_int_equal(n, str->data[1]);
+ } else if (n > 255 && n <= 32767) {
+ assert_int_equal(2, ntohl (str->size));
+ assert_int_equal(n >> 8, str->data[0]);
+ assert_int_equal(n & 0xFF, str->data[1]);
+ } else {
+ assert_int_equal(3, ntohl (str->size));
+ assert_int_equal(n >> 16, str->data[0]);
+ assert_int_equal((n >> 8) & 0xFF, str->data[1]);
+ assert_int_equal(n & 0xFF, str->data[2]);
+ }
+}
+
+static void check_bignum(int n, const char *nstr) {
+ bignum num, num2;
+ ssh_string str;
+ char *dec;
+
+ num = bignum_new();
+ assert_non_null(num);
+
+ assert_int_equal (1, bignum_set_word (num, n));
+
+ ssh_print_bignum("num", num);
+
+ dec = bignum_bn2dec (num);
+ assert_non_null (dec);
+ assert_string_equal (nstr, dec);
+ ssh_crypto_free(dec);
+
+ /* ssh_make_bignum_string */
+
+ str = ssh_make_bignum_string(num);
+ assert_non_null(str);
+
+ check_str (n, str);
+
+ /* ssh_make_string_bn */
+
+ num2 = ssh_make_string_bn(str);
+ ssh_string_free (str);
+ assert_non_null(num2);
+
+ ssh_print_bignum("num2", num2);
+
+ assert_int_equal (0, bignum_cmp (num, num2));
+
+ dec = bignum_bn2dec (num2);
+ assert_non_null (dec);
+ assert_string_equal (nstr, dec);
+ ssh_crypto_free(dec);
+
+ bignum_safe_free(num);
+ bignum_safe_free(num2);
+}
+
+
+static void torture_bignum(void **state) {
+ (void) state; /* unused */
+
+ ssh_set_log_level(SSH_LOG_TRACE);
+
+ check_bignum (1, "1");
+ check_bignum (17, "17");
+ check_bignum (42, "42");
+ check_bignum (127, "127");
+ check_bignum (128, "128");
+ check_bignum (254, "254");
+ check_bignum (255, "255");
+ check_bignum (256, "256");
+ check_bignum (257, "257");
+ check_bignum (300, "300");
+ check_bignum (32767, "32767");
+ check_bignum (32768, "32768");
+ check_bignum (65535, "65535");
+ check_bignum (65536, "65536");
+}
+
+int torture_run_tests(void) {
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_bignum),
+ };
+
+ ssh_init();
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+ ssh_finalize();
+ return rc;
+}
diff --git a/tests/unittests/torture_bind_config.c b/tests/unittests/torture_bind_config.c
index 11d8672e..a2f9be06 100644
--- a/tests/unittests/torture_bind_config.c
+++ b/tests/unittests/torture_bind_config.c
@@ -54,9 +54,6 @@ extern LIBSSH_THREAD int ssh_log_level;
#define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
#define MACS2 "hmac-sha1"
-#ifdef HAVE_DSA
-#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa"
-#endif
#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
#define LIBSSH_ED25519_TESTKEY "libssh_testkey.id_ed25519"
#ifdef HAVE_ECC
@@ -64,37 +61,94 @@ extern LIBSSH_THREAD int ssh_log_level;
#endif
#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS "libssh_test_bind_config_listenaddress"
+#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_STRING "ListenAddress "LISTEN_ADDRESS"\n"
#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2 "libssh_test_bind_config_listenaddress2"
+#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2_STRING "ListenAddress "LISTEN_ADDRESS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE "libssh_test_bind_config_listenaddress_twice"
+#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_STRING \
+ "ListenAddress "LISTEN_ADDRESS"\n" \
+ "ListenAddress "LISTEN_ADDRESS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC "libssh_test_bind_config_listenaddress_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC_STRING \
+ "ListenAddress "LISTEN_ADDRESS"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_PORT "libssh_test_bind_config_port"
+#define LIBSSH_TEST_BIND_CONFIG_PORT_STRING "Port 123\n"
#define LIBSSH_TEST_BIND_CONFIG_PORT2 "libssh_test_bind_config_port2"
+#define LIBSSH_TEST_BIND_CONFIG_PORT2_STRING "Port 456\n"
#define LIBSSH_TEST_BIND_CONFIG_PORT_TWICE "libssh_test_bind_config_port_twice"
+#define LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_STRING \
+ "Port 123\n" \
+ "Port 456\n"
#define LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC "libssh_test_bind_config_port_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC_STRING \
+ "Port 123\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_PORT2"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY "libssh_test_bind_config_hostkey"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_STRING "HostKey "LIBSSH_ECDSA_521_TESTKEY"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY2 "libssh_test_bind_config_hostkey2"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY2_STRING "HostKey "LIBSSH_RSA_TESTKEY"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE "libssh_test_bind_config_hostkey_twice"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_STRING \
+ "HostKey "LIBSSH_ECDSA_521_TESTKEY"\n" \
+ "HostKey "LIBSSH_RSA_TESTKEY"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC "libssh_test_bind_config_hostkey_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC_STRING \
+ "HostKey "LIBSSH_ECDSA_521_TESTKEY"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_HOSTKEY2"\n"
#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL "libssh_test_bind_config_loglevel"
-#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL2 "libssh_test_bind_config_loglevel2"
+#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_STRING "LogLevel "LOGLEVEL"\n"
+#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1 "libssh_test_bind_config_loglevel2"
+#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1_STRING "LogLevel "LOGLEVEL2"\n"
#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE "libssh_test_bind_config_loglevel_twice"
+#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_STRING \
+ "LogLevel "LOGLEVEL"\n" \
+ "LogLevel "LOGLEVEL2"\n"
#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC "libssh_test_bind_config_loglevel_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC_STRING \
+ "LogLevel "LOGLEVEL"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1"\n"
#define LIBSSH_TEST_BIND_CONFIG_CIPHERS "libssh_test_bind_config_ciphers"
+#define LIBSSH_TEST_BIND_CONFIG_CIPHERS_STRING "Ciphers "CIPHERS"\n"
#define LIBSSH_TEST_BIND_CONFIG_CIPHERS2 "libssh_test_bind_config_ciphers2"
+#define LIBSSH_TEST_BIND_CONFIG_CIPHERS2_STRING "Ciphers "CIPHERS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE "libssh_test_bind_config_ciphers_twice"
+#define LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_STRING \
+ "Ciphers "CIPHERS"\n" \
+ "Ciphers "CIPHERS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC "libssh_test_bind_config_ciphers_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC_STRING \
+ "Ciphers "CIPHERS"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_CIPHERS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_MACS "libssh_test_bind_config_macs"
+#define LIBSSH_TEST_BIND_CONFIG_MACS_STRING "MACs "MACS"\n"
#define LIBSSH_TEST_BIND_CONFIG_MACS2 "libssh_test_bind_config_macs2"
+#define LIBSSH_TEST_BIND_CONFIG_MACS2_STRING "MACs "MACS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_MACS_TWICE "libssh_test_bind_config_macs_twice"
+#define LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_STRING \
+ "MACs "MACS"\n" \
+ "MACs "MACS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC "libssh_test_bind_config_macs_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC_STRING \
+ "MACs "MACS"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_MACS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS "libssh_test_bind_config_kexalgorithms"
+#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_STRING "KexAlgorithms "KEXALGORITHMS"\n"
#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2 "libssh_test_bind_config_kexalgorithms2"
+#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2_STRING "KexAlgorithms "KEXALGORITHMS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE "libssh_test_bind_config_kexalgorithms_twice"
+#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_STRING \
+ "KexAlgorithms "KEXALGORITHMS"\n" \
+ "KexAlgorithms "KEXALGORITHMS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC "libssh_test_bind_config_kexalgorithms_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC_STRING \
+ "KexAlgorithms "KEXALGORITHMS"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_FULL "libssh_test_bind_config_full"
#define LIBSSH_TEST_BIND_CONFIG_INCLUDE "libssh_test_bind_config_include"
#define LIBSSH_TEST_BIND_CONFIG_INCLUDE_RECURSIVE "libssh_test_bind_config_include_recursive"
+#define LIBSSH_TEST_BIND_CONFIG_INCLUDE_RECURSIVE_LOOP "libssh_test_bind_config_include_recursive_loop"
#define LIBSSH_TEST_BIND_CONFIG_CORNER_CASES "libssh_test_bind_config_corner_cases"
#define LIBSSH_TEST_BIND_CONFIG_MATCH_ALL "libssh_test_bind_config_match_all"
@@ -106,16 +160,34 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TEST_BIND_CONFIG_MATCH_INVALID2 "libssh_test_bind_config_match_invalid2"
#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED "libssh_test_bind_config_pubkey"
+#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_STRING "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n"
#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2 "libssh_test_bind_config_pubkey2"
+#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2_STRING "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES2"\n"
#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE "libssh_test_bind_config_pubkey_twice"
+#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_STRING \
+ "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n" \
+ "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES2"\n"
#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC "libssh_test_bind_config_pubkey_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC_STRING \
+ "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES2"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS"\n"
#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN "libssh_test_bind_config_pubkey_unknown"
+#define LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN_STRING "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES_UNKNOWN"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS "libssh_test_bind_config_hostkey_alg"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_STRING "HostKeyAlgorithms "HOSTKEYALGORITHMS"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2 "libssh_test_bind_config_hostkey_alg2"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2_STRING "HostKeyAlgorithms "HOSTKEYALGORITHMS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE "libssh_test_bind_config_hostkey_alg_twice"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_STRING \
+ "HostKeyAlgorithms "HOSTKEYALGORITHMS"\n" \
+ "HostKeyAlgorithms "HOSTKEYALGORITHMS2"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC "libssh_test_bind_config_hostkey_alg_twice_rec"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC_STRING \
+ "HostKeyAlgorithms "HOSTKEYALGORITHMS2"\n" \
+ "Include "LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS"\n"
#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN "libssh_test_bind_config_hostkey_alg_unknown"
+#define LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN_STRING "HostKeyAlgorithms "HOSTKEYALGORITHMS_UNKNOWN"\n"
const char template[] = "temp_dir_XXXXXX";
@@ -162,87 +234,69 @@ static int setup_config_files(void **state)
torture_write_file(LIBSSH_ECDSA_521_TESTKEY,
torture_get_openssh_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
#endif
-#ifdef HAVE_DSA
- torture_write_file(LIBSSH_DSA_TESTKEY,
- torture_get_openssh_testkey(SSH_KEYTYPE_DSS, 0));
-#endif
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS,
- "ListenAddress "LISTEN_ADDRESS"\n");
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2,
- "ListenAddress "LISTEN_ADDRESS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE,
- "ListenAddress "LISTEN_ADDRESS"\n"
- "ListenAddress "LISTEN_ADDRESS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC,
- "ListenAddress "LISTEN_ADDRESS"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PORT,
- "Port 123\n");
+ LIBSSH_TEST_BIND_CONFIG_PORT_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PORT2,
- "Port 456\n");
+ LIBSSH_TEST_BIND_CONFIG_PORT2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PORT_TWICE,
- "Port 123\n"
- "Port 456\n");
+ LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC,
- "Port 123\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_PORT2"\n");
+ LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY,
- "HostKey "LIBSSH_ECDSA_521_TESTKEY"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY2,
- "HostKey "LIBSSH_RSA_TESTKEY"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE,
- "HostKey "LIBSSH_ECDSA_521_TESTKEY"\n"
- "HostKey "LIBSSH_RSA_TESTKEY"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC,
- "HostKey "LIBSSH_ECDSA_521_TESTKEY"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_HOSTKEY2"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LOGLEVEL,
- "LogLevel "LOGLEVEL"\n");
- torture_write_file(LIBSSH_TEST_BIND_CONFIG_LOGLEVEL2,
- "LogLevel "LOGLEVEL2"\n");
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_STRING);
+ torture_write_file(LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE,
- "LogLevel "LOGLEVEL"\n"
- "LogLevel "LOGLEVEL2"\n");
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC,
- "LogLevel "LOGLEVEL"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_LOGLEVEL2"\n");
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_CIPHERS,
- "Ciphers "CIPHERS"\n");
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_CIPHERS2,
- "Ciphers "CIPHERS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE,
- "Ciphers "CIPHERS"\n"
- "Ciphers "CIPHERS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC,
- "Ciphers "CIPHERS"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_CIPHERS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_MACS,
- "MACs "MACS"\n");
+ LIBSSH_TEST_BIND_CONFIG_MACS_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_MACS2,
- "MACs "MACS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_MACS2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_MACS_TWICE,
- "MACs "MACS"\n"
- "MACs "MACS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC,
- "MACs "MACS"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_MACS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS,
- "KexAlgorithms "KEXALGORITHMS"\n");
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2,
- "KexAlgorithms "KEXALGORITHMS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE,
- "KexAlgorithms "KEXALGORITHMS"\n"
- "KexAlgorithms "KEXALGORITHMS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC,
- "KexAlgorithms "KEXALGORITHMS"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_FULL,
"ListenAddress "LISTEN_ADDRESS"\n"
@@ -265,6 +319,9 @@ static int setup_config_files(void **state)
torture_write_file(LIBSSH_TEST_BIND_CONFIG_INCLUDE_RECURSIVE,
"Include "LIBSSH_TEST_BIND_CONFIG_INCLUDE"\n");
+ torture_write_file(LIBSSH_TEST_BIND_CONFIG_INCLUDE_RECURSIVE_LOOP,
+ "Include "LIBSSH_TEST_BIND_CONFIG_INCLUDE_RECURSIVE_LOOP"\n");
+
/* Unsupported options and corner cases */
torture_write_file(LIBSSH_TEST_BIND_CONFIG_CORNER_CASES,
"\n" /* empty line */
@@ -334,30 +391,26 @@ static int setup_config_files(void **state)
"\tLogLevel "LOGLEVEL4"\n");
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED,
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n");
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2,
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES2"\n");
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE,
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n"
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES2"\n");
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC,
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES2"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS"\n");
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN,
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES_UNKNOWN"\n");
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS,
- "HostKeyAlgorithms "HOSTKEYALGORITHMS"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2,
- "HostKeyAlgorithms "HOSTKEYALGORITHMS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE,
- "HostKeyAlgorithms "HOSTKEYALGORITHMS"\n"
- "HostKeyAlgorithms "HOSTKEYALGORITHMS2"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC,
- "HostKeyAlgorithms "HOSTKEYALGORITHMS2"\n"
- "Include "LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC_STRING);
torture_write_file(LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN,
- "HostKeyAlgorithms "HOSTKEYALGORITHMS_UNKNOWN"\n");
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN_STRING);
return 0;
}
@@ -405,11 +458,43 @@ static int sshbind_teardown(void **state)
return 0;
}
-static void torture_bind_config_listen_address(void **state)
+/**
+ * @brief helper function loading configuration from either file or string
+ */
+static void
+_parse_config(ssh_bind bind,
+ const char *file,
+ const char *string,
+ int expected)
+{
+ int ret = -1;
+
+ /* make sure either config file or config string is given,
+ * not both */
+ assert_int_not_equal(file == NULL, string == NULL);
+
+ if (file != NULL) {
+ ret = ssh_bind_config_parse_file(bind, file);
+ } else if (string != NULL) {
+ ret = ssh_bind_config_parse_string(bind, string);
+ } else {
+ /* should not happen */
+ fail();
+ }
+
+ /* make sure parsing went as expected */
+ assert_return_code(ret, expected);
+}
+
+
+static void
+torture_bind_config_listen_address(void **state,
+ const char *file,
+ const char *string,
+ const char *expect)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -417,37 +502,84 @@ static void torture_bind_config_listen_address(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS);
- assert_int_equal(rc, 0);
- assert_non_null(bind->bindaddr);
- assert_string_equal(bind->bindaddr, LISTEN_ADDRESS);
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE);
- assert_int_equal(rc, 0);
assert_non_null(bind->bindaddr);
- assert_string_equal(bind->bindaddr, LISTEN_ADDRESS);
+ assert_string_equal(bind->bindaddr, expect);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC);
- assert_int_equal(rc, 0);
- assert_non_null(bind->bindaddr);
- assert_string_equal(bind->bindaddr, LISTEN_ADDRESS);
+static void torture_bind_config_listen_address_file(void **state)
+{
+ torture_bind_config_listen_address(state,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS,
+ NULL,
+ LISTEN_ADDRESS);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2);
- assert_int_equal(rc, 0);
- assert_non_null(bind->bindaddr);
- assert_string_equal(bind->bindaddr, LISTEN_ADDRESS2);
+static void torture_bind_config_listen_address_string(void **state)
+{
+ torture_bind_config_listen_address(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_STRING,
+ LISTEN_ADDRESS);
+}
+
+static void torture_bind_config_listen_address2_file(void **state)
+{
+ torture_bind_config_listen_address(state,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2,
+ NULL,
+ LISTEN_ADDRESS2);
+}
+
+static void torture_bind_config_listen_address2_string(void **state)
+{
+ torture_bind_config_listen_address(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS2_STRING,
+ LISTEN_ADDRESS2);
+}
+
+static void torture_bind_config_listen_address_twice_file(void **state)
+{
+ torture_bind_config_listen_address(state,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE,
+ NULL,
+ LISTEN_ADDRESS);
+}
+
+static void torture_bind_config_listen_address_twice_string(void **state)
+{
+ torture_bind_config_listen_address(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_STRING,
+ LISTEN_ADDRESS);
+}
+
+static void torture_bind_config_listen_address_twice_rec_file(void **state)
+{
+ torture_bind_config_listen_address(state,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC,
+ NULL,
+ LISTEN_ADDRESS);
+}
+static void torture_bind_config_listen_address_twice_rec_string(void **state)
+{
+ torture_bind_config_listen_address(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LISTENADDRESS_TWICE_REC_STRING,
+ LISTEN_ADDRESS);
}
-static void torture_bind_config_port(void **state)
+static void
+torture_bind_config_port(void **state,
+ const char *file,
+ const char *string,
+ int expect)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -455,29 +587,82 @@ static void torture_bind_config_port(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_PORT);
- assert_int_equal(rc, 0);
- assert_int_equal(bind->bindport, 123);
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_PORT_TWICE);
- assert_int_equal(rc, 0);
- assert_int_equal(bind->bindport, 123);
+ assert_int_equal(bind->bindport, expect);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC);
- assert_int_equal(rc, 0);
- assert_int_equal(bind->bindport, 123);
+static void torture_bind_config_port_file(void **state)
+{
+ torture_bind_config_port(state,
+ LIBSSH_TEST_BIND_CONFIG_PORT,
+ NULL,
+ 123);
+}
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_PORT2);
- assert_int_equal(rc, 0);
- assert_int_equal(bind->bindport, 456);
+static void torture_bind_config_port_string(void **state)
+{
+ torture_bind_config_port(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PORT_STRING,
+ 123);
+}
+
+static void torture_bind_config_port2_file(void **state)
+{
+ torture_bind_config_port(state,
+ LIBSSH_TEST_BIND_CONFIG_PORT2,
+ NULL,
+ 456);
+}
+
+static void torture_bind_config_port2_string(void **state)
+{
+ torture_bind_config_port(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PORT2_STRING,
+ 456);
+}
+
+static void torture_bind_config_port_twice_file(void **state)
+{
+ torture_bind_config_port(state,
+ LIBSSH_TEST_BIND_CONFIG_PORT_TWICE,
+ NULL,
+ 123);
+}
+
+static void torture_bind_config_port_twice_string(void **state)
+{
+ torture_bind_config_port(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_STRING,
+ 123);
+}
+
+static void torture_bind_config_port_twice_rec_file(void **state)
+{
+ torture_bind_config_port(state,
+ LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC,
+ NULL,
+ 123);
+}
+
+static void torture_bind_config_port_twice_rec_string(void **state)
+{
+ torture_bind_config_port(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PORT_TWICE_REC_STRING,
+ 123);
}
-static void torture_bind_config_hostkey(void **state)
+static void
+torture_bind_config_hostkey(void **state,
+ const char *file,
+ const char *string)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -485,25 +670,19 @@ static void torture_bind_config_hostkey(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_HOSTKEY);
- assert_int_equal(rc, 0);
- assert_non_null(bind->ecdsakey);
- assert_string_equal(bind->ecdsakey, LIBSSH_ECDSA_521_TESTKEY);
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE);
- assert_int_equal(rc, 0);
assert_non_null(bind->ecdsakey);
assert_string_equal(bind->ecdsakey, LIBSSH_ECDSA_521_TESTKEY);
- assert_non_null(bind->rsakey);
- assert_string_equal(bind->rsakey, LIBSSH_RSA_TESTKEY);
}
-static void torture_bind_config_hostkey_twice_rec(void **state)
+static void
+torture_bind_config_hostkey2(void **state,
+ const char *file,
+ const char *string)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -511,15 +690,44 @@ static void torture_bind_config_hostkey_twice_rec(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC);
- assert_int_equal(rc, 0);
+ _parse_config(bind, file, string, SSH_OK);
+
assert_non_null(bind->ecdsakey);
assert_string_equal(bind->ecdsakey, LIBSSH_ECDSA_521_TESTKEY);
assert_non_null(bind->rsakey);
assert_string_equal(bind->rsakey, LIBSSH_RSA_TESTKEY);
}
+static void torture_bind_config_hostkey_file(void **state)
+{
+ torture_bind_config_hostkey(state, LIBSSH_TEST_BIND_CONFIG_HOSTKEY, NULL);
+}
+
+static void torture_bind_config_hostkey_string(void **state)
+{
+ torture_bind_config_hostkey(state, NULL, LIBSSH_TEST_BIND_CONFIG_HOSTKEY_STRING);
+}
+
+static void torture_bind_config_hostkey_twice_file(void **state)
+{
+ torture_bind_config_hostkey2(state, LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE, NULL);
+}
+
+static void torture_bind_config_hostkey_twice_string(void **state)
+{
+ torture_bind_config_hostkey2(state, NULL, LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_STRING);
+}
+
+static void torture_bind_config_hostkey_twice_rec_file(void **state)
+{
+ torture_bind_config_hostkey2(state, LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC, NULL);
+}
+
+static void torture_bind_config_hostkey_twice_rec_string(void **state)
+{
+ torture_bind_config_hostkey2(state, NULL, LIBSSH_TEST_BIND_CONFIG_HOSTKEY_TWICE_REC_STRING);
+}
+
static void torture_bind_config_hostkey_separately(void **state)
{
struct bind_st *test_state;
@@ -545,12 +753,17 @@ static void torture_bind_config_hostkey_separately(void **state)
assert_string_equal(bind->ecdsakey, LIBSSH_ECDSA_521_TESTKEY);
}
-static void torture_bind_config_loglevel(void **state)
+static void
+torture_bind_config_loglevel(void **state,
+ const char *file,
+ const char *string,
+ int expect)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
- int previous_level, new_level;
+ int previous_level, new_level, rc;
+
+ previous_level = ssh_get_log_level();
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -558,45 +771,88 @@ static void torture_bind_config_loglevel(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- previous_level = ssh_get_log_level();
-
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_LOGLEVEL);
- assert_int_equal(rc, 0);
+ _parse_config(bind, file, string, SSH_OK);
new_level = ssh_get_log_level();
- assert_int_equal(new_level, 2);
+ assert_int_equal(new_level, expect);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE);
- assert_int_equal(rc, 0);
+ rc = ssh_set_log_level(previous_level);
+ assert_int_equal(rc, SSH_OK);
+}
- new_level = ssh_get_log_level();
- assert_int_equal(new_level, 2);
+static void torture_bind_config_loglevel_file(void **state)
+{
+ torture_bind_config_loglevel(state,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL,
+ NULL,
+ 2);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC);
- assert_int_equal(rc, 0);
+static void torture_bind_config_loglevel_string(void **state)
+{
+ torture_bind_config_loglevel(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_STRING,
+ 2);
+}
- new_level = ssh_get_log_level();
- assert_int_equal(new_level, 2);
+static void torture_bind_config_loglevel1_file(void **state)
+{
+ torture_bind_config_loglevel(state,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1,
+ NULL,
+ 1);
+}
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_LOGLEVEL2);
- assert_int_equal(rc, 0);
+static void torture_bind_config_loglevel1_string(void **state)
+{
+ torture_bind_config_loglevel(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL1_STRING,
+ 1);
+}
- new_level = ssh_get_log_level();
- assert_int_equal(new_level, 1);
+static void torture_bind_config_loglevel_twice_file(void **state)
+{
+ torture_bind_config_loglevel(state,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE,
+ NULL,
+ 2);
+}
- rc = ssh_set_log_level(previous_level);
- assert_int_equal(rc, SSH_OK);
+static void torture_bind_config_loglevel_twice_string(void **state)
+{
+ torture_bind_config_loglevel(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_STRING,
+ 2);
+}
+
+static void torture_bind_config_loglevel_twice_rec_file(void **state)
+{
+ torture_bind_config_loglevel(state,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC,
+ NULL,
+ 2);
+}
+
+static void torture_bind_config_loglevel_twice_rec_string(void **state)
+{
+ torture_bind_config_loglevel(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_LOGLEVEL_TWICE_REC_STRING,
+ 2);
}
-static void torture_bind_config_ciphers(void **state)
+static void
+torture_bind_config_ciphers(void **state,
+ const char *file,
+ const char *string,
+ const char *expect)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
char *fips_ciphers = NULL;
- char *fips_ciphers2 = NULL;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -605,73 +861,96 @@ static void torture_bind_config_ciphers(void **state)
bind = test_state->bind;
if (ssh_fips_mode()) {
- fips_ciphers = ssh_keep_fips_algos(SSH_CRYPT_C_S, CIPHERS);
+ fips_ciphers = ssh_keep_fips_algos(SSH_CRYPT_C_S, expect);
assert_non_null(fips_ciphers);
- fips_ciphers2 = ssh_keep_fips_algos(SSH_CRYPT_C_S, CIPHERS2);
- assert_non_null(fips_ciphers2);
}
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_CIPHERS);
- assert_int_equal(rc, 0);
- assert_non_null(bind->wanted_methods[SSH_CRYPT_C_S]);
- assert_non_null(bind->wanted_methods[SSH_CRYPT_S_C]);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], fips_ciphers);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], fips_ciphers);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], CIPHERS);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], CIPHERS);
- }
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE);
- assert_int_equal(rc, 0);
assert_non_null(bind->wanted_methods[SSH_CRYPT_C_S]);
assert_non_null(bind->wanted_methods[SSH_CRYPT_S_C]);
if (ssh_fips_mode()) {
assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], fips_ciphers);
assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], fips_ciphers);
+ SAFE_FREE(fips_ciphers);
} else {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], CIPHERS);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], CIPHERS);
+ assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], expect);
+ assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], expect);
}
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC);
- assert_int_equal(rc, 0);
+static void torture_bind_config_ciphers_file(void **state)
+{
+ torture_bind_config_ciphers(state,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS,
+ NULL,
+ CIPHERS);
+}
- assert_non_null(bind->wanted_methods[SSH_CRYPT_C_S]);
- assert_non_null(bind->wanted_methods[SSH_CRYPT_S_C]);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], fips_ciphers);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], fips_ciphers);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], CIPHERS);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], CIPHERS);
- }
+static void torture_bind_config_ciphers_string(void **state)
+{
+ torture_bind_config_ciphers(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_STRING,
+ CIPHERS);
+}
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_CIPHERS2);
- assert_int_equal(rc, 0);
+static void torture_bind_config_ciphers2_file(void **state)
+{
+ torture_bind_config_ciphers(state,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS2,
+ NULL,
+ CIPHERS2);
+}
- assert_non_null(bind->wanted_methods[SSH_CRYPT_C_S]);
- assert_non_null(bind->wanted_methods[SSH_CRYPT_S_C]);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], fips_ciphers2);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], fips_ciphers2);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_C_S], CIPHERS2);
- assert_string_equal(bind->wanted_methods[SSH_CRYPT_S_C], CIPHERS2);
- }
+static void torture_bind_config_ciphers2_string(void **state)
+{
+ torture_bind_config_ciphers(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS2_STRING,
+ CIPHERS2);
+}
- SAFE_FREE(fips_ciphers);
- SAFE_FREE(fips_ciphers2);
+static void torture_bind_config_ciphers_twice_file(void **state)
+{
+ torture_bind_config_ciphers(state,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE,
+ NULL,
+ CIPHERS);
+}
+
+static void torture_bind_config_ciphers_twice_string(void **state)
+{
+ torture_bind_config_ciphers(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_STRING,
+ CIPHERS);
+}
+
+static void torture_bind_config_ciphers_twice_rec_file(void **state)
+{
+ torture_bind_config_ciphers(state,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC,
+ NULL,
+ CIPHERS);
}
-static void torture_bind_config_macs(void **state)
+static void torture_bind_config_ciphers_twice_rec_string(void **state)
+{
+ torture_bind_config_ciphers(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_CIPHERS_TWICE_REC_STRING,
+ CIPHERS);
+}
+
+static void
+torture_bind_config_macs(void **state,
+ const char *file,
+ const char *string,
+ const char *expect)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -679,59 +958,87 @@ static void torture_bind_config_macs(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_MACS);
- assert_int_equal(rc, 0);
-
- assert_non_null(bind->wanted_methods[SSH_MAC_S_C]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_S_C], MACS);
+ _parse_config(bind, file, string, SSH_OK);
assert_non_null(bind->wanted_methods[SSH_MAC_C_S]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_C_S], MACS);
-
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_MACS_TWICE);
- assert_int_equal(rc, 0);
-
assert_non_null(bind->wanted_methods[SSH_MAC_S_C]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_S_C], MACS);
+ assert_string_equal(bind->wanted_methods[SSH_MAC_C_S], expect);
+ assert_string_equal(bind->wanted_methods[SSH_MAC_S_C], expect);
+}
- assert_non_null(bind->wanted_methods[SSH_MAC_C_S]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_C_S], MACS);
+static void torture_bind_config_macs_file(void **state)
+{
+ torture_bind_config_macs(state,
+ LIBSSH_TEST_BIND_CONFIG_MACS,
+ NULL,
+ MACS);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC);
- assert_int_equal(rc, 0);
+static void torture_bind_config_macs_string(void **state)
+{
+ torture_bind_config_macs(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_MACS_STRING,
+ MACS);
+}
- assert_non_null(bind->wanted_methods[SSH_MAC_S_C]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_S_C], MACS);
+static void torture_bind_config_macs2_file(void **state)
+{
+ torture_bind_config_macs(state,
+ LIBSSH_TEST_BIND_CONFIG_MACS2,
+ NULL,
+ MACS2);
+}
- assert_non_null(bind->wanted_methods[SSH_MAC_C_S]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_C_S], MACS);
+static void torture_bind_config_macs2_string(void **state)
+{
+ torture_bind_config_macs(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_MACS2_STRING,
+ MACS2);
+}
- rc = ssh_bind_config_parse_file(bind, LIBSSH_TEST_BIND_CONFIG_MACS2);
- assert_int_equal(rc, 0);
+static void torture_bind_config_macs_twice_file(void **state)
+{
+ torture_bind_config_macs(state,
+ LIBSSH_TEST_BIND_CONFIG_MACS_TWICE,
+ NULL,
+ MACS);
+}
- assert_non_null(bind->wanted_methods[SSH_MAC_S_C]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_S_C], MACS2);
+static void torture_bind_config_macs_twice_string(void **state)
+{
+ torture_bind_config_macs(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_STRING,
+ MACS);
+}
- assert_non_null(bind->wanted_methods[SSH_MAC_C_S]);
- assert_string_equal(bind->wanted_methods[SSH_MAC_C_S], MACS2);
+static void torture_bind_config_macs_twice_rec_file(void **state)
+{
+ torture_bind_config_macs(state,
+ LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC,
+ NULL,
+ MACS);
}
-static void torture_bind_config_kexalgorithms(void **state)
+static void torture_bind_config_macs_twice_rec_string(void **state)
+{
+ torture_bind_config_macs(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_MACS_TWICE_REC_STRING,
+ MACS);
+}
+
+static void
+torture_bind_config_kexalgorithms(void **state,
+ const char *file,
+ const char *string,
+ const char *expect)
{
struct bind_st *test_state;
ssh_bind bind;
char *fips_kex = NULL;
- char *fips_kex2 = NULL;
- int rc;
-
- if (ssh_fips_mode()) {
- fips_kex = ssh_keep_fips_algos(SSH_KEX, KEXALGORITHMS);
- assert_non_null(fips_kex);
- fips_kex2 = ssh_keep_fips_algos(SSH_KEX, KEXALGORITHMS2);
- assert_non_null(fips_kex2);
- }
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -739,64 +1046,95 @@ static void torture_bind_config_kexalgorithms(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS);
- 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], fips_kex);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_KEX], KEXALGORITHMS);
+ fips_kex = ssh_keep_fips_algos(SSH_KEX, expect);
+ assert_non_null(fips_kex);
}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE);
- 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], fips_kex);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_KEX], KEXALGORITHMS);
- }
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC);
- 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], fips_kex);
+ SAFE_FREE(fips_kex);
} else {
- assert_string_equal(bind->wanted_methods[SSH_KEX], KEXALGORITHMS);
+ assert_string_equal(bind->wanted_methods[SSH_KEX], expect);
}
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2);
- 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], fips_kex2);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_KEX], KEXALGORITHMS2);
- }
+static void torture_bind_config_kexalgorithms_file(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS,
+ NULL,
+ KEXALGORITHMS);
+}
- SAFE_FREE(fips_kex);
- SAFE_FREE(fips_kex2);
+static void torture_bind_config_kexalgorithms_string(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_STRING,
+ KEXALGORITHMS);
+}
+
+static void torture_bind_config_kexalgorithms2_file(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2,
+ NULL,
+ KEXALGORITHMS2);
+}
+
+static void torture_bind_config_kexalgorithms2_string(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS2_STRING,
+ KEXALGORITHMS2);
}
-static void torture_bind_config_pubkey_accepted(void **state)
+static void torture_bind_config_kexalgorithms_twice_file(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE,
+ NULL,
+ KEXALGORITHMS);
+}
+
+static void torture_bind_config_kexalgorithms_twice_string(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_STRING,
+ KEXALGORITHMS);
+}
+
+static void torture_bind_config_kexalgorithms_twice_rec_file(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC,
+ NULL,
+ KEXALGORITHMS);
+}
+
+static void torture_bind_config_kexalgorithms_twice_rec_string(void **state)
+{
+ torture_bind_config_kexalgorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_KEXALGORITHMS_TWICE_REC_STRING,
+ KEXALGORITHMS);
+}
+
+static void
+torture_bind_config_pubkey_accepted(void **state,
+ const char *file,
+ const char *string,
+ const char *expect)
{
struct bind_st *test_state;
ssh_bind bind;
- int rc;
char *fips_pubkeys = NULL;
- char *fips_pubkeys2 = NULL;
-
- if (ssh_fips_mode()) {
- fips_pubkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, PUBKEYACCEPTEDTYPES);
- assert_non_null(fips_pubkeys);
- fips_pubkeys2 = ssh_keep_fips_algos(SSH_HOSTKEYS, PUBKEYACCEPTEDTYPES2);
- assert_non_null(fips_pubkeys2);
- }
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -804,75 +1142,111 @@ static void torture_bind_config_pubkey_accepted(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED);
- assert_int_equal(rc, 0);
- assert_non_null(bind->pubkey_accepted_key_types);
if (ssh_fips_mode()) {
- assert_string_equal(bind->pubkey_accepted_key_types, fips_pubkeys);
- } else {
- assert_string_equal(bind->pubkey_accepted_key_types, PUBKEYACCEPTEDTYPES);
+ fips_pubkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, expect);
+ assert_non_null(fips_pubkeys);
}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2);
- assert_int_equal(rc, 0);
- assert_non_null(bind->pubkey_accepted_key_types);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->pubkey_accepted_key_types, fips_pubkeys2);
- } else {
- assert_string_equal(bind->pubkey_accepted_key_types, PUBKEYACCEPTEDTYPES2);
- }
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE);
- assert_int_equal(rc, 0);
assert_non_null(bind->pubkey_accepted_key_types);
if (ssh_fips_mode()) {
assert_string_equal(bind->pubkey_accepted_key_types, fips_pubkeys);
+ SAFE_FREE(fips_pubkeys);
} else {
- assert_string_equal(bind->pubkey_accepted_key_types, PUBKEYACCEPTEDTYPES);
+ assert_string_equal(bind->pubkey_accepted_key_types, expect);
}
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC);
- assert_int_equal(rc, 0);
- assert_non_null(bind->pubkey_accepted_key_types);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->pubkey_accepted_key_types, fips_pubkeys2);
- } else {
- assert_string_equal(bind->pubkey_accepted_key_types, PUBKEYACCEPTEDTYPES2);
- }
+static void torture_bind_config_pubkey_accepted_file(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED,
+ NULL,
+ PUBKEYACCEPTEDTYPES);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN);
- assert_int_equal(rc, 0);
- assert_non_null(bind->pubkey_accepted_key_types);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->pubkey_accepted_key_types, fips_pubkeys);
- } else {
- assert_string_equal(bind->pubkey_accepted_key_types, PUBKEYACCEPTEDTYPES);
- }
+static void torture_bind_config_pubkey_accepted_string(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_STRING,
+ PUBKEYACCEPTEDTYPES);
+}
- SAFE_FREE(fips_pubkeys);
- SAFE_FREE(fips_pubkeys2);
+static void torture_bind_config_pubkey_accepted_twice_file(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE,
+ NULL,
+ PUBKEYACCEPTEDTYPES);
}
-static void torture_bind_config_hostkey_algorithms(void **state)
+static void torture_bind_config_pubkey_accepted_twice_string(void **state)
{
- struct bind_st *test_state;
- ssh_bind bind;
- int rc;
+ torture_bind_config_pubkey_accepted(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_STRING,
+ PUBKEYACCEPTEDTYPES);
+}
+
+static void torture_bind_config_pubkey_accepted_twice_rec_file(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC,
+ NULL,
+ PUBKEYACCEPTEDTYPES2);
+}
- char *fips_hostkeys = NULL;
- char *fips_hostkeys2 = NULL;
+static void torture_bind_config_pubkey_accepted_twice_rec_string(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_TWICE_REC_STRING,
+ PUBKEYACCEPTEDTYPES2);
+}
- if (ssh_fips_mode()) {
- fips_hostkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, HOSTKEYALGORITHMS);
- assert_non_null(fips_hostkeys);
- fips_hostkeys2 = ssh_keep_fips_algos(SSH_HOSTKEYS, HOSTKEYALGORITHMS2);
- assert_non_null(fips_hostkeys2);
- }
+static void torture_bind_config_pubkey_accepted_unknown_file(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN,
+ NULL,
+ PUBKEYACCEPTEDTYPES);
+}
+
+static void torture_bind_config_pubkey_accepted_unknown_string(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED_UNKNOWN_STRING,
+ PUBKEYACCEPTEDTYPES);
+}
+
+static void torture_bind_config_pubkey_accepted2_file(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2,
+ NULL,
+ PUBKEYACCEPTEDTYPES2);
+}
+
+static void torture_bind_config_pubkey_accepted2_string(void **state)
+{
+ torture_bind_config_pubkey_accepted(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_PUBKEY_ACCEPTED2_STRING,
+ PUBKEYACCEPTEDTYPES2);
+}
+
+static void
+torture_bind_config_hostkey_algorithms(void **state,
+ const char *file,
+ const char *string,
+ const char *expect)
+{
+ struct bind_st *test_state;
+ ssh_bind bind;
+ char *fips_hostkey = NULL;
assert_non_null(state);
test_state = *((struct bind_st **)state);
@@ -880,58 +1254,100 @@ static void torture_bind_config_hostkey_algorithms(void **state)
assert_non_null(test_state->bind);
bind = test_state->bind;
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS);
- assert_int_equal(rc, 0);
- assert_non_null(bind->wanted_methods[SSH_HOSTKEYS]);
if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], fips_hostkeys);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS);
+ fips_hostkey = ssh_keep_fips_algos(SSH_HOSTKEYS, expect);
+ assert_non_null(fips_hostkey);
}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2);
- assert_int_equal(rc, 0);
- assert_non_null(bind->wanted_methods[SSH_HOSTKEYS]);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], fips_hostkeys2);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS2);
- }
+ _parse_config(bind, file, string, SSH_OK);
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE);
- assert_int_equal(rc, 0);
assert_non_null(bind->wanted_methods[SSH_HOSTKEYS]);
if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], fips_hostkeys);
+ assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], fips_hostkey);
+ SAFE_FREE(fips_hostkey);
} else {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS);
+ assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], expect);
}
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC);
- assert_int_equal(rc, 0);
- assert_non_null(bind->wanted_methods[SSH_HOSTKEYS]);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], fips_hostkeys2);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS2);
- }
+static void torture_bind_config_hostkey_algorithms_file(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS,
+ NULL,
+ HOSTKEYALGORITHMS);
+}
- rc = ssh_bind_config_parse_file(bind,
- LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN);
- assert_int_equal(rc, 0);
- assert_non_null(bind->wanted_methods[SSH_HOSTKEYS]);
- if (ssh_fips_mode()) {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], fips_hostkeys);
- } else {
- assert_string_equal(bind->wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS);
- }
+static void torture_bind_config_hostkey_algorithms_string(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_STRING,
+ HOSTKEYALGORITHMS);
+}
+
+static void torture_bind_config_hostkey_algorithms_twice_file(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE,
+ NULL,
+ HOSTKEYALGORITHMS);
+}
+
+static void torture_bind_config_hostkey_algorithms_twice_string(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_STRING,
+ HOSTKEYALGORITHMS);
+}
+
+static void torture_bind_config_hostkey_algorithms_twice_rec_file(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC,
+ NULL,
+ HOSTKEYALGORITHMS2);
+}
+
+static void torture_bind_config_hostkey_algorithms_twice_rec_string(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_TWICE_REC_STRING,
+ HOSTKEYALGORITHMS2);
+}
+
+static void torture_bind_config_hostkey_algorithms2_file(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2,
+ NULL,
+ HOSTKEYALGORITHMS2);
+}
- SAFE_FREE(fips_hostkeys);
- SAFE_FREE(fips_hostkeys2);
+static void torture_bind_config_hostkey_algorithms2_string(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS2_STRING,
+ HOSTKEYALGORITHMS2);
+}
+
+static void torture_bind_config_hostkey_algorithms_unknown_file(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN,
+ NULL,
+ HOSTKEYALGORITHMS);
+}
+
+static void torture_bind_config_hostkey_algorithms_unknown_string(void **state)
+{
+ torture_bind_config_hostkey_algorithms(state,
+ NULL,
+ LIBSSH_TEST_BIND_CONFIG_HOSTKEY_ALGORITHMS_UNKNOWN_STRING,
+ HOSTKEYALGORITHMS);
}
static int assert_full_bind_config(void **state)
@@ -1076,6 +1492,23 @@ static void torture_bind_config_include_recursive(void **state)
assert_int_equal(rc, SSH_OK);
}
+static void torture_bind_config_include_recursive_loop(void **state)
+{
+ struct bind_st *test_state;
+ ssh_bind bind;
+ int rc;
+
+ assert_non_null(state);
+ test_state = *((struct bind_st **)state);
+ assert_non_null(test_state);
+ assert_non_null(test_state->bind);
+ bind = test_state->bind;
+
+ rc = ssh_bind_config_parse_file(bind,
+ LIBSSH_TEST_BIND_CONFIG_INCLUDE_RECURSIVE_LOOP);
+ assert_int_equal(rc, 0);
+}
+
/**
* @brief Verify the configuration parser does not choke on unknown
* or unsupported configuration options
@@ -1263,23 +1696,115 @@ int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
- cmocka_unit_test_setup_teardown(torture_bind_config_listen_address,
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address2_file,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_port,
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address2_string,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_hostkey,
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address_twice_file,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_twice_rec,
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_listen_address_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port2_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port2_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_port_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_twice_rec_string,
sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_separately,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_loglevel,
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel1_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel1_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_loglevel_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers2_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers2_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_ciphers_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs_file,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_ciphers,
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs_string,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_macs,
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs2_file,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms,
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs2_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_macs_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms2_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms2_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_kexalgorithms_twice_rec_string,
sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_config_full,
sshbind_setup, sshbind_teardown),
@@ -1287,6 +1812,8 @@ int torture_run_tests(void)
sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_config_include_recursive,
sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_include_recursive_loop,
+ sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_config_corner_cases,
sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_config_match_all,
@@ -1301,9 +1828,45 @@ int torture_run_tests(void)
sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_config_match_invalid,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted,
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted2_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted2_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_unknown_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_pubkey_accepted_unknown_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_twice_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_twice_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_twice_rec_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_twice_rec_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms2_file,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms2_string,
+ sshbind_setup, sshbind_teardown),
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_unknown_file,
sshbind_setup, sshbind_teardown),
- cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms,
+ cmocka_unit_test_setup_teardown(torture_bind_config_hostkey_algorithms_unknown_string,
sshbind_setup, sshbind_teardown),
};
diff --git a/tests/unittests/torture_callbacks.c b/tests/unittests/torture_callbacks.c
index 85f4d1f4..25111b2f 100644
--- a/tests/unittests/torture_callbacks.c
+++ b/tests/unittests/torture_callbacks.c
@@ -43,7 +43,7 @@ static int teardown(void **state)
}
static void torture_callbacks_size(void **state) {
- struct ssh_callbacks_struct *cb = *state;;
+ struct ssh_callbacks_struct *cb = *state;
assert_int_not_equal(cb->size, 0);
}
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 08b67deb..ebc2cdbd 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -2,11 +2,18 @@
#define LIBSSH_STATIC
+#ifndef _WIN32
+#define _POSIX_PTHREAD_SEMANTICS
+#include <pwd.h>
+#endif
+
#include "torture.h"
#include "libssh/options.h"
#include "libssh/session.h"
#include "libssh/config_parser.h"
#include "match.c"
+#include "config.c"
+#include "libssh/socket.h"
extern LIBSSH_THREAD int ssh_log_level;
@@ -34,10 +41,16 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TESTCONFIG10 "libssh_testconfig10.tmp"
#define LIBSSH_TESTCONFIG11 "libssh_testconfig11.tmp"
#define LIBSSH_TESTCONFIG12 "libssh_testconfig12.tmp"
+#define LIBSSH_TESTCONFIG14 "libssh_testconfig14.tmp"
+#define LIBSSH_TESTCONFIG15 "libssh_testconfig15.tmp"
+#define LIBSSH_TESTCONFIG16 "libssh_testconfig16.tmp"
+#define LIBSSH_TESTCONFIG17 "libssh_testconfig17.tmp"
#define LIBSSH_TESTCONFIGGLOB "libssh_testc*[36].tmp"
#define LIBSSH_TEST_PUBKEYTYPES "libssh_test_PubkeyAcceptedKeyTypes.tmp"
+#define LIBSSH_TEST_PUBKEYALGORITHMS "libssh_test_PubkeyAcceptedAlgorithms.tmp"
#define LIBSSH_TEST_NONEWLINEEND "libssh_test_NoNewLineEnd.tmp"
#define LIBSSH_TEST_NONEWLINEONELINE "libssh_test_NoNewLineOneline.tmp"
+#define LIBSSH_TEST_RECURSIVE_INCLUDE "libssh_test_recursive_include.tmp"
#define LIBSSH_TESTCONFIG_STRING1 \
"User "USERNAME"\nInclude "LIBSSH_TESTCONFIG2"\n\n"
@@ -50,7 +63,7 @@ extern LIBSSH_THREAD int ssh_log_level;
"\n\nIdentityFile "ID_FILE"\n" \
"\n\nKexAlgorithms "KEXALGORITHMS"\n" \
"\n\nHostKeyAlgorithms "HOSTKEYALGORITHMS"\n" \
- "\n\nPubkeyAcceptedTypes "PUBKEYACCEPTEDTYPES"\n" \
+ "\n\nPubkeyAcceptedAlgorithms "PUBKEYACCEPTEDTYPES"\n" \
"\n\nMACs "MACS"\n"
/* Multiple Port settings -> parsing returns early. */
@@ -168,14 +181,49 @@ extern LIBSSH_THREAD int ssh_log_level;
"Host time4\n" \
"\tRekeyLimit default 9600\n"
-/* Multiple IdentityFile settings all are aplied */
+/* Multiple IdentityFile settings all are applied */
#define LIBSSH_TESTCONFIG_STRING13 \
"IdentityFile id_rsa_one\n" \
- "IdentityFile id_ecdsa_two\n"
+ "CertificateFile id_rsa_one-cert.pub\n" \
+ "IdentityFile id_ecdsa_two\n" \
+ "CertificateFile id_ecdsa_two-cert.pub\n" \
+
+/* +,-,^ features for all supported list */
+/* kex won't work in fips */
+#define LIBSSH_TESTCONFIG_STRING14 \
+ "HostKeyAlgorithms +ssh-rsa\n" \
+ "Ciphers +aes128-cbc,aes256-cbc\n" \
+ "KexAlgorithms +diffie-hellman-group14-sha1,diffie-hellman-group1-sha1\n" \
+ "MACs +hmac-sha1,hmac-sha1-etm@openssh.com\n"
+
+/* have to be algorithms which are in the default list */
+#define LIBSSH_TESTCONFIG_STRING15 \
+ "HostKeyAlgorithms -rsa-sha2-512,rsa-sha2-256\n" \
+ "Ciphers -aes256-ctr\n" \
+ "KexAlgorithms -diffie-hellman-group18-sha512,diffie-hellman-group16-sha512\n" \
+ "MACs -hmac-sha2-256-etm@openssh.com\n"
+
+#define LIBSSH_TESTCONFIG_STRING16 \
+ "HostKeyAlgorithms ^rsa-sha2-512,rsa-sha2-256\n" \
+ "Ciphers ^aes256-cbc\n" \
+ "KexAlgorithms ^diffie-hellman-group18-sha512,diffie-hellman-group16-sha512\n" \
+ "MACs ^hmac-sha1\n"
+
+/* Connection Multiplexing */
+#define LIBSSH_TESTCONFIG_STRING17 \
+ "Host simple\n" \
+ "\tControlMaster auto\n" \
+ "\tControlPath /tmp/ssh-%r@%h:%p\n" \
+ "Host none\n" \
+ "\tControlMaster yes\n" \
+ "\tControlPath none\n"
#define LIBSSH_TEST_PUBKEYTYPES_STRING \
"PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n"
+#define LIBSSH_TEST_PUBKEYALGORITHMS_STRING \
+ "PubkeyAcceptedAlgorithms "PUBKEYACCEPTEDTYPES"\n"
+
#define LIBSSH_TEST_NONEWLINEEND_STRING \
"ConnectTimeout 30\n" \
"LogLevel DEBUG3"
@@ -183,6 +231,9 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TEST_NONEWLINEONELINE_STRING \
"ConnectTimeout 30"
+#define LIBSSH_TEST_RECURSIVE_INCLUDE_STRING \
+ "Include " LIBSSH_TEST_RECURSIVE_INCLUDE
+
/**
* @brief helper function loading configuration from either file or string
*/
@@ -224,7 +275,12 @@ static int setup_config_files(void **state)
unlink(LIBSSH_TESTCONFIG10);
unlink(LIBSSH_TESTCONFIG11);
unlink(LIBSSH_TESTCONFIG12);
+ unlink(LIBSSH_TESTCONFIG14);
+ unlink(LIBSSH_TESTCONFIG15);
+ unlink(LIBSSH_TESTCONFIG16);
+ unlink(LIBSSH_TESTCONFIG17);
unlink(LIBSSH_TEST_PUBKEYTYPES);
+ unlink(LIBSSH_TEST_PUBKEYALGORITHMS);
unlink(LIBSSH_TEST_NONEWLINEEND);
unlink(LIBSSH_TEST_NONEWLINEONELINE);
@@ -270,9 +326,22 @@ static int setup_config_files(void **state)
torture_write_file(LIBSSH_TESTCONFIG12,
LIBSSH_TESTCONFIG_STRING12);
+ /* +,-,^ feature */
+ torture_write_file(LIBSSH_TESTCONFIG14,
+ LIBSSH_TESTCONFIG_STRING14);
+ torture_write_file(LIBSSH_TESTCONFIG15,
+ LIBSSH_TESTCONFIG_STRING15);
+ torture_write_file(LIBSSH_TESTCONFIG16,
+ LIBSSH_TESTCONFIG_STRING16);
+ torture_write_file(LIBSSH_TESTCONFIG17,
+ LIBSSH_TESTCONFIG_STRING17);
+
torture_write_file(LIBSSH_TEST_PUBKEYTYPES,
LIBSSH_TEST_PUBKEYTYPES_STRING);
+ torture_write_file(LIBSSH_TEST_PUBKEYALGORITHMS,
+ LIBSSH_TEST_PUBKEYALGORITHMS_STRING);
+
torture_write_file(LIBSSH_TEST_NONEWLINEEND,
LIBSSH_TEST_NONEWLINEEND_STRING);
@@ -298,7 +367,12 @@ static int teardown_config_files(void **state)
unlink(LIBSSH_TESTCONFIG10);
unlink(LIBSSH_TESTCONFIG11);
unlink(LIBSSH_TESTCONFIG12);
+ unlink(LIBSSH_TESTCONFIG14);
+ unlink(LIBSSH_TESTCONFIG15);
+ unlink(LIBSSH_TESTCONFIG16);
+ unlink(LIBSSH_TESTCONFIG17);
unlink(LIBSSH_TEST_PUBKEYTYPES);
+ unlink(LIBSSH_TEST_PUBKEYALGORITHMS);
return 0;
}
@@ -306,6 +380,25 @@ static int teardown_config_files(void **state)
static int setup(void **state)
{
ssh_session session = NULL;
+ char *wd = NULL;
+ int verbosity;
+
+ session = ssh_new();
+
+ verbosity = torture_libssh_verbosity();
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ wd = torture_get_current_working_dir();
+ ssh_options_set(session, SSH_OPTIONS_SSH_DIR, wd);
+ free(wd);
+
+ *state = session;
+
+ return 0;
+}
+
+static int setup_no_sshdir(void **state)
+{
+ ssh_session session = NULL;
int verbosity;
session = ssh_new();
@@ -418,6 +511,22 @@ static void torture_config_include_string(void **state)
}
/**
+ * @brief tests ssh_config_parse_file with recursive Include directives from file
+ */
+static void torture_config_include_recursive_file(void **state)
+{
+ _parse_config(*state, LIBSSH_TEST_RECURSIVE_INCLUDE, NULL, SSH_OK);
+}
+
+/**
+ * @brief tests ssh_config_parse_string with Include directives from string
+ */
+static void torture_config_include_recursive_string(void **state)
+{
+ _parse_config(*state, NULL, LIBSSH_TEST_RECURSIVE_INCLUDE_STRING, SSH_OK);
+}
+
+/**
* @brief tests ssh_config_parse_file with multiple Port settings.
*/
static void torture_config_double_ports_file(void **state)
@@ -487,12 +596,14 @@ static void torture_config_new(void ** state,
assert_string_equal(session->opts.bindaddr, BIND_ADDRESS);
#ifdef WITH_ZLIB
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
- "zlib@openssh.com,zlib");
+ "zlib@openssh.com,none");
assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
- "zlib@openssh.com,zlib");
+ "zlib@openssh.com,none");
#else
- assert_null(session->opts.wanted_methods[SSH_COMP_C_S]);
- assert_null(session->opts.wanted_methods[SSH_COMP_S_C]);
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
+ "none");
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
+ "none");
#endif /* WITH_ZLIB */
assert_int_equal(session->opts.StrictHostKeyChecking, 0);
assert_int_equal(session->opts.gss_delegate_creds, 1);
@@ -593,7 +704,7 @@ static void torture_config_unknown(void **state,
/* test corner cases */
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
- "ssh -W [%h]:%p many-spaces.com");
+ "ssh -W '[%h]:%p' many-spaces.com");
assert_string_equal(session->opts.host, "equal.sign");
ret = ssh_config_parse_file(session, "/etc/ssh/ssh_config");
@@ -889,28 +1000,28 @@ static void torture_config_proxyjump(void **state,
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
_parse_config(session, file, string, SSH_OK);
- assert_string_equal(session->opts.ProxyCommand, "ssh -W [%h]:%p jumpbox");
+ assert_string_equal(session->opts.ProxyCommand, "ssh -W '[%h]:%p' jumpbox");
/* With username */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "user");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
- "ssh -l user -W [%h]:%p jumpbox");
+ "ssh -l user -W '[%h]:%p' jumpbox");
/* With port */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "port");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
- "ssh -p 2222 -W [%h]:%p jumpbox");
+ "ssh -p 2222 -W '[%h]:%p' jumpbox");
/* Two step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "two-step");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
- "ssh -l u1 -p 222 -J u2@second:33 -W [%h]:%p first");
+ "ssh -l u1 -p 222 -J u2@second:33 -W '[%h]:%p' first");
/* none */
torture_reset_config(session);
@@ -918,43 +1029,42 @@ static void torture_config_proxyjump(void **state,
_parse_config(session, file, string, SSH_OK);
assert_true(session->opts.ProxyCommand == NULL);
- /* If also ProxyCommand is specifed, the first is applied */
+ /* If also ProxyCommand is specified, the first is applied */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "only-command");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand, PROXYCMD);
- /* If also ProxyCommand is specifed, the first is applied */
+ /* If also ProxyCommand is specified, the first is applied */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "only-jump");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
- "ssh -W [%h]:%p jumpbox");
+ "ssh -W '[%h]:%p' jumpbox");
/* IPv6 address */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "ipv6");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
- "ssh -W [%h]:%p 2620:52:0::fed");
+ "ssh -W '[%h]:%p' 2620:52:0::fed");
- /* In this part, we try various other config files and strings. */
-
- /* Try to create some invalid configurations */
- /* Non-numeric port */
- config = "Host bad-port\n"
- "\tProxyJump jumpbox:22bad22\n";
+ /* Multiple @ is allowed in second jump */
+ config = "Host allowed-hostname\n"
+ "\tProxyJump localhost,user@principal.com@jumpbox:22\n";
if (file != NULL) {
torture_write_file(file, config);
} else {
string = config;
}
torture_reset_config(session);
- ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port");
- _parse_config(session, file, string, SSH_ERROR);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname");
+ _parse_config(session, file, string, SSH_OK);
+ assert_string_equal(session->opts.ProxyCommand,
+ "ssh -J user@principal.com@jumpbox:22 -W '[%h]:%p' localhost");
- /* Too many @ */
- config = "Host bad-hostname\n"
+ /* Multiple @ is allowed */
+ config = "Host allowed-hostname\n"
"\tProxyJump user@principal.com@jumpbox:22\n";
if (file != NULL) {
torture_write_file(file, config);
@@ -962,7 +1072,24 @@ static void torture_config_proxyjump(void **state,
string = config;
}
torture_reset_config(session);
- ssh_options_set(session, SSH_OPTIONS_HOST, "bad-hostname");
+ ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname");
+ _parse_config(session, file, string, SSH_OK);
+ assert_string_equal(session->opts.ProxyCommand,
+ "ssh -l user@principal.com -p 22 -W '[%h]:%p' jumpbox");
+
+ /* In this part, we try various other config files and strings. */
+
+ /* Try to create some invalid configurations */
+ /* Non-numeric port */
+ config = "Host bad-port\n"
+ "\tProxyJump jumpbox:22bad22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
+ torture_reset_config(session);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port");
_parse_config(session, file, string, SSH_ERROR);
/* Braces mismatch in hostname */
@@ -1037,18 +1164,6 @@ static void torture_config_proxyjump(void **state,
ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port-2");
_parse_config(session, file, string, SSH_ERROR);
- /* Too many @ in second jump */
- config = "Host bad-hostname\n"
- "\tProxyJump localhost,user@principal.com@jumpbox:22\n";
- if (file != NULL) {
- torture_write_file(file, config);
- } else {
- string = config;
- }
- torture_reset_config(session);
- ssh_options_set(session, SSH_OPTIONS_HOST, "bad-hostname");
- _parse_config(session, file, string, SSH_ERROR);
-
/* Braces mismatch in second jump */
config = "Host mismatch\n"
"\tProxyJump localhost,[::1:20\n";
@@ -1127,6 +1242,76 @@ static void torture_config_proxyjump_string(void **state)
}
/**
+ * @brief Verify we can parse ControlPath configuration option
+ */
+static void torture_config_control_path(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+
+ torture_reset_config(session);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
+ _parse_config(session, file, string, SSH_OK);
+ assert_string_equal(session->opts.control_path, "/tmp/ssh-%r@%h:%p");
+
+ torture_reset_config(session);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "none");
+ _parse_config(session, file, string, SSH_OK);
+ assert_null(session->opts.control_path);
+}
+
+/**
+ * @brief Verify we can parse ControlPath configuration option from string
+ */
+static void torture_config_control_path_string(void **state)
+{
+ torture_config_control_path(state, NULL, LIBSSH_TESTCONFIG_STRING17);
+}
+
+/**
+ * @brief Verify we can parse ControlPath configuration option from file
+ */
+static void torture_config_control_path_file(void **state)
+{
+ torture_config_control_path(state, LIBSSH_TESTCONFIG17, NULL);
+}
+
+/**
+ * @brief Verify we can parse ControlMaster configuration option
+ */
+static void torture_config_control_master(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+
+ torture_reset_config(session);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
+ _parse_config(session, file, string, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_AUTO);
+
+ torture_reset_config(session);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "none");
+ _parse_config(session, file, string, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_YES);
+}
+
+/**
+ * @brief Verify we can parse ControlMaster configuration option from string
+ */
+static void torture_config_control_master_string(void **state)
+{
+ torture_config_control_master(state, NULL, LIBSSH_TESTCONFIG_STRING17);
+}
+
+/**
+ * @brief Verify we can parse ControlMaster configuration option from file
+ */
+static void torture_config_control_master_file(void **state)
+{
+ torture_config_control_master(state, LIBSSH_TESTCONFIG17, NULL);
+}
+
+/**
* @brief Verify the configuration parser handles all the possible
* versions of RekeyLimit configuration option.
*/
@@ -1212,6 +1397,349 @@ static void torture_config_rekey_string(void **state)
}
/**
+ * @brief Remove substring from a string
+ *
+ * @param occurrence 0 means "remove the first occurrence"
+ * 1 means "remove the second occurrence" and so on
+ */
+static void helper_remove_substring(char *s, const char *subs, int occurrence) {
+ char *p;
+ /* remove the substring from the defaults */
+ p = strstr(s, subs);
+ assert_non_null(p);
+ /* look for second occurrence */
+ for (int i = 0; i < occurrence; i++) {
+ p = strstr(p + 1, subs);
+ assert_non_null(p);
+ }
+ memmove(p, p + strlen(subs), strlen(p + strlen(subs)) + 1);
+}
+
+/**
+ * @brief test that openssh style '+' feature works
+ */
+static void torture_config_plus(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+ const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S);
+ const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S);
+ const char *def_kex = ssh_kex_get_default_methods(SSH_KEX);
+ const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX);
+ const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S);
+ const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S);
+ const char *hostkeys_added = ",ssh-rsa";
+ const char *ciphers_added = "aes128-cbc,aes256-cbc";
+ const char *kex_added = ",diffie-hellman-group14-sha1,diffie-hellman-group1-sha1";
+ const char *mac_added = ",hmac-sha1,hmac-sha1-etm@openssh.com";
+ char *awaited = NULL;
+ int rc;
+
+ _parse_config(session, file, string, SSH_OK);
+
+ /* check hostkeys */
+ if (ssh_fips_mode()) {
+ /* ssh-rsa is disabled in fips */
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], fips_hostkeys);
+ } else {
+ awaited = calloc(strlen(def_hostkeys) + strlen(hostkeys_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_hostkeys) + strlen(hostkeys_added) + 1,
+ "%s%s", def_hostkeys, hostkeys_added);
+ assert_int_equal(rc, strlen(def_hostkeys) + strlen(hostkeys_added));
+
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited);
+ free(awaited);
+ }
+
+ /* check ciphers */
+ if (ssh_fips_mode()) {
+ /* already all supported is in the list */
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], fips_ciphers);
+ } else {
+ awaited = calloc(strlen(def_ciphers) + strlen(ciphers_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_ciphers) + strlen(ciphers_added) + 1,
+ "%s%s", def_ciphers, ciphers_added);
+ assert_int_equal(rc, strlen(def_ciphers) + strlen(ciphers_added));
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited);
+ free(awaited);
+ }
+
+ /* check kex */
+ if (ssh_fips_mode()) {
+ /* sha1 is disabled in fips */
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], fips_kex);
+ } else {
+ awaited = calloc(strlen(def_kex) + strlen(kex_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_kex) + strlen(kex_added) + 1,
+ "%s%s", def_kex, kex_added);
+ assert_int_equal(rc, strlen(def_kex) + strlen(kex_added));
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited);
+ free(awaited);
+ }
+
+ /* check mac */
+ if (ssh_fips_mode()) {
+ /* the added algos are already in the fips_methods */
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], fips_mac);
+ } else {
+ awaited = calloc(strlen(def_mac) + strlen(mac_added) + 1, 1);
+ rc = snprintf(awaited, strlen(def_mac) + strlen(mac_added) + 1,
+ "%s%s", def_mac, mac_added);
+ assert_int_equal(rc, strlen(def_mac) + strlen(mac_added));
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited);
+ free(awaited);
+ }
+}
+
+/**
+ * @brief test that openssh style '+' feature works from file
+ */
+static void torture_config_plus_file(void **state)
+{
+ torture_config_plus(state, LIBSSH_TESTCONFIG14, NULL);
+}
+
+/**
+ * @brief test that openssh style '+' feature works from string
+ */
+static void torture_config_plus_string(void **state)
+{
+ torture_config_plus(state, NULL, LIBSSH_TESTCONFIG_STRING14);
+}
+
+/**
+ * @brief test that openssh style '-' feature works from string
+ */
+static void torture_config_minus(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+ const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S);
+ const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S);
+ const char *def_kex = ssh_kex_get_default_methods(SSH_KEX);
+ const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX);
+ const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S);
+ const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S);
+ const char *hostkeys_removed = ",rsa-sha2-512,rsa-sha2-256";
+ const char *ciphers_removed = ",aes256-ctr";
+ const char *kex_removed = ",diffie-hellman-group18-sha512,diffie-hellman-group16-sha512";
+ const char *fips_kex_removed = ",diffie-hellman-group16-sha512,diffie-hellman-group18-sha512";
+ const char *mac_removed = "hmac-sha2-256-etm@openssh.com,";
+ char *awaited = NULL;
+ int rc;
+
+ _parse_config(session, file, string, SSH_OK);
+
+ /* check hostkeys */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_hostkeys) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_hostkeys) + 1, "%s", fips_hostkeys);
+ assert_int_equal(rc, strlen(fips_hostkeys));
+ } else {
+ awaited = calloc(strlen(def_hostkeys) + 1, 1);
+ rc = snprintf(awaited, strlen(def_hostkeys) + 1, "%s", def_hostkeys);
+ assert_int_equal(rc, strlen(def_hostkeys));
+ }
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, hostkeys_removed, 0);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited);
+ free(awaited);
+
+ /* check ciphers */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_ciphers) + 1, "%s", fips_ciphers);
+ assert_int_equal(rc, strlen(fips_ciphers));
+ } else {
+ awaited = calloc(strlen(def_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(def_ciphers) + 1, "%s", def_ciphers);
+ assert_int_equal(rc, strlen(def_ciphers));
+ /* remove the comma at the end of the list */
+ awaited[strlen(awaited) - 1] = '\0';
+ }
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, ciphers_removed, 0);
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited);
+ free(awaited);
+
+ /* check kex */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_kex) + 1, "%s", fips_kex);
+ assert_int_equal(rc, strlen(fips_kex));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, fips_kex_removed, 0);
+ } else {
+ awaited = calloc(strlen(def_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(def_kex) + 1, "%s", def_kex);
+ assert_int_equal(rc, strlen(def_kex));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, kex_removed, 0);
+ }
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited);
+ free(awaited);
+
+ /* check mac */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(fips_mac) + 1, 1);
+ rc = snprintf(awaited, strlen(fips_mac) + 1, "%s", fips_mac);
+ assert_int_equal(rc, strlen(fips_mac));
+ } else {
+ awaited = calloc(strlen(def_mac) + 1, 1);
+ rc = snprintf(awaited, strlen(def_mac) + 1, "%s", def_mac);
+ assert_int_equal(rc, strlen(def_mac));
+ }
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, mac_removed, 0);
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited);
+ free(awaited);
+}
+
+/**
+ * @brief test that openssh style '-' feature works from file
+ */
+static void torture_config_minus_file(void **state)
+{
+ torture_config_minus(state, LIBSSH_TESTCONFIG15, NULL);
+}
+
+/**
+ * @brief test that openssh style '-' feature works from string
+ */
+static void torture_config_minus_string(void **state)
+{
+ torture_config_minus(state, NULL, LIBSSH_TESTCONFIG_STRING15);
+}
+
+/**
+ * @brief test that openssh style '^' feature works from string
+ */
+static void torture_config_caret(void **state,
+ const char *file, const char *string)
+{
+ ssh_session session = *state;
+ const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S);
+ const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S);
+ const char *def_kex = ssh_kex_get_default_methods(SSH_KEX);
+ const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX);
+ const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S);
+ const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S);
+ const char *hostkeys_prio = "rsa-sha2-512,rsa-sha2-256";
+ const char *ciphers_prio = "aes256-cbc,";
+ const char *kex_prio = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,";
+ const char *fips_kex_prio = ",diffie-hellman-group16-sha512,diffie-hellman-group18-sha512";
+ const char *mac_prio = "hmac-sha1,";
+ char *awaited = NULL;
+ int rc;
+
+ _parse_config(session, file, string, SSH_OK);
+
+ /* check hostkeys */
+ /* +2 for the added comma and the \0 */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(hostkeys_prio) + strlen(fips_hostkeys) + 2, 1);
+ rc = snprintf(awaited, strlen(hostkeys_prio) + strlen(fips_hostkeys) + 2,
+ "%s,%s", hostkeys_prio, fips_hostkeys);
+ assert_int_equal(rc, strlen(hostkeys_prio) + strlen(fips_hostkeys) + 1);
+ } else {
+ awaited = calloc(strlen(def_hostkeys) + strlen(hostkeys_prio) + 2, 1);
+ rc = snprintf(awaited, strlen(hostkeys_prio) + strlen(def_hostkeys) + 2,
+ "%s,%s", hostkeys_prio, def_hostkeys);
+ assert_int_equal(rc, strlen(hostkeys_prio) + strlen(def_hostkeys) + 1);
+ }
+
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, hostkeys_prio, 1);
+ /* remove the comma at the end of the list */
+ awaited[strlen(awaited) - 1] = '\0';
+
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited);
+ free(awaited);
+
+ /* check ciphers */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(ciphers_prio) + strlen(fips_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(ciphers_prio) + strlen(fips_ciphers) + 1,
+ "%s%s", ciphers_prio, fips_ciphers);
+ assert_int_equal(rc, strlen(ciphers_prio) + strlen(fips_ciphers));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, ciphers_prio, 1);
+ } else {
+ /* + 2 because the '\0' and the comma */
+ awaited = calloc(strlen(ciphers_prio) + strlen(def_ciphers) + 1, 1);
+ rc = snprintf(awaited, strlen(ciphers_prio) + strlen(def_ciphers) + 1,
+ "%s%s", ciphers_prio, def_ciphers);
+ assert_int_equal(rc, strlen(ciphers_prio) + strlen(def_ciphers));
+ /* remove the comma at the end of the list */
+ awaited[strlen(awaited) - 1] = '\0';
+ }
+
+ assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited);
+ free(awaited);
+
+ /* check kex */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(kex_prio) + strlen(fips_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(kex_prio) + strlen(fips_kex) + 1,
+ "%s%s", kex_prio, fips_kex);
+ assert_int_equal(rc, strlen(kex_prio) + strlen(fips_kex));
+ /* remove the substring from the defaults */
+ /* the default list has different order of these two algos than the fips
+ * and because here is a braindead string substitution being done,
+ * change the order and remove the first occurrence of it */
+ helper_remove_substring(awaited, fips_kex_prio, 0);
+ } else {
+ awaited = calloc(strlen(kex_prio) + strlen(def_kex) + 1, 1);
+ rc = snprintf(awaited, strlen(kex_prio) + strlen(def_kex) + 1,
+ "%s%s", kex_prio, def_kex);
+ assert_int_equal(rc, strlen(def_kex) + strlen(kex_prio));
+ /* remove the substring from the defaults */
+ helper_remove_substring(awaited, kex_prio, 1);
+ }
+
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited);
+ free(awaited);
+
+ /* check mac */
+ if (ssh_fips_mode()) {
+ awaited = calloc(strlen(mac_prio) + strlen(fips_mac) + 1, 1);
+ rc = snprintf(awaited, strlen(mac_prio) + strlen(fips_mac) + 1, "%s%s", mac_prio, fips_mac);
+ assert_int_equal(rc, strlen(mac_prio) + strlen(fips_mac));
+ /* the fips list contains hmac-sha1 algo */
+ helper_remove_substring(awaited, mac_prio, 1);
+ } else {
+ awaited = calloc(strlen(mac_prio) + strlen(def_mac) + 1, 1);
+ /* the mac is not in default; it is added to the list */
+ rc = snprintf(awaited, strlen(mac_prio) + strlen(def_mac) + 1, "%s%s", mac_prio, def_mac);
+ assert_int_equal(rc, strlen(mac_prio) + strlen(def_mac));
+ }
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited);
+ free(awaited);
+}
+
+/**
+ * @brief test that openssh style '^' feature works from file
+ */
+static void torture_config_caret_file(void **state)
+{
+ torture_config_caret(state, LIBSSH_TESTCONFIG16, NULL);
+}
+
+/**
+ * @brief test that openssh style '^' feature works from string
+ */
+static void torture_config_caret_string(void **state)
+{
+ torture_config_caret(state, NULL, LIBSSH_TESTCONFIG_STRING16);
+}
+
+/**
* @brief test PubkeyAcceptedKeyTypes helper function
*/
static void torture_config_pubkeytypes(void **state,
@@ -1250,6 +1778,22 @@ static void torture_config_pubkeytypes_string(void **state)
}
/**
+ * @brief test parsing PubkeyAcceptedKAlgorithms from file
+ */
+static void torture_config_pubkeyalgorithms_file(void **state)
+{
+ torture_config_pubkeytypes(state, LIBSSH_TEST_PUBKEYALGORITHMS, NULL);
+}
+
+/**
+ * @brief test parsing PubkeyAcceptedAlgorithms from string
+ */
+static void torture_config_pubkeyalgorithms_string(void **state)
+{
+ torture_config_pubkeytypes(state, NULL, LIBSSH_TEST_PUBKEYALGORITHMS_STRING);
+}
+
+/**
* @brief Verify the configuration parser handles
* missing newline in the end
*/
@@ -1307,17 +1851,20 @@ static void torture_config_nonewlineoneline_string(void **state)
NULL, LIBSSH_TEST_NONEWLINEONELINE_STRING);
}
-/* ssh_config_get_cmd() does three things:
+/* ssh_config_get_cmd() does these two things:
* * Strips leading whitespace
- * * Terminate the characted on the end of next quotes-enclosed string
* * Terminate on the end of line
*/
static void torture_config_parser_get_cmd(void **state)
{
char *p = NULL, *tok = NULL;
char data[256];
-
- (void) state;
+#ifdef __unix__
+ FILE *outfile = NULL, *infile = NULL;
+ int pid;
+ char buffer[256] = {0};
+#endif
+ (void)state;
/* Ignore leading whitespace */
strncpy(data, " \t\t string\n", sizeof(data));
@@ -1333,14 +1880,11 @@ static void torture_config_parser_get_cmd(void **state)
assert_string_equal(tok, "string \t\t ");
assert_int_equal(*p, '\0');
- /* should drop the quotes and split them into separate arguments */
+ /* should not drop the quotes and not split them into separate arguments */
strncpy(data, "\"multi string\" something\n", sizeof(data));
p = data;
tok = ssh_config_get_cmd(&p);
- assert_string_equal(tok, "multi string");
- assert_int_equal(*p, ' ');
- tok = ssh_config_get_cmd(&p);
- assert_string_equal(tok, "something");
+ assert_string_equal(tok, "\"multi string\" something");
assert_int_equal(*p, '\0');
/* But it does not split tokens by whitespace
@@ -1350,6 +1894,46 @@ static void torture_config_parser_get_cmd(void **state)
tok = ssh_config_get_cmd(&p);
assert_string_equal(tok, "multi string something");
assert_int_equal(*p, '\0');
+
+ /* Commands in quotes are not treated special */
+ sprintf(data, "%s%s%s%s", "\"", SOURCEDIR "/tests/unittests/hello world.sh", "\" ", "\"hello libssh\"\n");
+ printf("%s\n", data);
+ p = data;
+ tok = ssh_config_get_cmd(&p);
+ assert_string_equal(tok, data);
+ assert_int_equal(*p, '\0');
+
+#ifdef __unix__
+ /* Check if the command would get correctly executed
+ * Use the script file "hello world.sh" to echo the first argument
+ * Run as <= "/workdir/hello world.sh" "hello libssh" => */
+
+ /* output to file and check wrong */
+ outfile = fopen("output.log", "a+");
+ assert_non_null(outfile);
+ printf("the tok is %s\n", tok);
+
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ } else if (pid == 0) {
+ ssh_execute_command(tok, fileno(outfile), fileno(outfile));
+ /* Does not return */
+ } else {
+ /* parent
+ * wait child process */
+ wait(NULL);
+ infile = fopen("output.log", "r");
+ assert_non_null(infile);
+ p = fgets(buffer, sizeof(buffer), infile);
+ fclose(infile);
+ remove("output.log");
+ assert_non_null(p);
+ }
+
+ fclose(outfile);
+ assert_string_equal(buffer, "hello libssh");
+#endif
}
/* ssh_config_get_token() should behave as expected
@@ -1620,12 +2204,14 @@ static void torture_config_match_pattern(void **state)
static void torture_config_identity(void **state)
{
const char *id = NULL;
+ const char *cert = NULL;
struct ssh_iterator *it = NULL;
ssh_session session = *state;
_parse_config(session, NULL, LIBSSH_TESTCONFIG_STRING13, SSH_OK);
- it = ssh_list_get_iterator(session->opts.identity);
+ /* The identities are first added to this temporary list before expanding */
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
assert_non_null(it);
id = it->data;
/* The identities are prepended to the list so we start with second one */
@@ -1635,8 +2221,166 @@ static void torture_config_identity(void **state)
assert_non_null(it);
id = it->data;
assert_string_equal(id, "id_rsa_one");
+
+ /* The certs are first added to this temporary list before expanding */
+ it = ssh_list_get_iterator(session->opts.certificate_non_exp);
+ assert_non_null(it);
+ cert = it->data;
+ /* The certs are coming as listed in the configuration file */
+ assert_string_equal(cert, "id_rsa_one-cert.pub");
+
+ it = it->next;
+ assert_non_null(it);
+ cert = it->data;
+ assert_string_equal(cert, "id_ecdsa_two-cert.pub");
+ /* and that is all */
+ assert_null(it->next);
}
+/* Make absolute path for config include
+ */
+static void torture_config_make_absolute_int(void **state, bool no_sshdir_fails)
+{
+ ssh_session session = *state;
+ char *result = NULL;
+#ifndef _WIN32
+ char h[256];
+ char *user;
+ char *home;
+
+ user = getenv("USER");
+ if (user == NULL) {
+ user = getenv("LOGNAME");
+ }
+
+ /* in certain CIs there no such variables */
+ if (!user) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw){
+ user = pw->pw_name;
+ }
+ }
+
+ home = getenv("HOME");
+ assert_non_null(home);
+#endif
+
+ /* Absolute path already -- should not change in any case */
+ result = ssh_config_make_absolute(session, "/etc/ssh/ssh_config.d/*.conf", 1);
+ assert_string_equal(result, "/etc/ssh/ssh_config.d/*.conf");
+ free(result);
+ result = ssh_config_make_absolute(session, "/etc/ssh/ssh_config.d/*.conf", 0);
+ assert_string_equal(result, "/etc/ssh/ssh_config.d/*.conf");
+ free(result);
+
+ /* Global is relative to /etc/ssh/ */
+ result = ssh_config_make_absolute(session, "ssh_config.d/test.conf", 1);
+ assert_string_equal(result, "/etc/ssh/ssh_config.d/test.conf");
+ free(result);
+ result = ssh_config_make_absolute(session, "./ssh_config.d/test.conf", 1);
+ assert_string_equal(result, "/etc/ssh/./ssh_config.d/test.conf");
+ free(result);
+
+ /* User config is relative to sshdir -- here faked to /tmp/ssh/ */
+ result = ssh_config_make_absolute(session, "my_config", 0);
+ if (no_sshdir_fails) {
+ assert_null(result);
+ } else {
+ /* The path depends on the PWD so lets skip checking the actual path here */
+ assert_non_null(result);
+ }
+ free(result);
+
+ /* User config is relative to sshdir -- here faked to /tmp/ssh/ */
+ ssh_options_set(session, SSH_OPTIONS_SSH_DIR, "/tmp/ssh");
+ result = ssh_config_make_absolute(session, "my_config", 0);
+ assert_string_equal(result, "/tmp/ssh/my_config");
+ free(result);
+
+#ifndef _WIN32
+ /* Tilde expansion works only in user config */
+ result = ssh_config_make_absolute(session, "~/.ssh/config.d/*.conf", 0);
+ snprintf(h, 256 - 1, "%s/.ssh/config.d/*.conf", home);
+ assert_string_equal(result, h);
+ free(result);
+
+ snprintf(h, 256 - 1, "~%s/.ssh/config.d/*.conf", user);
+ result = ssh_config_make_absolute(session, h, 0);
+ snprintf(h, 256 - 1, "%s/.ssh/config.d/*.conf", home);
+ assert_string_equal(result, h);
+ free(result);
+
+ /* in global config its just prefixed without expansion */
+ result = ssh_config_make_absolute(session, "~/.ssh/config.d/*.conf", 1);
+ assert_string_equal(result, "/etc/ssh/~/.ssh/config.d/*.conf");
+ free(result);
+ snprintf(h, 256 - 1, "~%s/.ssh/config.d/*.conf", user);
+ result = ssh_config_make_absolute(session, h, 1);
+ snprintf(h, 256 - 1, "/etc/ssh/~%s/.ssh/config.d/*.conf", user);
+ assert_string_equal(result, h);
+ free(result);
+#endif
+}
+
+static void torture_config_make_absolute(void **state)
+{
+ torture_config_make_absolute_int(state, 0);
+}
+
+static void torture_config_make_absolute_no_sshdir(void **state)
+{
+ torture_config_make_absolute_int(state, 1);
+}
+
+static void torture_config_parse_uri(void **state)
+{
+ char *username = NULL;
+ char *hostname = NULL;
+ char *port = NULL;
+ int rc;
+
+ (void)state; /* unused */
+
+ rc = ssh_config_parse_uri("localhost", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "localhost");
+ SAFE_FREE(hostname);
+ assert_null(port);
+
+ rc = ssh_config_parse_uri("1.2.3.4", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1.2.3.4");
+ SAFE_FREE(hostname);
+ assert_null(port);
+
+ rc = ssh_config_parse_uri("1.2.3.4:2222", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1.2.3.4");
+ SAFE_FREE(hostname);
+ assert_string_equal(port, "2222");
+ SAFE_FREE(port);
+
+ rc = ssh_config_parse_uri("[1:2:3::4]:2222", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1:2:3::4");
+ SAFE_FREE(hostname);
+ assert_string_equal(port, "2222");
+ SAFE_FREE(port);
+
+ /* do not want port */
+ rc = ssh_config_parse_uri("1:2:3::4", &username, &hostname, NULL, true);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1:2:3::4");
+ SAFE_FREE(hostname);
+
+ rc = ssh_config_parse_uri("user -name@", &username, NULL, NULL, true);
+ assert_int_equal(rc, SSH_ERROR);
+}
int torture_run_tests(void)
{
@@ -1646,6 +2390,10 @@ int torture_run_tests(void)
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_include_string,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_include_recursive_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_include_recursive_string,
+ setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_double_ports_file,
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_double_ports_string,
@@ -1674,14 +2422,38 @@ int torture_run_tests(void)
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_proxyjump_string,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_control_path_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_control_path_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_control_master_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_control_master_string,
+ setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_rekey_file,
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_rekey_string,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_plus_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_plus_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_minus_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_minus_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_caret_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_caret_string,
+ setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_file,
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_string,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_pubkeyalgorithms_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_pubkeyalgorithms_string,
+ setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_nonewlineend_file,
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_nonewlineend_string,
@@ -1698,6 +2470,12 @@ int torture_run_tests(void)
setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_identity,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_make_absolute,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_make_absolute_no_sshdir,
+ setup_no_sshdir, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_parse_uri,
+ setup, teardown),
};
diff --git a/tests/unittests/torture_keyfiles.c b/tests/unittests/torture_keyfiles.c
index 2f06e88b..2ce47b72 100644
--- a/tests/unittests/torture_keyfiles.c
+++ b/tests/unittests/torture_keyfiles.c
@@ -7,9 +7,6 @@
#include "legacy.c"
#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
-#ifdef HAVE_DSA
-#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa"
-#endif
static int setup_rsa_key(void **state)
{
@@ -29,26 +26,6 @@ static int setup_rsa_key(void **state)
return 0;
}
-#ifdef HAVE_DSA
-static int setup_dsa_key(void **state)
-{
- ssh_session session;
-
- unlink(LIBSSH_DSA_TESTKEY);
- unlink(LIBSSH_DSA_TESTKEY ".pub");
-
- torture_write_file(LIBSSH_DSA_TESTKEY,
- torture_get_testkey(SSH_KEYTYPE_DSS, 0));
- torture_write_file(LIBSSH_DSA_TESTKEY ".pub",
- torture_get_testkey_pub(SSH_KEYTYPE_DSS));
-
- session = ssh_new();
- *state = session;
-
- return 0;
-}
-#endif
-
static int setup_both_keys(void **state) {
int rc;
@@ -56,11 +33,6 @@ static int setup_both_keys(void **state) {
if (rc != 0) {
return rc;
}
-#ifdef HAVE_DSA
- ssh_free(*state);
-
- rc = setup_dsa_key(state);
-#endif
return rc;
}
@@ -74,13 +46,6 @@ static int setup_both_keys_passphrase(void **state)
torture_write_file(LIBSSH_RSA_TESTKEY ".pub",
torture_get_testkey_pub(SSH_KEYTYPE_RSA));
-#ifdef HAVE_DSA
- torture_write_file(LIBSSH_DSA_TESTKEY,
- torture_get_testkey(SSH_KEYTYPE_DSS, 1));
- torture_write_file(LIBSSH_DSA_TESTKEY ".pub",
- torture_get_testkey_pub(SSH_KEYTYPE_DSS));
-#endif
-
session = ssh_new();
*state = session;
@@ -89,10 +54,6 @@ static int setup_both_keys_passphrase(void **state)
static int teardown(void **state)
{
-#ifdef HAVE_DSA
- unlink(LIBSSH_DSA_TESTKEY);
- unlink(LIBSSH_DSA_TESTKEY ".pub");
-#endif
unlink(LIBSSH_RSA_TESTKEY);
unlink(LIBSSH_RSA_TESTKEY ".pub");
@@ -235,15 +196,6 @@ static void torture_privatekey_from_file(void **state) {
key = NULL;
}
-#ifdef HAVE_DSA
- key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, SSH_KEYTYPE_DSS, NULL);
- assert_non_null(key);
- if (key != NULL) {
- privatekey_free(key);
- key = NULL;
- }
-#endif
-
/* Test the automatic type discovery */
key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, NULL);
assert_non_null(key);
@@ -252,14 +204,6 @@ static void torture_privatekey_from_file(void **state) {
key = NULL;
}
-#ifdef HAVE_DSA
- key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, 0, NULL);
- assert_non_null(key);
- if (key != NULL) {
- privatekey_free(key);
- key = NULL;
- }
-#endif
}
/**
@@ -276,15 +220,6 @@ static void torture_privatekey_from_file_passphrase(void **state) {
key = NULL;
}
-#ifdef HAVE_DSA
- key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, SSH_KEYTYPE_DSS, TORTURE_TESTKEY_PASSWORD);
- assert_non_null(key);
- if (key != NULL) {
- privatekey_free(key);
- key = NULL;
- }
-#endif
-
/* Test the automatic type discovery */
key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, TORTURE_TESTKEY_PASSWORD);
assert_non_null(key);
@@ -293,14 +228,6 @@ static void torture_privatekey_from_file_passphrase(void **state) {
key = NULL;
}
-#ifdef HAVE_DSA
- key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, 0, TORTURE_TESTKEY_PASSWORD);
- assert_non_null(key);
- if (key != NULL) {
- privatekey_free(key);
- key = NULL;
- }
-#endif
}
int torture_run_tests(void) {
diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c
index bde0eb60..bc79fd0f 100644
--- a/tests/unittests/torture_knownhosts_parsing.c
+++ b/tests/unittests/torture_knownhosts_parsing.c
@@ -4,10 +4,10 @@
#define LIBSSH_STATIC
#include <libssh/priv.h>
-#include "torture.h"
#include "knownhosts.c"
+#include "torture.h"
#if (defined _WIN32) || (defined _WIN64)
#ifndef S_IRWXO
#define S_IRWXO 0
@@ -17,6 +17,7 @@
#endif
#endif
+#define LOCALHOST_DSS_LINE "localhost,127.0.0.1 ssh-dss AAAAB3NzaC1kc3MAAACBAIK3RTEWBw+rAPcYUM2Qq4kEw59gXpUQ/WvkdeY7QDO64MHaaorySj8xsraNudmQFh4xb/i5Q1EMnNchOFxtilfU5bUJgdTvetyZEWFL+2HxqBs8GaWRyB1vtSFAw3GO8VUEnjF844N3dNyLoc0NX8IvzwNIaQho6KTsueQlG1X9AAAAFQCXUl4a5UvElL4thi/8QlxR5PtEewAAAIBqNpl5MTBxKQu5jT0+WASa7pAqwT53ofv7ZTDIEokYRb57/nwzDgkcs1fsBRrI6eczJ/VlXWwKbsgkx2Nh3ZiWYwC+HY5uqRpDaj3HERC6LMn4dzdcl29fYeziEibCbRjJX5lZF2vIaA1Ewv8yT0UlunyHZRiyw4WlEglkf/NITAAAAIBxLsdBBXn+8qEYwWK9KT+arRqNXC/lrl0Fp5YyxGNGCv82JcnuOShGGTzhYf8AtTCY1u5oixiW9kea6KXGAKgTjfJShr7n47SZVfOPOrBT3VLhRdGGO3GblDUppzfL8wsEdoqXjzrJuxSdrGnkFu8S9QjkPn9dCtScvWEcluHqMw=="
#define LOCALHOST_RSA_LINE "localhost,127.0.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD7g+vV5cvxxGN0Ldmda4WZCPgRaxV1tV+1KRZoGUNUI61h0X4bmmGaAPRQBCz4G1d9bawqDqEqnpFWazrxBU5cQtISSjzuDJKovLGliky/ShTszee1Thszg3qVNk9gGOWj7jn/HDaOxRlp003Bp47MOdnMnK/oftllFDfY2fF5IRpE6sSIGtg2ZDtF95TV5/9W2oMOIAy8u/83tuibYlNPa1X/von5LgdaPLn6Bk16bQKIhAhlMtFZH8MBYEWe4ZtOGaSWKOsK9MM/RTMlwPi6PkfoHNl4MCMupjx+CdLXwbQEt9Ww+bBIaCui2VWBEiruVbIgJh0W2Tal0e2BzYZ What a Wurst!"
#define LOCALHOST_ECDSA_SHA1_NISTP256_LINE "localhost ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFWmI0n0Tn5+zR7pPGcKYszRbJ/T0T3QfzRBSMMiyebGKRY8tjkU5h2l/UMugzOrOyWqMGQDgQn+a0aMunhKMg0="
#define LOCALHOST_DEFAULT_ED25519 "localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA7M22fXD7OiS7kGMXP+OoIjCa+J+5sq8SgAZfIOmDgM"
@@ -144,6 +145,36 @@ close_fp:
return rc;
}
+static int setup_knownhosts_file_unsupported_type(void **state)
+{
+ char *tmp_file = NULL;
+ size_t nwritten;
+ FILE *fp = NULL;
+ int rc = 0;
+
+ tmp_file = torture_create_temp_file(TMP_FILE_NAME);
+ assert_non_null(tmp_file);
+
+ *state = tmp_file;
+
+ fp = fopen(tmp_file, "w");
+ assert_non_null(fp);
+
+ nwritten = fwrite(LOCALHOST_DSS_LINE,
+ sizeof(char),
+ strlen(LOCALHOST_DSS_LINE),
+ fp);
+ if (nwritten != strlen(LOCALHOST_DSS_LINE)) {
+ rc = -1;
+ goto close_fp;
+ }
+
+close_fp:
+ fclose(fp);
+
+ return rc;
+}
+
static int teardown_knownhosts_file(void **state)
{
char *tmp_file = *state;
@@ -396,6 +427,29 @@ static void torture_knownhosts_get_algorithms_names(void **state)
ssh_free(session);
}
+/* Do not remove this test if we completely remove DSA support! */
+static void torture_knownhosts_get_algorithms_names_unsupported(void **state)
+{
+ const char *knownhosts_file = *state;
+ ssh_session session;
+ 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_null(names);
+
+ ssh_free(session);
+}
+
static void torture_knownhosts_algorithms_wanted(void **state)
{
const char *knownhosts_file = *state;
@@ -576,7 +630,9 @@ static void torture_knownhosts_algorithms(void **state)
bool process_config = false;
const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,"
"ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
- "ecdsa-sha2-nistp256";
+ "ecdsa-sha2-nistp256,"
+ "sk-ssh-ed25519@openssh.com,"
+ "sk-ecdsa-sha2-nistp256@openssh.com";
const char *expect_fips = "rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521,"
"ecdsa-sha2-nistp384,ecdsa-sha2-nistp256";
@@ -611,7 +667,9 @@ static void torture_knownhosts_algorithms_global(void **state)
bool process_config = false;
const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,"
"ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
- "ecdsa-sha2-nistp256";
+ "ecdsa-sha2-nistp256,"
+ "sk-ssh-ed25519@openssh.com,"
+ "sk-ecdsa-sha2-nistp256@openssh.com";
const char *expect_fips = "rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521,"
"ecdsa-sha2-nistp384,ecdsa-sha2-nistp256";
@@ -660,6 +718,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names,
setup_knownhosts_file,
teardown_knownhosts_file),
+ cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names_unsupported,
+ setup_knownhosts_file_unsupported_type,
+ teardown_knownhosts_file),
cmocka_unit_test_setup_teardown(torture_knownhosts_algorithms_wanted,
setup_knownhosts_file,
teardown_knownhosts_file),
diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c
index c7b99b5d..5a3b4805 100644
--- a/tests/unittests/torture_misc.c
+++ b/tests/unittests/torture_misc.c
@@ -4,8 +4,9 @@
#include <unistd.h>
#endif
#include <sys/types.h>
-#ifndef _WIN32
+#include <fcntl.h>
+#ifndef _WIN32
#define _POSIX_PTHREAD_SEMANTICS
#include <pwd.h>
#endif
@@ -13,11 +14,18 @@
#define LIBSSH_STATIC
#include <libssh/priv.h>
-#include "torture.h"
#include "misc.c"
+#include "torture.h"
#include "error.c"
+#ifdef _WIN32
+#include <netioapi.h>
+#else
+#include <net/if.h>
+#endif
+
#define TORTURE_TEST_DIR "/usr/local/bin/truc/much/.."
+#define TORTURE_IPV6_LOCAL_LINK "fe80::98e1:82ff:fe8d:28b3%%%s"
const char template[] = "temp_dir_XXXXXX";
@@ -168,17 +176,25 @@ static void torture_path_expand_tilde_unix(void **state) {
static void torture_path_expand_escape(void **state) {
ssh_session session = *state;
- const char *s = "%d/%h/by/%r";
+ const char *s = "%d/%h/%p/by/%r";
char *e;
session->opts.sshdir = strdup("guru");
session->opts.host = strdup("meditation");
+ session->opts.port = 0;
session->opts.username = strdup("root");
e = ssh_path_expand_escape(session, s);
assert_non_null(e);
- assert_string_equal(e, "guru/meditation/by/root");
- free(e);
+ assert_string_equal(e, "guru/meditation/22/by/root");
+ ssh_string_free_char(e);
+
+ session->opts.port = 222;
+
+ e = ssh_path_expand_escape(session, s);
+ assert_non_null(e);
+ assert_string_equal(e, "guru/meditation/222/by/root");
+ ssh_string_free_char(e);
}
static void torture_path_expand_known_hosts(void **state) {
@@ -724,6 +740,395 @@ static void torture_ssh_strreplace(void **state)
assert_null(replaced_string);
}
+static void torture_ssh_strerror(void **state)
+{
+ char buf[1024];
+ size_t bufflen = sizeof(buf);
+ char *out = NULL;
+
+ (void) state;
+
+ out = ssh_strerror(ENOENT, buf, 1); /* too short */
+ assert_string_equal(out, "\0");
+
+ out = ssh_strerror(256, buf, bufflen); /* unknown error code */
+ /* This error is always different:
+ * Freebd: "Unknown error: 256"
+ * MinGW/Win: "Unknown error"
+ * Linux/glibc: "Unknown error 256"
+ * Alpine/musl: "No error information"
+ */
+ assert_non_null(out);
+
+ out = ssh_strerror(ENOMEM, buf, bufflen);
+ /* This actually differs too for glibc/musl:
+ * musl: "Out of memory"
+ * everything else: "Cannot allocate memory"
+ */
+ assert_non_null(out);
+}
+
+static void torture_ssh_readn(void **state)
+{
+ char *write_buf = NULL, *read_buf = NULL, *file_path = NULL;
+ size_t data_len = 10 * 1024 * 1024;
+ size_t read_buf_size = data_len + 1024;
+ size_t i, total_bytes_written = 0;
+
+ const char *file_template = "libssh_torture_ssh_readn_test_XXXXXX";
+
+ off_t off;
+ ssize_t bytes_read, bytes_written;
+ int fd, rc, flags;
+
+ (void)state;
+
+ write_buf = malloc(data_len);
+ assert_non_null(write_buf);
+
+ /* Fill the write buffer with random data */
+ for (i = 0; i < data_len; ++i) {
+ rc = rand();
+ write_buf[i] = (char)(rc & 0xff);
+ }
+
+ /*
+ * The read buffer's size is intentionally kept larger than data_len.
+ *
+ * This is done so that we are able to test the scenario when the user
+ * requests ssh_readn() to read more bytes than the number of bytes present
+ * in the file.
+ *
+ * If the read buffer's size is kept same as data_len and if the test
+ * requests ssh_readn() to read more than data_len bytes starting from file
+ * offset 0, it will lead to a valgrind failure as in this case, ssh_readn()
+ * will pass an unallocated memory address in the last call it makes to
+ * read().
+ */
+ read_buf = malloc(read_buf_size);
+ assert_non_null(read_buf);
+
+ file_path = torture_create_temp_file(file_template);
+ assert_non_null(file_path);
+
+ /* Open a file for reading and writing */
+ flags = O_RDWR;
+#ifdef _WIN32
+ flags |= O_BINARY;
+#endif
+
+ fd = open(file_path, flags, 0);
+ assert_int_not_equal(fd, -1);
+
+ /* Write the data present in the write buffer to the file */
+ do {
+ bytes_written = write(fd,
+ write_buf + total_bytes_written,
+ data_len - total_bytes_written);
+
+ if (bytes_written == -1 && errno == EINTR) {
+ continue;
+ }
+
+ assert_int_not_equal(bytes_written, -1);
+ total_bytes_written += bytes_written;
+ } while (total_bytes_written < data_len);
+
+ /* Seek to the start of the file */
+ off = lseek(fd, 0, SEEK_SET);
+ assert_int_not_equal(off, -1);
+
+ bytes_read = ssh_readn(fd, read_buf, data_len);
+ assert_int_equal(bytes_read, data_len);
+
+ /*
+ * Ensure that the data stored in the read buffer is same as the data
+ * present in the file and not some garbage.
+ */
+ assert_memory_equal(read_buf, write_buf, data_len);
+
+ /*
+ * Ensure that the file offset is on EOF and requesting to read more leads
+ * to 0 bytes getting read.
+ */
+ off = lseek(fd, 0, SEEK_CUR);
+ assert_int_equal(off, data_len);
+
+ bytes_read = ssh_readn(fd, read_buf, data_len);
+ assert_int_equal(bytes_read, 0);
+
+ /* Try to read more bytes than what are present in the file */
+ off = lseek(fd, 0, SEEK_SET);
+ assert_int_not_equal(off, -1);
+
+ bytes_read = ssh_readn(fd, read_buf, read_buf_size);
+ assert_int_equal(bytes_read, data_len);
+
+ /*
+ * Ensure that the data stored in the read buffer is same as the data
+ * present in the file and not some garbage.
+ */
+ assert_memory_equal(read_buf, write_buf, data_len);
+
+ /* Negative tests start */
+ bytes_read = ssh_readn(-2, read_buf, data_len);
+ assert_int_equal(bytes_read, -1);
+
+ bytes_read = ssh_readn(fd, NULL, data_len);
+ assert_int_equal(bytes_read, -1);
+
+ bytes_read = ssh_readn(fd, read_buf, 0);
+ assert_int_equal(bytes_read, -1);
+
+ /* Clean up */
+ rc = close(fd);
+ assert_int_equal(rc, 0);
+
+ rc = unlink(file_path);
+ assert_int_equal(rc, 0);
+
+ free(file_path);
+ free(read_buf);
+ free(write_buf);
+}
+
+static void torture_ssh_writen(void **state)
+{
+ char *write_buf = NULL, *read_buf = NULL, *file_path = NULL;
+ const char *file_template = "libssh_torture_ssh_writen_test_XXXXXX";
+
+ size_t data_len = 10 * 1024 * 1024;
+ size_t i, total_bytes_read = 0;
+ ssize_t bytes_written, bytes_read;
+ off_t off;
+ int rc, fd, flags;
+
+ (void)state;
+
+ write_buf = malloc(data_len);
+ assert_non_null(write_buf);
+
+ /* Fill the write buffer with random data */
+ for (i = 0; i < data_len; ++i) {
+ rc = rand();
+ write_buf[i] = (char)(rc & 0xff);
+ }
+
+ read_buf = malloc(data_len);
+ assert_non_null(read_buf);
+
+ file_path = torture_create_temp_file(file_template);
+ assert_non_null(file_path);
+
+ /* Open a file for reading and writing */
+ flags = O_RDWR;
+#ifdef _WIN32
+ flags |= O_BINARY;
+#endif
+
+ fd = open(file_path, flags, 0);
+ assert_int_not_equal(fd, -1);
+
+ /* Write the data present in the write buffer to the file */
+ bytes_written = ssh_writen(fd, write_buf, data_len);
+ assert_int_equal(bytes_written, data_len);
+
+ /*
+ * Ensure that the file offset is incremented by the number of bytes
+ * written.
+ */
+ off = lseek(fd, 0, SEEK_CUR);
+ assert_int_equal(off, data_len);
+
+ /*
+ * Ensure that the data present in the write buffer has been written to the
+ * file and not some garbage.
+ */
+ off = lseek(fd, 0, SEEK_SET);
+ assert_int_not_equal(off, -1);
+
+ do {
+ bytes_read = read(fd,
+ read_buf + total_bytes_read,
+ data_len - total_bytes_read);
+
+ if (bytes_read == -1 && errno == EINTR) {
+ continue;
+ }
+
+ assert_int_not_equal(bytes_read, -1);
+ assert_int_not_equal(bytes_read, 0);
+
+ total_bytes_read += bytes_read;
+ } while (total_bytes_read < data_len);
+
+ assert_memory_equal(write_buf, read_buf, data_len);
+
+ /* Negative tests start */
+ bytes_written = ssh_writen(-3, write_buf, data_len);
+ assert_int_equal(bytes_written, -1);
+
+ bytes_written = ssh_writen(fd, NULL, data_len);
+ assert_int_equal(bytes_written, -1);
+
+ bytes_written = ssh_writen(fd, write_buf, 0);
+ assert_int_equal(bytes_written, -1);
+
+ /* Clean up */
+ rc = close(fd);
+ assert_int_equal(rc, 0);
+
+ rc = unlink(file_path);
+ assert_int_equal(rc, 0);
+
+ free(file_path);
+ free(read_buf);
+ free(write_buf);
+}
+
+static void torture_ssh_check_hostname_syntax(void **state)
+{
+ int rc;
+ (void)state;
+
+ rc = ssh_check_hostname_syntax("duckduckgo.com");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("www.libssh.org");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("Some-Thing.com");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123.ok");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("lavabo-inter.innocentes-manus-meas");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("localhost");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("a");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("a-0.b-b");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("libssh.");
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_check_hostname_syntax(NULL);
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("/");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("@");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("[");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("`");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("{");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("&");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("|");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("\"");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("`");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax(" ");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("*the+giant&\"rooks\".c0m");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("!www.libssh.org");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("--.--");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("libssh.a234567890123456789012345678901234567890123456789012345678901234");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("libssh.a234567890123456789012345678901234567890123456789012345678901234.a234567890123456789012345678901234567890123456789012345678901234");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("libssh-");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("fe80::9656:d028:8652:66b6");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax(".");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("..");
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_ssh_check_username_syntax(void **state) {
+ int rc;
+ (void)state;
+
+ rc = ssh_check_username_syntax("username");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("Alice");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("Alice and Bob");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("n4me?");
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_check_username_syntax("alice&bob");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("backslash\\");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("&var|()us\"<ha`r{}'");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax(" -");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("me and -");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("los -santos");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("- who?");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax(NULL);
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("");
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_ssh_is_ipaddr(void **state) {
+ int rc;
+ char *interf = malloc(64);
+ char *test_interf = malloc(128);
+ (void)state;
+
+ assert_non_null(interf);
+ assert_non_null(test_interf);
+ rc = ssh_is_ipaddr("201.255.3.69");
+ assert_int_equal(rc, 1);
+ rc = ssh_is_ipaddr("::1");
+ assert_int_equal(rc, 1);
+ rc = ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assert_int_equal(rc, 1);
+ if_indextoname(1, interf);
+ assert_non_null(interf);
+ rc = sprintf(test_interf, TORTURE_IPV6_LOCAL_LINK, interf);
+ /* the "%%s" is not written */
+ assert_int_equal(rc, strlen(interf) + strlen(TORTURE_IPV6_LOCAL_LINK) - 3);
+ rc = ssh_is_ipaddr(test_interf);
+ assert_int_equal(rc, 1);
+ free(interf);
+ free(test_interf);
+
+ rc = ssh_is_ipaddr("..");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr(":::");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("1.1.1.1.1");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("1.1");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("caesar");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("::xa:1");
+ assert_int_equal(rc, 0);
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -747,6 +1152,12 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_mkdirs),
cmocka_unit_test(torture_ssh_quote_file_name),
cmocka_unit_test(torture_ssh_strreplace),
+ cmocka_unit_test(torture_ssh_strerror),
+ cmocka_unit_test(torture_ssh_readn),
+ cmocka_unit_test(torture_ssh_writen),
+ cmocka_unit_test(torture_ssh_check_hostname_syntax),
+ cmocka_unit_test(torture_ssh_check_username_syntax),
+ cmocka_unit_test(torture_ssh_is_ipaddr),
};
ssh_init();
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index d2ceedd5..e41c15da 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -18,9 +18,6 @@
#include <libssh/bind.h>
#define LIBSSH_CUSTOM_BIND_CONFIG_FILE "my_bind_config"
#endif
-#ifdef HAVE_DSA
-#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa"
-#endif
#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
#define LIBSSH_ED25519_TESTKEY "libssh_testkey.id_ed25519"
#ifdef HAVE_ECC
@@ -60,12 +57,38 @@ static void torture_options_set_host(void **state) {
assert_non_null(session->opts.host);
assert_string_equal(session->opts.host, "localhost");
+ /* IPv4 address */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.1.1.1");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "127.1.1.1");
+ assert_null(session->opts.username);
+
+ /* IPv6 address */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "::1");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "::1");
+ assert_null(session->opts.username);
+
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation");
assert_true(rc == 0);
assert_non_null(session->opts.host);
assert_string_equal(session->opts.host, "meditation");
assert_non_null(session->opts.username);
assert_string_equal(session->opts.username, "guru");
+
+ /* more @ in uri is OK -- it should go to the username */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "at@login@hostname");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "hostname");
+ assert_non_null(session->opts.username);
+ assert_string_equal(session->opts.username, "at@login");
+
+ /* disallow metacharacters in the username */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "shallN()tP4ss -@hostname");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
}
static void torture_options_set_ciphers(void **state) {
@@ -223,7 +246,7 @@ static void torture_options_set_pubkey_accepted_types(void **state) {
/* simulate the SHA2 extension was negotiated */
session->extensions = SSH_EXT_SIG_RSA_SHA256;
- /* previous configuration did not list the SHA2 extension algoritms, so
+ /* previous configuration did not list the SHA2 extension algorithms, so
* it should not be used */
type = ssh_key_type_to_hash(session, SSH_KEYTYPE_RSA);
assert_int_equal(type, SSH_DIGEST_SHA1);
@@ -312,6 +335,7 @@ static void torture_options_set_port(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_PORT_STR, "five");
assert_true(rc == -1);
+ assert_int_not_equal(session->opts.port, 0);
rc = ssh_options_set(session, SSH_OPTIONS_PORT, NULL);
assert_true(rc == -1);
@@ -372,6 +396,9 @@ static void torture_options_set_user(void **state) {
assert_true(rc == 0);
#endif /* _WIN32 */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, "&shallN()tP4ss");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
rc = ssh_options_set(session, SSH_OPTIONS_USER, "guru");
assert_true(rc == 0);
assert_string_equal(session->opts.username, "guru");
@@ -399,12 +426,12 @@ static void torture_options_set_identity(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1");
assert_true(rc == 0);
- assert_string_equal(session->opts.identity->root->data, "identity1");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "identity1");
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2");
assert_true(rc == 0);
- assert_string_equal(session->opts.identity->root->data, "identity2");
- assert_string_equal(session->opts.identity->root->next->data, "identity1");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "identity2");
+ assert_string_equal(session->opts.identity_non_exp->root->next->data, "identity1");
}
static void torture_options_get_identity(void **state) {
@@ -422,7 +449,7 @@ static void torture_options_get_identity(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2");
assert_int_equal(rc, SSH_OK);
- assert_string_equal(session->opts.identity->root->data, "identity2");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "identity2");
rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_non_null(identity);
@@ -527,6 +554,74 @@ static void torture_options_proxycommand(void **state) {
assert_null(session->opts.ProxyCommand);
}
+static void torture_options_control_master (void **state) {
+ ssh_session session = *state;
+ int rc, val = SSH_CONTROL_MASTER_NO;
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_CONTROL_MASTER,
+ &val);
+ assert_int_equal(rc, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_NO);
+
+ val = SSH_CONTROL_MASTER_AUTO;
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_CONTROL_MASTER,
+ &val);
+ assert_int_equal(rc, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_AUTO);
+
+ val = SSH_CONTROL_MASTER_YES;
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_CONTROL_MASTER,
+ &val);
+ assert_int_equal(rc, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_YES);
+
+ val = SSH_CONTROL_MASTER_ASK;
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_CONTROL_MASTER,
+ &val);
+ assert_int_equal(rc, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_ASK);
+
+ val = SSH_CONTROL_MASTER_AUTOASK;
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_CONTROL_MASTER,
+ &val);
+ assert_int_equal(rc, SSH_OK);
+ assert_int_equal(session->opts.control_master, SSH_CONTROL_MASTER_AUTOASK);
+
+ val = 255;
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_CONTROL_MASTER,
+ &val);
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_options_control_path(void **state) {
+ ssh_session session = *state;
+ char *str = NULL;
+ int rc;
+
+ /* Set Control Path */
+ rc = ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, "/tmp/ssh-%r@%h:%p");
+ assert_int_equal(rc, 0);
+
+ assert_string_equal(session->opts.control_path, "/tmp/ssh-%r@%h:%p");
+
+ rc = ssh_options_get(session, SSH_OPTIONS_CONTROL_PATH, &str);
+ assert_int_equal(rc, 0);
+ assert_string_equal(str, "/tmp/ssh-%r@%h:%p");
+
+ /* Disable Multiplexing */
+ rc = ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, "none");
+ assert_int_equal(rc, 0);
+
+ assert_null(session->opts.control_path);
+ SSH_STRING_FREE_CHAR(str);
+}
+
static void torture_options_config_host(void **state) {
ssh_session session = *state;
FILE *config = NULL;
@@ -827,6 +922,9 @@ static void torture_options_copy(void **state)
config = fopen("test_config", "w");
assert_non_null(config);
fputs("IdentityFile ~/.ssh/id_ecdsa\n"
+ "IdentityFile ~/.ssh/my_rsa\n"
+ "CertificateFile ~/.ssh/my_rsa-cert.pub\n"
+ "CertificateFile ~/.ssh/id_ecdsa-cert.pub\n"
"User tester\n"
"Hostname example.com\n"
"BindAddress 127.0.0.2\n"
@@ -837,8 +935,10 @@ static void torture_options_copy(void **state)
"MACs hmac-sha2-256\n"
"HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp521\n"
"Compression yes\n"
- "PubkeyAcceptedTypes ssh-ed25519,ecdsa-sha2-nistp521\n"
+ "PubkeyAcceptedAlgorithms ssh-ed25519,ecdsa-sha2-nistp521\n"
"ProxyCommand nc 127.0.0.10 22\n"
+ "ControlMaster ask\n"
+ "ControlPath /tmp/ssh-%r@%h:%p\n"
/* ops.custombanner */
"ConnectTimeout 42\n"
"Port 222\n"
@@ -860,9 +960,22 @@ static void torture_options_copy(void **state)
assert_non_null(new);
/* Check the identities match */
- it = ssh_list_get_iterator(session->opts.identity);
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
assert_non_null(it);
- it2 = ssh_list_get_iterator(new->opts.identity);
+ it2 = ssh_list_get_iterator(new->opts.identity_non_exp);
+ assert_non_null(it2);
+ while (it != NULL && it2 != NULL) {
+ assert_string_equal(it->data, it2->data);
+ it = it->next;
+ it2 = it2->next;
+ }
+ assert_null(it);
+ assert_null(it2);
+
+ /* Check the certificates match */
+ it = ssh_list_get_iterator(session->opts.certificate_non_exp);
+ assert_non_null(it);
+ it2 = ssh_list_get_iterator(new->opts.certificate_non_exp);
assert_non_null(it2);
while (it != NULL && it2 != NULL) {
assert_string_equal(it->data, it2->data);
@@ -890,10 +1003,12 @@ static void torture_options_copy(void **state)
assert_string_equal(session->opts.pubkey_accepted_types,
new->opts.pubkey_accepted_types);
assert_string_equal(session->opts.ProxyCommand, new->opts.ProxyCommand);
+ assert_string_equal(session->opts.control_path, new->opts.control_path);
/* TODO custombanner */
assert_int_equal(session->opts.timeout, new->opts.timeout);
assert_int_equal(session->opts.timeout_usec, new->opts.timeout_usec);
assert_int_equal(session->opts.port, new->opts.port);
+ assert_int_equal(session->opts.control_master, new->opts.control_master);
assert_int_equal(session->opts.StrictHostKeyChecking,
new->opts.StrictHostKeyChecking);
assert_int_equal(session->opts.compressionlevel,
@@ -911,6 +1026,34 @@ static void torture_options_copy(void **state)
sizeof(session->opts.options_seen));
ssh_free(new);
+
+ /* test if ssh_options_apply was called before ssh_options_copy
+ * the opts.identity list gets copied (percent expanded list) */
+ rv = ssh_options_apply(session);
+ assert_ssh_return_code(session, rv);
+
+ rv = ssh_options_copy(session, &new);
+ assert_ssh_return_code(session, rv);
+ assert_non_null(new);
+
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
+ assert_null(it);
+ it2 = ssh_list_get_iterator(new->opts.identity_non_exp);
+ assert_null(it2);
+
+ it = ssh_list_get_iterator(session->opts.identity);
+ assert_non_null(it);
+ it2 = ssh_list_get_iterator(new->opts.identity);
+ assert_non_null(it2);
+ while (it != NULL && it2 != NULL) {
+ assert_string_equal(it->data, it2->data);
+ it = it->next;
+ it2 = it2->next;
+ }
+ assert_null(it);
+ assert_null(it2);
+
+ ssh_free(new);
}
#define EXECUTABLE_NAME "test-exec"
@@ -923,8 +1066,6 @@ static void torture_options_getopt(void **state)
"-vv", "-v", "-r", "-c", "aes128-ctr",
"-i", "id_rsa", "-C", "-2", "-1", NULL};
int argc = sizeof(argv)/sizeof(char *) - 1;
- const char *argv_invalid[] = {EXECUTABLE_NAME, "-r", "-d", NULL};
-
previous_level = ssh_get_log_level();
/* Test with all the supported options */
@@ -949,11 +1090,18 @@ static void torture_options_getopt(void **state)
"aes128-ctr");
assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_S_C],
"aes128-ctr");
- assert_string_equal(session->opts.identity->root->data, "id_rsa");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "id_rsa");
+#ifdef WITH_ZLIB
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
+ "zlib@openssh.com,none");
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
+ "zlib@openssh.com,none");
+#else
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
- "zlib@openssh.com,zlib");
+ "none");
assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
- "zlib@openssh.com,zlib");
+ "none");
+#endif
/* -1 and -2 are noop */
@@ -1007,36 +1155,39 @@ static void torture_options_getopt(void **state)
assert_string_equal(argv[4], "hmac-sha1");
assert_string_equal(argv[5], "example.com");
-
- /* Invalid configuration combination -d and -r (for some reason?) */
- argc = 3;
- rc = ssh_options_getopt(session, &argc, (char **)argv_invalid);
- assert_ssh_return_code_equal(session, rc, SSH_ERROR);
- assert_int_equal(argc, 3);
- assert_string_equal(argv_invalid[0], EXECUTABLE_NAME);
- assert_string_equal(argv_invalid[1], "-r");
- assert_string_equal(argv_invalid[2], "-d");
-
-
/* Corner case: only one argument */
argv[1] = "-C";
argv[2] = NULL;
argc = 2;
rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
assert_ssh_return_code(session, rc);
+#ifdef WITH_ZLIB
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
+ "none,zlib@openssh.com");
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
+ "none,zlib@openssh.com");
+#else
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
"none");
assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
"none");
+#endif
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code(session, rc);
assert_int_equal(argc, 1);
assert_string_equal(argv[0], EXECUTABLE_NAME);
+#ifdef WITH_ZLIB
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
- "zlib@openssh.com,zlib");
+ "zlib@openssh.com,none");
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
+ "zlib@openssh.com,none");
+#else
+ assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
+ "none");
assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
- "zlib@openssh.com,zlib");
+ "none");
+#endif
/* Corner case: only hostname is not parsed */
argv[1] = "example.com";
@@ -1059,6 +1210,402 @@ static void torture_options_getopt(void **state)
#endif /* _NSC_VER */
}
+static void torture_options_plus_sign(void **state)
+{
+ ssh_session session = *state;
+ int rc;
+ const char *def_host_alg, *alg, *algs;
+ char *awaited;
+ size_t alg_len, algs_len;
+
+ if (ssh_fips_mode()) {
+ alg = ",rsa-sha2-512-cert-v01@openssh.com";
+ algs = ",rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521";
+ def_host_alg = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ } else {
+ alg = ",ssh-rsa";
+ algs = ",ssh-rsa,ssh-rsa-cert-v01@openssh.com";
+ def_host_alg = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ }
+ alg_len = strlen(alg);
+ algs_len = strlen(algs);
+
+ /* in fips mode, the default list is the available list, which means
+ * we can't append anything because everything enabled is already
+ * included */
+ if (ssh_fips_mode()) {
+ awaited = strdup(def_host_alg);
+ assert_non_null(awaited);
+ } else {
+ awaited = calloc(strlen(def_host_alg) + alg_len + 1, 1);
+ assert_non_null(awaited);
+
+ memcpy(awaited, def_host_alg, strlen(def_host_alg));
+ memcpy(awaited+strlen(def_host_alg), alg, alg_len);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+rsa-sha2-512-cert-v01@openssh.com");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+ssh-rsa");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ if (!ssh_fips_mode()) {
+ /* different algorithm list is used here */
+ free(awaited);
+
+ awaited = calloc(strlen(def_host_alg) + algs_len + 1, 1);
+ assert_non_null(awaited);
+ memcpy(awaited, def_host_alg, strlen(def_host_alg));
+ memcpy(awaited+strlen(def_host_alg), algs, algs_len);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "+rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "+ssh-rsa,ssh-rsa-cert-v01@openssh.com");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+blablabla");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ free(awaited);
+}
+
+static void torture_options_minus_sign(void **state)
+{
+ ssh_session session = *state;
+ int rc;
+ const char *def_host_alg, *alg, *algs;
+ char *awaited, *p;
+ size_t alg_len, algs_len;
+
+ if (ssh_fips_mode()) {
+ alg = "rsa-sha2-512-cert-v01@openssh.com,";
+ algs = "rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521,";
+ def_host_alg = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ } else {
+ alg = "ssh-ed25519,";
+ algs = "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,";
+ def_host_alg = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ }
+ alg_len = strlen(alg);
+ algs_len = strlen(algs);
+
+ awaited = calloc(strlen(def_host_alg) + 1, 1);
+ assert_non_null(awaited);
+
+ memcpy(awaited, def_host_alg, strlen(def_host_alg));
+ p = strstr(awaited, alg);
+ assert_non_null(p);
+ memmove(p, p+alg_len, strlen(p + alg_len) + 1);
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-rsa-sha2-512-cert-v01@openssh.com");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-ssh-ed25519");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ p = strstr(awaited, algs);
+ assert_non_null(p);
+ memmove(p, p+algs_len, strlen(p + algs_len) + 1);
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "-blablabla");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ free(awaited);
+}
+
+static void torture_options_caret_sign(void **state)
+{
+ ssh_session session = *state;
+ int rc;
+ const char *def_host_alg, *alg, *algs;
+ size_t alg_len, algs_len;
+ char *awaited, *p;
+
+ if (ssh_fips_mode()) {
+ alg = "rsa-sha2-512-cert-v01@openssh.com,";
+ algs = "rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521,";
+ def_host_alg = ssh_kex_get_fips_methods(SSH_HOSTKEYS);
+ } else {
+ alg = "ssh-rsa,";
+ algs = "ssh-rsa,ssh-rsa-cert-v01@openssh.com,";
+ def_host_alg = ssh_kex_get_default_methods(SSH_HOSTKEYS);
+ }
+ alg_len = strlen(alg);
+ algs_len = strlen(algs);
+
+ awaited = calloc(strlen(def_host_alg) + alg_len + 1, 1);
+ assert_non_null(awaited);
+
+ memcpy(awaited, alg, alg_len);
+ memcpy(awaited+alg_len, def_host_alg, strlen(def_host_alg));
+ if (ssh_fips_mode()) {
+ p = strstr(awaited, alg);
+ /* look for second occurrence */
+ p = strstr(p+1, algs);
+ memmove(p, p+alg_len, strlen(p + alg_len) + 1);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^rsa-sha2-512-cert-v01@openssh.com");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^ssh-rsa");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+ /* different algorithm list is used here */
+ free(awaited);
+
+ awaited = calloc(strlen(def_host_alg) + algs_len + 1, 1);
+ assert_non_null(awaited);
+ memcpy(awaited, algs, algs_len);
+ memcpy(awaited+algs_len, def_host_alg, strlen(def_host_alg));
+ if (ssh_fips_mode()) {
+ p = strstr(awaited, algs);
+ /* look for second occurrence */
+ p = strstr(p+1, algs);
+ memmove(p, p+algs_len, strlen(p + algs_len) + 1);
+ }
+
+ if (ssh_fips_mode()) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "^rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ecdsa-sha2-nistp521");
+ } else {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS,
+ "^ssh-rsa,ssh-rsa-cert-v01@openssh.com");
+ }
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ awaited);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "^blablabla");
+ assert_ssh_return_code(session, rc);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ def_host_alg);
+
+ free(awaited);
+}
+
+static void torture_options_apply (void **state) {
+ ssh_session session = *state;
+ struct ssh_list *awaited_list = NULL;
+ struct ssh_iterator *it1 = NULL, *it2 = NULL;
+ char *id = NULL;
+ int rc;
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_KNOWNHOSTS,
+ "%%d/.ssh/known_hosts");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
+ "/etc/%%u/libssh/known_hosts");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_PROXYCOMMAND,
+ "exec echo \"Hello libssh %%d!\"");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "%%d/do_not_expand");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_apply(session);
+ assert_ssh_return_code(session, rc);
+
+ /* check that the values got expanded */
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND);
+ assert_true(ssh_list_count(session->opts.identity_non_exp) == 0);
+ assert_true(ssh_list_count(session->opts.identity) > 0);
+
+ /* should not change anything calling it again */
+ rc = ssh_options_apply(session);
+ assert_ssh_return_code(session, rc);
+
+ /* check that the expansion was done only once */
+ assert_string_equal(session->opts.knownhosts, "%d/.ssh/known_hosts");
+ assert_string_equal(session->opts.global_knownhosts,
+ "/etc/%u/libssh/known_hosts");
+ /* no exec should be added if there already is one */
+ assert_string_equal(session->opts.ProxyCommand,
+ "exec echo \"Hello libssh %d!\"");
+ assert_string_equal(session->opts.identity->root->data,
+ "%d/do_not_expand");
+
+ /* apply should keep the freshest setting */
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_KNOWNHOSTS,
+ "hello there");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
+ "lorem ipsum");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_PROXYCOMMAND,
+ "mission_impossible");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "007");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "3");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "2");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "1");
+ assert_ssh_return_code(session, rc);
+
+ /* check that flags show need of escape expansion */
+ assert_false(session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS);
+ assert_false(session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS);
+ assert_false(session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND);
+ assert_false(ssh_list_count(session->opts.identity_non_exp) == 0);
+
+ rc = ssh_options_apply(session);
+ assert_ssh_return_code(session, rc);
+
+ /* check that the values got expanded */
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND);
+ assert_true(ssh_list_count(session->opts.identity_non_exp) == 0);
+
+ assert_string_equal(session->opts.knownhosts, "hello there");
+ assert_string_equal(session->opts.global_knownhosts, "lorem ipsum");
+ /* check that the "exec " was added at the beginning */
+ assert_string_equal(session->opts.ProxyCommand, "exec mission_impossible");
+ assert_string_equal(session->opts.identity->root->data, "1");
+
+ /* check the order of the identity files after double expansion */
+ awaited_list = ssh_list_new();
+ /* append the new data in order */
+ id = strdup("1");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("2");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("3");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("007");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("%d/do_not_expand");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ /* append the defaults; this list is copied from ssh_new@src/session.c */
+ id = ssh_path_expand_escape(session, "%d/id_ed25519");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+#ifdef HAVE_ECC
+ id = ssh_path_expand_escape(session, "%d/id_ecdsa");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+#endif
+ id = ssh_path_expand_escape(session, "%d/id_rsa");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+
+ assert_int_equal(ssh_list_count(awaited_list),
+ ssh_list_count(session->opts.identity));
+
+ it1 = ssh_list_get_iterator(awaited_list);
+ assert_non_null(it1);
+ it2 = ssh_list_get_iterator(session->opts.identity);
+ assert_non_null(it2);
+ while (it1 != NULL && it2 != NULL) {
+ assert_string_equal(it1->data, it2->data);
+
+ free((void*)it1->data);
+ it1 = it1->next;
+ it2 = it2->next;
+ }
+ assert_null(it1);
+ assert_null(it2);
+
+ ssh_list_free(awaited_list);
+}
+
+static void torture_options_set_verbosity (void **state)
+{
+ ssh_session session = *state;
+ int rc, new_level;
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_LOG_VERBOSITY_STR,
+ "3");
+ assert_int_equal(rc, SSH_OK);
+ new_level = ssh_get_log_level();
+ assert_int_equal(new_level, SSH_LOG_PACKET);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_LOG_VERBOSITY_STR,
+ "datsun");
+ assert_int_equal(rc, -1);
+ new_level = ssh_get_log_level();
+ assert_int_not_equal(new_level, 0);
+}
+
#ifdef WITH_SERVER
const char template[] = "temp_dir_XXXXXX";
@@ -1105,10 +1652,6 @@ static int ssh_bind_setup_files(void **state)
torture_write_file(LIBSSH_ECDSA_521_TESTKEY,
torture_get_openssh_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
#endif
-#ifdef HAVE_DSA
- torture_write_file(LIBSSH_DSA_TESTKEY,
- torture_get_openssh_testkey(SSH_KEYTYPE_DSS, 0));
-#endif
torture_write_file(LIBSSH_CUSTOM_BIND_CONFIG_FILE,
"Port 42\n");
return 0;
@@ -1190,16 +1733,6 @@ static void torture_bind_options_import_key(void **state)
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_IMPORT_KEY, key);
assert_int_equal(rc, 0);
-#ifdef HAVE_DSA
- /* set dsa key */
- base64_key = torture_get_testkey(SSH_KEYTYPE_DSS, 0);
- rc = ssh_pki_import_privkey_base64(base64_key, NULL, NULL, NULL, &key);
- assert_int_equal(rc, SSH_OK);
- assert_non_null(key);
-
- rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_IMPORT_KEY, key);
- assert_int_equal(rc, 0);
-#endif
#ifdef HAVE_ECC
/* set ecdsa key */
base64_key = torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0);
@@ -1249,15 +1782,6 @@ static void torture_bind_options_hostkey(void **state)
assert_non_null(bind->ecdsakey);
assert_string_equal(bind->ecdsakey, LIBSSH_ECDSA_521_TESTKEY);
#endif
-#ifdef HAVE_DSA
- /* Test DSA key */
- rc = ssh_bind_options_set(bind,
- SSH_BIND_OPTIONS_HOSTKEY,
- LIBSSH_DSA_TESTKEY);
- assert_int_equal(rc, 0);
- assert_non_null(bind->dsakey);
- assert_string_equal(bind->dsakey, LIBSSH_DSA_TESTKEY);
-#endif
}
static void torture_bind_options_bindaddr(void **state)
@@ -1313,6 +1837,10 @@ static void torture_bind_options_bindport_str(void **state)
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT_STR, "23");
assert_int_equal(rc, 0);
assert_int_equal(bind->bindport, 23);
+
+ rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT_STR, "twentythree");
+ assert_int_equal(rc, -1);
+ assert_int_not_equal(bind->bindport, 0);
}
static void torture_bind_options_log_verbosity(void **state)
@@ -1362,32 +1890,15 @@ static void torture_bind_options_log_verbosity_str(void **state)
new_level = ssh_get_log_level();
assert_int_equal(new_level, SSH_LOG_PACKET);
+ rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "verbosity");
+ assert_int_equal(rc, -1);
+ new_level = ssh_get_log_level();
+ assert_int_not_equal(new_level, 0);
+
rc = ssh_set_log_level(previous_level);
assert_int_equal(rc, SSH_OK);
}
-#ifdef HAVE_DSA
-static void torture_bind_options_dsakey(void **state)
-{
- struct bind_st *test_state;
- ssh_bind bind;
- int rc;
-
- assert_non_null(state);
- test_state = *((struct bind_st **)state);
- assert_non_null(test_state);
- assert_non_null(test_state->bind);
- bind = test_state->bind;
-
- rc = ssh_bind_options_set(bind,
- SSH_BIND_OPTIONS_DSAKEY,
- LIBSSH_DSA_TESTKEY);
- assert_int_equal(rc, 0);
- assert_non_null(bind->dsakey);
- assert_string_equal(bind->dsakey, LIBSSH_DSA_TESTKEY);
-}
-#endif
-
static void torture_bind_options_rsakey(void **state)
{
struct bind_st *test_state;
@@ -1401,7 +1912,7 @@ static void torture_bind_options_rsakey(void **state)
bind = test_state->bind;
rc = ssh_bind_options_set(bind,
- SSH_BIND_OPTIONS_RSAKEY,
+ SSH_BIND_OPTIONS_HOSTKEY,
LIBSSH_RSA_TESTKEY);
assert_int_equal(rc, 0);
assert_non_null(bind->rsakey);
@@ -1422,7 +1933,7 @@ static void torture_bind_options_ecdsakey(void **state)
bind = test_state->bind;
rc = ssh_bind_options_set(bind,
- SSH_BIND_OPTIONS_ECDSAKEY,
+ SSH_BIND_OPTIONS_HOSTKEY,
LIBSSH_ECDSA_521_TESTKEY);
assert_int_equal(rc, 0);
assert_non_null(bind->ecdsakey);
@@ -1840,6 +2351,8 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_options_set_knownhosts, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_get_knownhosts, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_proxycommand, setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_control_master, setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_control_path, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_set_ciphers, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_set_key_exchange, setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_set_hostkey, setup, teardown),
@@ -1853,6 +2366,14 @@ int torture_run_tests(void) {
setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_getopt,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_plus_sign,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_minus_sign,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_caret_sign,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_apply, setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_set_verbosity, setup, teardown),
};
#ifdef WITH_SERVER
@@ -1871,10 +2392,6 @@ int torture_run_tests(void) {
sshbind_setup, sshbind_teardown),
cmocka_unit_test_setup_teardown(torture_bind_options_log_verbosity_str,
sshbind_setup, sshbind_teardown),
-#ifdef HAVE_DSA
- cmocka_unit_test_setup_teardown(torture_bind_options_dsakey,
- sshbind_setup, sshbind_teardown),
-#endif
cmocka_unit_test_setup_teardown(torture_bind_options_rsakey,
sshbind_setup, sshbind_teardown),
#ifdef HAVE_ECC
diff --git a/tests/unittests/torture_packet.c b/tests/unittests/torture_packet.c
index 9f07f394..faf78116 100644
--- a/tests/unittests/torture_packet.c
+++ b/tests/unittests/torture_packet.c
@@ -272,10 +272,15 @@ static void torture_packet_aes256_cbc_etm(UNUSED_PARAM(void **state))
}
}
-static void torture_packet_3des_cbc(void **state)
+static void torture_packet_3des_cbc(UNUSED_PARAM(void **state))
{
int i;
- (void)state; /* unused */
+
+ /* 3des is not completely FIPS-allowed cipher since 140-3 */
+ if (ssh_fips_mode()) {
+ skip();
+ }
+
for (i=1;i<256;++i){
torture_packet("3des-cbc", "hmac-sha1", "none", i);
}
@@ -284,6 +289,12 @@ static void torture_packet_3des_cbc(void **state)
static void torture_packet_3des_cbc_etm(UNUSED_PARAM(void **state))
{
int i;
+
+ /* 3des is not completely FIPS-allowed cipher since 140-3 */
+ if (ssh_fips_mode()) {
+ skip();
+ }
+
for (i = 1; i < 256; ++i) {
torture_packet("3des-cbc", "hmac-sha1-etm@openssh.com", "none", i);
}
diff --git a/tests/unittests/torture_packet_filter.c b/tests/unittests/torture_packet_filter.c
index ffa49602..06cd74a7 100644
--- a/tests/unittests/torture_packet_filter.c
+++ b/tests/unittests/torture_packet_filter.c
@@ -20,7 +20,7 @@
*/
/*
- * This test checks if the messages accepted by the packet filter were intented
+ * This test checks if the messages accepted by the packet filter were intended
* to be accepted.
*
* The process consists in 2 steps:
diff --git a/tests/unittests/torture_pki.c b/tests/unittests/torture_pki.c
index d886245c..c610e8a2 100644
--- a/tests/unittests/torture_pki.c
+++ b/tests/unittests/torture_pki.c
@@ -5,10 +5,10 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_pki.h"
#include "torture_key.h"
-#include "pki.c"
const unsigned char INPUT[] = "1234567890123456789012345678901234567890"
"123456789012345678901234";
@@ -125,15 +125,7 @@ struct key_attrs key_attrs_list[][5] = {
{0, 0, "", 0, 0, "", 0}, /* UNKNOWN, SHA384 */
{0, 0, "", 0, 0, "", 0}, /* UNKNOWN, SHA512 */
},
-#ifdef HAVE_DSA
- {
- {1, 1, "ssh-dss", 1024, 0, "", 0}, /* DSS, AUTO */
- {1, 1, "ssh-dss", 1024, 20, "ssh-dss", 1}, /* DSS, SHA1 */
- {1, 1, "ssh-dss", 1024, 0, "", 0}, /* DSS, SHA256 */
- {1, 1, "ssh-dss", 1024, 0, "", 0}, /* DSS, SHA384 */
- {1, 1, "ssh-dss", 1024, 0, "", 0}, /* DSS, SHA512 */
- },
-#else
+ /* Cannot remove this as it will break the array indexing used */
{
{0, 0, "", 0, 0, "", 0}, /* DSS, AUTO */
{0, 0, "", 0, 0, "", 0}, /* DSS, SHA1 */
@@ -141,7 +133,6 @@ struct key_attrs key_attrs_list[][5] = {
{0, 0, "", 0, 0, "", 0}, /* DSS, SHA384 */
{0, 0, "", 0, 0, "", 0}, /* DSS, SHA512 */
},
-#endif /* HAVE_DSA */
{
{1, 1, "ssh-rsa", 2048, 0, "", 0}, /* RSA, AUTO */
{1, 1, "ssh-rsa", 2048, 20, "ssh-rsa", 1}, /* RSA, SHA1 */
@@ -164,21 +155,12 @@ struct key_attrs key_attrs_list[][5] = {
{0, 1, "", 521, 0, "", 0}, /* ECDSA, SHA512 */
},
{
- {1, 1, "ssh-ed25519", 0, 33, "ssh-ed25519", 1}, /* ED25519, AUTO */
- {1, 1, "ssh-ed25519", 0, 0, "", 0}, /* ED25519, SHA1 */
- {1, 1, "ssh-ed25519", 0, 0, "", 0}, /* ED25519, SHA256 */
- {1, 1, "ssh-ed25519", 0, 0, "", 0}, /* ED25519, SHA384 */
- {1, 1, "ssh-ed25519", 0, 0, "", 0}, /* ED25519, SHA512 */
+ {1, 1, "ssh-ed25519", 255, 33, "ssh-ed25519", 1}, /* ED25519, AUTO */
+ {1, 1, "ssh-ed25519", 255, 0, "", 0}, /* ED25519, SHA1 */
+ {1, 1, "ssh-ed25519", 255, 0, "", 0}, /* ED25519, SHA256 */
+ {1, 1, "ssh-ed25519", 255, 0, "", 0}, /* ED25519, SHA384 */
+ {1, 1, "ssh-ed25519", 255, 0, "", 0}, /* ED25519, SHA512 */
},
-#ifdef HAVE_DSA
- {
- {0, 1, "", 0, 0, "", 0}, /* DSS CERT, AUTO */
- {0, 1, "", 0, 0, "", 0}, /* DSS CERT, SHA1 */
- {0, 1, "", 0, 0, "", 0}, /* DSS CERT, SHA256 */
- {0, 1, "", 0, 0, "", 0}, /* DSS CERT, SHA384 */
- {0, 1, "", 0, 0, "", 0}, /* DSS CERT, SHA512 */
- },
-#else
{
{0, 0, "", 0, 0, "", 0}, /* DSS CERT, AUTO */
{0, 0, "", 0, 0, "", 0}, /* DSS CERT, SHA1 */
@@ -186,7 +168,6 @@ struct key_attrs key_attrs_list[][5] = {
{0, 0, "", 0, 0, "", 0}, /* DSS CERT, SHA384 */
{0, 0, "", 0, 0, "", 0}, /* DSS CERT, SHA512 */
},
-#endif /* HAVE_DSA */
{
{0, 1, "", 0, 0, "", 0}, /* RSA CERT, AUTO */
{0, 1, "", 0, 0, "", 0}, /* RSA CERT, SHA1 */
@@ -260,12 +241,13 @@ static void torture_pki_verify_mismatch(void **state)
enum ssh_digest_e hash;
size_t input_length = sizeof(INPUT);
struct key_attrs skey_attrs, vkey_attrs;
+ int bits;
(void) state;
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
- for (sig_type = SSH_KEYTYPE_DSS;
+ for (sig_type = SSH_KEYTYPE_RSA;
sig_type <= SSH_KEYTYPE_ED25519_CERT01;
sig_type++)
{
@@ -274,8 +256,7 @@ static void torture_pki_verify_mismatch(void **state)
hash++)
{
if (ssh_fips_mode()) {
- if (sig_type == SSH_KEYTYPE_DSS ||
- sig_type == SSH_KEYTYPE_ED25519 ||
+ if (sig_type == SSH_KEYTYPE_ED25519 ||
hash == SSH_DIGEST_SHA1)
{
/* In FIPS mode, skip unsupported algorithms */
@@ -294,6 +275,8 @@ static void torture_pki_verify_mismatch(void **state)
assert_non_null(key);
assert_int_equal(key->type, sig_type);
assert_string_equal(key->type_c, skey_attrs.type_c);
+ bits = ssh_key_size(key);
+ assert_int_equal(bits, skey_attrs.size_arg);
SSH_LOG(SSH_LOG_TRACE, "Creating signature %d with hash %d",
sig_type, hash);
@@ -338,13 +321,12 @@ static void torture_pki_verify_mismatch(void **state)
input_length);
assert_true(rc == SSH_OK);
- for (key_type = SSH_KEYTYPE_DSS;
+ for (key_type = SSH_KEYTYPE_RSA;
key_type <= SSH_KEYTYPE_ED25519_CERT01;
key_type++)
{
if (ssh_fips_mode()) {
- if (key_type == SSH_KEYTYPE_DSS ||
- key_type == SSH_KEYTYPE_ED25519)
+ if (key_type == SSH_KEYTYPE_ED25519)
{
/* In FIPS mode, skip unsupported algorithms */
continue;
@@ -425,7 +407,6 @@ static void torture_pki_verify_mismatch(void **state)
key = NULL;
}
}
-
ssh_free(session);
}
diff --git a/tests/unittests/torture_pki_dsa.c b/tests/unittests/torture_pki_dsa.c
index b555e487..dfd59a64 100644
--- a/tests/unittests/torture_pki_dsa.c
+++ b/tests/unittests/torture_pki_dsa.c
@@ -6,10 +6,10 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_key.h"
#include "torture_pki.h"
-#include "pki.c"
#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa"
#define LIBSSH_DSA_TESTKEY_PASSPHRASE "libssh_testkey_passphrase.id_dsa"
@@ -129,10 +129,8 @@ static void torture_pki_dsa_import_pubkey_file(void **state)
/* The key doesn't have the hostname as comment after the key */
rc = ssh_pki_import_pubkey_file(LIBSSH_DSA_TESTKEY ".pub", &pubkey);
- assert_return_code(rc, errno);
- assert_non_null(pubkey);
-
- SSH_KEY_FREE(pubkey);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(pubkey);
}
static void torture_pki_dsa_import_pubkey_from_openssh_privkey(void **state)
@@ -144,10 +142,8 @@ static void torture_pki_dsa_import_pubkey_from_openssh_privkey(void **state)
/* The key doesn't have the hostname as comment after the key */
rc = ssh_pki_import_pubkey_file(LIBSSH_DSA_TESTKEY_PASSPHRASE, &pubkey);
- assert_return_code(rc, errno);
- assert_non_null(pubkey);
-
- SSH_KEY_FREE(pubkey);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(pubkey);
}
static void torture_pki_dsa_import_privkey_base64(void **state)
@@ -163,723 +159,33 @@ static void torture_pki_dsa_import_privkey_base64(void **state)
NULL,
NULL,
&key);
- assert_true(rc == 0);
-
- SSH_KEY_FREE(key);
-}
-
-static void torture_pki_dsa_import_privkey_base64_comment(void **state)
-{
- int rc, file_str_len;
- ssh_key key = NULL;
- const char *passphrase = torture_get_testkey_passphrase();
- const char *comment_str = "#this is line-comment\n#this is another\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(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);
-
- free(file_str);
- 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,
- size_t input_len)
-{
- ssh_signature sig;
- ssh_key pubkey = NULL;
- int rc;
-
- /* Get the public key to verify signature */
- rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
- assert_int_equal(rc, SSH_OK);
- assert_non_null(pubkey);
-
- /* Sign the buffer */
- sig = pki_sign_data(key, hash_type, input, input_len);
- assert_non_null(sig);
-
- /* Verify signature */
- rc = pki_verify_data_signature(sig, pubkey, input, input_len);
- assert_int_equal(rc, SSH_OK);
-
- ssh_signature_free(sig);
- SSH_KEY_FREE(pubkey);
-
- return rc;
-}
-
-static void torture_pki_sign_data_dsa(void **state)
-{
- int rc;
- ssh_key key = NULL;
-
- (void) state;
-
- /* Setup */
- rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 2048, &key);
- assert_int_equal(rc, SSH_OK);
- assert_non_null(key);
-
- /* Test using SHA1 */
- rc = test_sign_verify_data(key, SSH_DIGEST_SHA1, INPUT, sizeof(INPUT));
- assert_int_equal(rc, SSH_OK);
-
- /* Cleanup */
- SSH_KEY_FREE(key);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(key);
}
-static void torture_pki_fail_sign_with_incompatible_hash(void **state)
+static void torture_pki_generate_dsa(void **state)
{
int rc;
ssh_key key = NULL;
- ssh_key pubkey = NULL;
- ssh_signature sig, bad_sig;
(void) state;
/* Setup */
rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 2048, &key);
- assert_int_equal(rc, SSH_OK);
- assert_non_null(key);
-
- /* Get the public key to verify signature */
- rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
- assert_int_equal(rc, SSH_OK);
- assert_non_null(pubkey);
-
- /* Sign the buffer */
- sig = pki_sign_data(key, SSH_DIGEST_SHA1, INPUT, sizeof(INPUT));
- assert_non_null(sig);
-
- /* Verify signature */
- rc = pki_verify_data_signature(sig, pubkey, INPUT, sizeof(INPUT));
- assert_int_equal(rc, SSH_OK);
-
- /* Test if signature fails with SSH_DIGEST_AUTO */
- bad_sig = pki_sign_data(key, SSH_DIGEST_AUTO, INPUT, sizeof(INPUT));
- assert_null(bad_sig);
-
- /* Test if verification fails with SSH_DIGEST_AUTO */
- sig->hash_type = SSH_DIGEST_AUTO;
- rc = pki_verify_data_signature(sig, pubkey, INPUT, sizeof(INPUT));
- assert_int_not_equal(rc, SSH_OK);
-
- /* Test if signature fails with SSH_DIGEST_SHA256 */
- bad_sig = pki_sign_data(key, SSH_DIGEST_SHA256, INPUT, sizeof(INPUT));
- assert_null(bad_sig);
-
- /* Test if verification fails with SSH_DIGEST_SHA256 */
- sig->hash_type = SSH_DIGEST_SHA256;
- rc = pki_verify_data_signature(sig, pubkey, INPUT, sizeof(INPUT));
- assert_int_not_equal(rc, SSH_OK);
-
- /* Test if signature fails with SSH_DIGEST_SHA384 */
- bad_sig = pki_sign_data(key, SSH_DIGEST_SHA384, INPUT, sizeof(INPUT));
- assert_null(bad_sig);
-
- /* Test if verification fails with SSH_DIGEST_SHA384 */
- sig->hash_type = SSH_DIGEST_SHA384;
- rc = pki_verify_data_signature(sig, pubkey, INPUT, sizeof(INPUT));
- assert_int_not_equal(rc, SSH_OK);
-
- /* Test if signature fails with SSH_DIGEST_SHA512 */
- bad_sig = pki_sign_data(key, SSH_DIGEST_SHA512, INPUT, sizeof(INPUT));
- assert_null(bad_sig);
-
- /* Test if verification fails with SSH_DIGEST_SHA512 */
- sig->hash_type = SSH_DIGEST_SHA512;
- rc = pki_verify_data_signature(sig, pubkey, INPUT, sizeof(INPUT));
- assert_int_not_equal(rc, SSH_OK);
-
- /* Cleanup */
- ssh_signature_free(sig);
- SSH_KEY_FREE(pubkey);
- SSH_KEY_FREE(key);
-}
-
-#ifdef HAVE_LIBCRYPTO
-static void torture_pki_dsa_write_privkey(void **state)
-{
- ssh_key origkey = NULL;
- ssh_key privkey = NULL;
- int rc;
-
- (void) state; /* unused */
-
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY,
- NULL,
- NULL,
- NULL,
- &origkey);
- assert_true(rc == 0);
- assert_non_null(origkey);
-
- unlink(LIBSSH_DSA_TESTKEY);
-
- rc = ssh_pki_export_privkey_file(origkey,
- NULL,
- NULL,
- NULL,
- LIBSSH_DSA_TESTKEY);
- assert_true(rc == 0);
-
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY,
- NULL,
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
- assert_non_null(privkey);
-
- rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
-
- SSH_KEY_FREE(origkey);
- SSH_KEY_FREE(privkey);
-
- /* Test with passphrase */
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY_PASSPHRASE,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- &origkey);
- assert_true(rc == 0);
- assert_non_null(origkey);
-
- unlink(LIBSSH_DSA_TESTKEY_PASSPHRASE);
- rc = ssh_pki_export_privkey_file(origkey,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- LIBSSH_DSA_TESTKEY_PASSPHRASE);
- assert_true(rc == 0);
-
- /* Test with invalid passphrase */
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY_PASSPHRASE,
- "invalid secret",
- NULL,
- NULL,
- &privkey);
- assert_true(rc == SSH_ERROR);
- assert_null(privkey);
-
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY_PASSPHRASE,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
- assert_non_null(privkey);
-
- rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
-
- SSH_KEY_FREE(origkey);
- SSH_KEY_FREE(privkey);
-}
-#endif
-
-static void torture_pki_dsa_import_privkey_base64_passphrase(void **state)
-{
- int rc;
- ssh_key key = NULL;
- const char *passphrase = torture_get_testkey_passphrase();
-
- (void) state; /* unused */
-
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 1),
- passphrase,
- NULL,
- NULL,
- &key);
- assert_return_code(rc, errno);
- assert_non_null(key);
-
- rc = ssh_key_is_private(key);
- assert_true(rc == 1);
-
- SSH_KEY_FREE(key);
-
- /* test if it returns -1 if passphrase is wrong */
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 1),
- "wrong passphrase !!",
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
- assert_null(key);
-
- /* test if it returns -1 if passphrase is NULL */
- /* libcrypto asks for a passphrase, so skip this test */
-#ifndef HAVE_LIBCRYPTO
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 1),
- NULL,
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
- assert_null(key);
-#endif
-
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 1),
- passphrase,
- NULL,
- NULL,
- &key);
- assert_return_code(rc, errno);
- assert_non_null(key);
-
- rc = ssh_key_is_private(key);
- assert_true(rc == 1);
-
- SSH_KEY_FREE(key);
-
- /* test if it returns -1 if passphrase is wrong */
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 1),
- "wrong passphrase !!",
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
- assert_null(key);
-
- /* This free in unnecessary, but the static analyser does not know */
- SSH_KEY_FREE(key);
-
-#ifndef HAVE_LIBCRYPTO
- /* test if it returns -1 if passphrase is NULL */
- /* libcrypto asks for a passphrase, so skip this test */
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 1),
- NULL,
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
- assert_null(key);
-
- /* This free in unnecessary, but the static analyser does not know */
- SSH_KEY_FREE(key);
-#endif /* HAVE_LIBCRYPTO */
-}
-
-static void
-torture_pki_dsa_import_openssh_privkey_base64_passphrase(void **state)
-{
- int rc;
- ssh_key key = NULL;
- const char *passphrase = torture_get_testkey_passphrase();
- const char *keystring = NULL;
-
- (void) state; /* unused */
-
- keystring = torture_get_openssh_testkey(SSH_KEYTYPE_DSS, 1);
- assert_non_null(keystring);
-
- rc = ssh_pki_import_privkey_base64(keystring,
- passphrase,
- NULL,
- NULL,
- &key);
- assert_return_code(rc, errno);
- assert_non_null(key);
-
- rc = ssh_key_is_private(key);
- assert_true(rc == 1);
-
- SSH_KEY_FREE(key);
-
- /* test if it returns -1 if passphrase is wrong */
- rc = ssh_pki_import_privkey_base64(keystring,
- "wrong passphrase !!",
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
+ assert_int_equal(rc, SSH_ERROR);
assert_null(key);
-
- /* test if it returns -1 if passphrase is NULL */
- rc = ssh_pki_import_privkey_base64(keystring,
- NULL,
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
-
- rc = ssh_pki_import_privkey_base64(keystring,
- passphrase,
- NULL,
- NULL,
- &key);
- assert_return_code(rc, errno);
- assert_non_null(key);
-
- rc = ssh_key_is_private(key);
- assert_true(rc == 1);
-
- SSH_KEY_FREE(key);
-
- /* test if it returns -1 if passphrase is wrong */
- rc = ssh_pki_import_privkey_base64(keystring,
- "wrong passphrase !!",
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
- assert_null(key);
-
- /* This free is unnecessary, but the static analyser does not know */
- SSH_KEY_FREE(key);
-
- /* test if it returns -1 if passphrase is NULL */
- rc = ssh_pki_import_privkey_base64(keystring,
- NULL,
- NULL,
- NULL,
- &key);
- assert_true(rc == -1);
- assert_null(key);
-
- /* This free is unnecessary, but the static analyser does not know */
- SSH_KEY_FREE(key);
-}
-
-
-static void torture_pki_dsa_publickey_from_privatekey(void **state)
-{
- int rc;
- ssh_key key = NULL;
- ssh_key pubkey = NULL;
- const char *passphrase = NULL;
-
- (void) state; /* unused */
-
- rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0),
- passphrase,
- NULL,
- NULL,
- &key);
- assert_true(rc == 0);
- assert_non_null(key);
-
- rc = ssh_key_is_private(key);
- assert_true(rc == 1);
-
- rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
- assert_true(rc == SSH_OK);
- assert_non_null(pubkey);
-
- SSH_KEY_FREE(key);
- SSH_KEY_FREE(pubkey);
}
static void torture_pki_dsa_import_cert_file(void **state)
{
int rc;
ssh_key cert = NULL;
- enum ssh_keytypes_e type;
-
- (void) state; /* unused */
-
- rc = ssh_pki_import_cert_file(LIBSSH_DSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == 0);
- assert_non_null(cert);
-
- type = ssh_key_type(cert);
- assert_true(type == SSH_KEYTYPE_DSS_CERT01);
-
- rc = ssh_key_is_public(cert);
- assert_true(rc == 1);
-
- SSH_KEY_FREE(cert);
-}
-
-static void torture_pki_dsa_publickey_base64(void **state)
-{
- enum ssh_keytypes_e type;
- char *b64_key = NULL, *key_buf = NULL, *p = NULL;
- const char *str = NULL;
- ssh_key key = NULL;
- size_t keylen;
- size_t i;
- int rc;
-
- (void) state; /* unused */
-
- key_buf = strdup(torture_get_testkey_pub(SSH_KEYTYPE_DSS));
- assert_non_null(key_buf);
-
- keylen = strlen(key_buf);
-
- str = p = key_buf;
- for (i = 0; i < keylen; i++) {
- if (isspace((int)p[i])) {
- p[i] = '\0';
- break;
- }
-
- }
-
- type = ssh_key_type_from_name(str);
- assert_true(type == SSH_KEYTYPE_DSS);
-
- str = &p[i + 1];
-
- for (; i < keylen; i++) {
- if (isspace((int)p[i])) {
- p[i] = '\0';
- break;
- }
- }
-
- rc = ssh_pki_import_pubkey_base64(str, type, &key);
- assert_true(rc == 0);
- assert_non_null(key);
-
- rc = ssh_pki_export_pubkey_base64(key, &b64_key);
- assert_true(rc == 0);
- assert_non_null(b64_key);
-
- assert_string_equal(str, b64_key);
-
- free(b64_key);
- free(key_buf);
- SSH_KEY_FREE(key);
-}
-
-static void torture_pki_dsa_generate_pubkey_from_privkey(void **state)
-{
- char pubkey_generated[4096] = {0};
- ssh_key privkey = NULL;
- ssh_key pubkey = NULL;
- int len;
- int rc;
(void) state; /* unused */
- /* remove the public key, generate it from the private key and write it. */
- unlink(LIBSSH_DSA_TESTKEY ".pub");
-
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY,
- NULL,
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
- assert_non_null(privkey);
-
- rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
- assert_true(rc == SSH_OK);
- assert_non_null(pubkey);
-
- rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_DSA_TESTKEY ".pub");
- assert_true(rc == 0);
-
- rc = torture_read_one_line(LIBSSH_DSA_TESTKEY ".pub",
- pubkey_generated,
- sizeof(pubkey_generated));
- assert_true(rc == 0);
-
- len = torture_pubkey_len(torture_get_testkey_pub(SSH_KEYTYPE_DSS));
- assert_memory_equal(torture_get_testkey_pub(SSH_KEYTYPE_DSS),
- pubkey_generated,
- len);
-
- SSH_KEY_FREE(privkey);
- SSH_KEY_FREE(pubkey);
-}
-
-static void torture_pki_dsa_duplicate_key(void **state)
-{
- int rc;
- char *b64_key = NULL;
- char *b64_key_gen = NULL;
- ssh_key pubkey = NULL;
- ssh_key pubkey_dup = NULL;
- ssh_key privkey = NULL;
- ssh_key privkey_dup = NULL;
-
- (void) state;
-
- rc = ssh_pki_import_pubkey_file(LIBSSH_DSA_TESTKEY ".pub", &pubkey);
- assert_true(rc == 0);
- assert_non_null(pubkey);
-
- rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key);
- assert_true(rc == 0);
- assert_non_null(b64_key);
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY,
- NULL,
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
- assert_non_null(privkey);
-
- privkey_dup = ssh_key_dup(privkey);
- assert_non_null(privkey_dup);
-
- rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey_dup);
- assert_true(rc == SSH_OK);
- assert_non_null(pubkey_dup);
-
- rc = ssh_pki_export_pubkey_base64(pubkey_dup, &b64_key_gen);
- assert_true(rc == 0);
- assert_non_null(b64_key_gen);
-
- assert_string_equal(b64_key, b64_key_gen);
-
- rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
-
- rc = ssh_key_cmp(pubkey, pubkey_dup, SSH_KEY_CMP_PUBLIC);
- assert_true(rc == 0);
-
- SSH_KEY_FREE(pubkey);
- SSH_KEY_FREE(pubkey_dup);
-
- SSH_KEY_FREE(privkey);
- SSH_KEY_FREE(privkey_dup);
- SSH_STRING_FREE_CHAR(b64_key);
- SSH_STRING_FREE_CHAR(b64_key_gen);
-}
-
-static void torture_pki_dsa_generate_key(void **state)
-{
- int rc;
- ssh_key key = NULL, pubkey = NULL;
- ssh_signature sign = NULL;
- ssh_session session=ssh_new();
- (void) state;
-
- rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 1024, &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, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1);
- assert_non_null(sign);
- rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
- ssh_signature_free(sign);
- SSH_KEY_FREE(key);
- SSH_KEY_FREE(pubkey);
-
- rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 2048, &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, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1);
- assert_non_null(sign);
- rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
- ssh_signature_free(sign);
- SSH_KEY_FREE(key);
- SSH_KEY_FREE(pubkey);
-
- rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 3072, &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, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1);
- assert_non_null(sign);
- rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
- ssh_signature_free(sign);
- SSH_KEY_FREE(key);
- SSH_KEY_FREE(pubkey);
-
- ssh_free(session);
-}
-
-static void torture_pki_dsa_cert_verify(void **state)
-{
- int rc;
- ssh_key privkey = NULL, cert = NULL;
- ssh_signature sign = NULL;
- ssh_session session=ssh_new();
- (void) state;
-
- rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY,
- NULL,
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
- assert_non_null(privkey);
-
rc = ssh_pki_import_cert_file(LIBSSH_DSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == 0);
- assert_non_null(cert);
-
- sign = pki_do_sign(privkey, INPUT, sizeof(INPUT), SSH_DIGEST_SHA1);
- assert_non_null(sign);
- rc = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
- ssh_signature_free(sign);
- SSH_KEY_FREE(privkey);
- SSH_KEY_FREE(cert);
-
- ssh_free(session);
-}
-
-static void torture_pki_dsa_skip(UNUSED_PARAM(void **state))
-{
- skip();
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(cert);
}
int torture_run_tests(void)
@@ -895,60 +201,20 @@ int torture_run_tests(void)
cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64,
setup_dsa_key,
teardown),
- 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),
- cmocka_unit_test_setup_teardown(torture_pki_dsa_publickey_from_privatekey,
- setup_dsa_key,
- teardown),
cmocka_unit_test_setup_teardown(torture_pki_dsa_import_cert_file,
setup_dsa_key,
teardown),
-#ifdef HAVE_LIBCRYPTO
- cmocka_unit_test_setup_teardown(torture_pki_dsa_write_privkey,
- setup_dsa_key,
- teardown),
-#endif
- cmocka_unit_test(torture_pki_sign_data_dsa),
- cmocka_unit_test(torture_pki_fail_sign_with_incompatible_hash),
- cmocka_unit_test(torture_pki_dsa_import_privkey_base64_passphrase),
- cmocka_unit_test(torture_pki_dsa_import_openssh_privkey_base64_passphrase),
-
- /* public key */
- cmocka_unit_test_setup_teardown(torture_pki_dsa_publickey_base64,
- setup_dsa_key,
- teardown),
- cmocka_unit_test_setup_teardown(torture_pki_dsa_generate_pubkey_from_privkey,
- setup_dsa_key,
- teardown),
- cmocka_unit_test_setup_teardown(torture_pki_dsa_duplicate_key,
- setup_dsa_key,
- teardown),
- cmocka_unit_test_setup_teardown(torture_pki_dsa_duplicate_key,
- setup_dsa_key,
- teardown),
- cmocka_unit_test(torture_pki_dsa_generate_key),
- cmocka_unit_test_setup_teardown(torture_pki_dsa_cert_verify,
- setup_dsa_key,
- teardown),
- };
- struct CMUnitTest skip_tests[] = {
- cmocka_unit_test(torture_pki_dsa_skip)
+ cmocka_unit_test_setup_teardown(torture_pki_generate_dsa,
+ setup_dsa_key,
+ teardown),
};
ssh_init();
- if (ssh_fips_mode()) {
- rc = cmocka_run_group_tests(skip_tests, NULL, NULL);
- } else {
- torture_filter_tests(tests);
- rc = cmocka_run_group_tests(tests, NULL, NULL);
- }
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
ssh_finalize();
return rc;
}
diff --git a/tests/unittests/torture_pki_ecdsa.c b/tests/unittests/torture_pki_ecdsa.c
index 22f06a46..f6b17e8a 100644
--- a/tests/unittests/torture_pki_ecdsa.c
+++ b/tests/unittests/torture_pki_ecdsa.c
@@ -1,14 +1,15 @@
#include "config.h"
+#include "libssh/libssh.h"
#define LIBSSH_STATIC
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_key.h"
#include "torture_pki.h"
-#include "pki.c"
#define LIBSSH_ECDSA_TESTKEY "libssh_testkey.id_ecdsa"
#define LIBSSH_ECDSA_TESTKEY_PASSPHRASE "libssh_testkey_passphrase.id_ecdsa"
@@ -218,27 +219,60 @@ static void torture_pki_ecdsa_import_pubkey_from_openssh_privkey(void **state)
SSH_KEY_FREE(pubkey);
}
-static void torture_pki_ecdsa_import_privkey_base64(void **state)
+static void
+torture_pki_ecdsa_import_export_privkey_base64_format(void **state,
+ enum ssh_file_format_e format)
{
int rc;
- char *key_str = NULL;
- ssh_key key = NULL;
+ char *key_str = NULL, *new_key_str = NULL;
+ ssh_key key = NULL, new_key = NULL;
const char *passphrase = torture_get_testkey_passphrase();
- (void) state; /* unused */
+ (void)state; /* unused */
key_str = torture_pki_read_file(LIBSSH_ECDSA_TESTKEY);
assert_non_null(key_str);
rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(key);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
+
+ /* Export */
+ rc = ssh_pki_export_privkey_base64_format(key,
+ passphrase,
+ NULL,
+ NULL,
+ &new_key_str,
+ format);
+ assert_int_equal(rc, SSH_OK);
+ assert_non_null(new_key_str);
+
+ /* and import again */
+ rc = ssh_pki_import_privkey_base64(new_key_str, passphrase, NULL, NULL,
+ &new_key);
+ assert_int_equal(rc, 0);
+ assert_non_null(new_key);
+
+ rc = ssh_key_is_private(new_key);
+ assert_int_equal(rc, 1);
+
+ rc = ssh_key_cmp(key, new_key, SSH_KEY_CMP_PRIVATE | SSH_KEY_CMP_PUBLIC);
+ assert_int_equal(rc, 0);
free(key_str);
+ free(new_key_str);
SSH_KEY_FREE(key);
+ SSH_KEY_FREE(new_key);
+}
+
+static void
+torture_pki_ecdsa_import_export_privkey_base64_default(void **state)
+{
+ torture_pki_ecdsa_import_export_privkey_base64_format(state,
+ SSH_FILE_FORMAT_DEFAULT);
}
static void torture_pki_ecdsa_import_privkey_base64_comment(void **state)
@@ -261,11 +295,11 @@ static void torture_pki_ecdsa_import_privkey_base64_comment(void **state)
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_int_equal(rc, 0);
assert_non_null(key);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
free(key_str);
free(file_str);
@@ -292,11 +326,11 @@ static void torture_pki_ecdsa_import_privkey_base64_whitespace(void **state)
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_int_equal(rc, 0);
assert_non_null(key);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
free(key_str);
free(file_str);
@@ -318,11 +352,11 @@ static void torture_pki_ecdsa_publickey_from_privatekey(void **state)
assert_non_null(key_str);
rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
free(key_str);
@@ -337,15 +371,20 @@ static void torture_pki_ecdsa_import_cert_file(void **state)
enum ssh_keytypes_e type;
struct pki_st *test_state = *((struct pki_st **)state);
+ /* Importing public key as cert should fail */
+ rc = ssh_pki_import_cert_file(LIBSSH_ECDSA_TESTKEY ".pub", &cert);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(cert);
+
rc = ssh_pki_import_cert_file(LIBSSH_ECDSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(cert);
type = ssh_key_type(cert);
- assert_true(type == test_state->type+3);
+ assert_int_equal(type, test_state->type+3);
rc = ssh_key_is_public(cert);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(cert);
}
@@ -369,7 +408,7 @@ static void torture_pki_ecdsa_publickey_base64(void **state)
}
type = ssh_key_type_from_name(q);
- assert_true(type == test_state->type);
+ assert_int_equal(type, test_state->type);
q = ++p;
while (p != NULL && *p != '\0' && *p != ' ') p++;
@@ -378,11 +417,11 @@ static void torture_pki_ecdsa_publickey_base64(void **state)
}
rc = ssh_pki_import_pubkey_base64(q, type, &key);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(key);
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(b64_key);
assert_string_equal(q, b64_key);
@@ -406,7 +445,7 @@ static void torture_pki_ecdsa_generate_pubkey_from_privkey(void **state)
rc = torture_read_one_line(LIBSSH_ECDSA_TESTKEY ".pub",
pubkey_original,
sizeof(pubkey_original));
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
/* remove the public key, generate it from the private key and write it. */
unlink(LIBSSH_ECDSA_TESTKEY ".pub");
@@ -416,22 +455,23 @@ static void torture_pki_ecdsa_generate_pubkey_from_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_ECDSA_TESTKEY ".pub");
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
rc = torture_read_one_line(LIBSSH_ECDSA_TESTKEY ".pub",
pubkey_generated,
sizeof(pubkey_generated));
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
len = torture_pubkey_len(pubkey_original);
- assert_int_equal(strncmp(pubkey_original, pubkey_generated, len), 0);
+ assert_int_equal(len, torture_pubkey_len(pubkey_generated));
+ assert_memory_equal(pubkey_original, pubkey_generated, len);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
@@ -450,11 +490,11 @@ static void torture_pki_ecdsa_duplicate_key(void **state)
(void) state;
rc = ssh_pki_import_pubkey_file(LIBSSH_ECDSA_TESTKEY ".pub", &pubkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(pubkey);
rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(b64_key);
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY,
@@ -462,27 +502,27 @@ static void torture_pki_ecdsa_duplicate_key(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
privkey_dup = ssh_key_dup(privkey);
assert_non_null(privkey_dup);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey_dup);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey_dup);
rc = ssh_pki_export_pubkey_base64(pubkey_dup, &b64_key_gen);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(b64_key_gen);
assert_string_equal(b64_key, b64_key_gen);
rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
rc = ssh_key_cmp(pubkey, pubkey_dup, SSH_KEY_CMP_PUBLIC);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
SSH_KEY_FREE(pubkey);
SSH_KEY_FREE(pubkey_dup);
@@ -539,7 +579,7 @@ static void torture_pki_generate_key_ecdsa(void **state)
(void) state;
rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA_P256, 0, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -547,13 +587,13 @@ static void torture_pki_generate_key_ecdsa(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ECDSA_P256);
+ assert_int_equal(type, SSH_KEYTYPE_ECDSA_P256);
type_char = ssh_key_type_to_char(type);
- assert_true(strcmp(type_char, "ecdsa-sha2-nistp256") == 0);
+ assert_string_equal(type_char, "ecdsa-sha2-nistp256");
etype_char = ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, "ecdsa-sha2-nistp256") == 0);
+ assert_string_equal(etype_char, "ecdsa-sha2-nistp256");
ssh_signature_free(sign);
SSH_KEY_FREE(key);
@@ -561,7 +601,7 @@ static void torture_pki_generate_key_ecdsa(void **state)
/* deprecated */
rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 256, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -569,20 +609,20 @@ static void torture_pki_generate_key_ecdsa(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ECDSA_P256);
+ assert_int_equal(type, SSH_KEYTYPE_ECDSA_P256);
type_char = ssh_key_type_to_char(type);
- assert_true(strcmp(type_char, "ecdsa-sha2-nistp256") == 0);
+ assert_string_equal(type_char, "ecdsa-sha2-nistp256");
etype_char = ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, "ecdsa-sha2-nistp256") == 0);
+ assert_string_equal(etype_char, "ecdsa-sha2-nistp256");
ssh_signature_free(sign);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA_P384, 0, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -590,13 +630,13 @@ static void torture_pki_generate_key_ecdsa(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA384);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ECDSA_P384);
+ assert_int_equal(type, SSH_KEYTYPE_ECDSA_P384);
type_char = ssh_key_type_to_char(type);
- assert_true(strcmp(type_char, "ecdsa-sha2-nistp384") == 0);
- etype_char =ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, "ecdsa-sha2-nistp384") == 0);
+ assert_string_equal(type_char, "ecdsa-sha2-nistp384");
+ etype_char = ssh_pki_key_ecdsa_name(key);
+ assert_string_equal(etype_char, "ecdsa-sha2-nistp384");
ssh_signature_free(sign);
SSH_KEY_FREE(key);
@@ -604,7 +644,7 @@ static void torture_pki_generate_key_ecdsa(void **state)
/* deprecated */
rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 384, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -612,20 +652,20 @@ static void torture_pki_generate_key_ecdsa(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA384);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ECDSA_P384);
+ assert_int_equal(type, SSH_KEYTYPE_ECDSA_P384);
type_char = ssh_key_type_to_char(type);
- assert_true(strcmp(type_char, "ecdsa-sha2-nistp384") == 0);
- etype_char =ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, "ecdsa-sha2-nistp384") == 0);
+ assert_string_equal(type_char, "ecdsa-sha2-nistp384");
+ etype_char = ssh_pki_key_ecdsa_name(key);
+ assert_string_equal(etype_char, "ecdsa-sha2-nistp384");
ssh_signature_free(sign);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA_P521, 0, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -633,13 +673,13 @@ static void torture_pki_generate_key_ecdsa(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA512);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ECDSA_P521);
+ assert_int_equal(type, SSH_KEYTYPE_ECDSA_P521);
type_char = ssh_key_type_to_char(type);
- assert_true(strcmp(type_char, "ecdsa-sha2-nistp521") == 0);
+ assert_string_equal(type_char, "ecdsa-sha2-nistp521");
etype_char =ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, "ecdsa-sha2-nistp521") == 0);
+ assert_string_equal(etype_char, "ecdsa-sha2-nistp521");
ssh_signature_free(sign);
SSH_KEY_FREE(key);
@@ -647,7 +687,7 @@ static void torture_pki_generate_key_ecdsa(void **state)
/* deprecated */
rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 521, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -655,13 +695,13 @@ static void torture_pki_generate_key_ecdsa(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA512);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ECDSA_P521);
+ assert_int_equal(type, SSH_KEYTYPE_ECDSA_P521);
type_char = ssh_key_type_to_char(type);
- assert_true(strcmp(type_char, "ecdsa-sha2-nistp521") == 0);
- etype_char =ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, "ecdsa-sha2-nistp521") == 0);
+ assert_string_equal(type_char, "ecdsa-sha2-nistp521");
+ etype_char = ssh_pki_key_ecdsa_name(key);
+ assert_string_equal(etype_char, "ecdsa-sha2-nistp521");
ssh_signature_free(sign);
SSH_KEY_FREE(key);
@@ -684,11 +724,11 @@ static void torture_pki_ecdsa_cert_verify(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_pki_import_cert_file(LIBSSH_ECDSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(cert);
/* Get the hash type to be used in the signature based on the key type */
@@ -697,7 +737,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 = ssh_pki_signature_verify(session, sign, cert, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
ssh_signature_free(sign);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(cert);
@@ -822,8 +862,9 @@ static void torture_pki_fail_sign_with_incompatible_hash(void **state)
SSH_KEY_FREE(key);
}
-#ifdef HAVE_LIBCRYPTO
-static void torture_pki_ecdsa_write_privkey(void **state)
+static void
+torture_pki_ecdsa_write_privkey_format(void **state,
+ enum ssh_file_format_e format)
{
ssh_key origkey = NULL;
ssh_key privkey = NULL;
@@ -836,28 +877,29 @@ static void torture_pki_ecdsa_write_privkey(void **state)
NULL,
NULL,
&origkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(origkey);
unlink(LIBSSH_ECDSA_TESTKEY);
- rc = ssh_pki_export_privkey_file(origkey,
- NULL,
- NULL,
- NULL,
- LIBSSH_ECDSA_TESTKEY);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ NULL,
+ NULL,
+ NULL,
+ LIBSSH_ECDSA_TESTKEY,
+ format);
+ assert_int_equal(rc, 0);
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY,
NULL,
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
@@ -868,16 +910,17 @@ static void torture_pki_ecdsa_write_privkey(void **state)
NULL,
NULL,
&origkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(origkey);
unlink(LIBSSH_ECDSA_TESTKEY_PASSPHRASE);
- rc = ssh_pki_export_privkey_file(origkey,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- LIBSSH_ECDSA_TESTKEY_PASSPHRASE);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ torture_get_testkey_passphrase(),
+ NULL,
+ NULL,
+ LIBSSH_ECDSA_TESTKEY_PASSPHRASE,
+ format);
+ assert_int_equal(rc, 0);
/* Test with invalid passphrase */
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY_PASSPHRASE,
@@ -885,7 +928,7 @@ static void torture_pki_ecdsa_write_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
assert_null(privkey);
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY_PASSPHRASE,
@@ -893,15 +936,48 @@ static void torture_pki_ecdsa_write_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
}
+
+static void
+torture_pki_ecdsa_write_privkey(void **state)
+{
+ torture_pki_ecdsa_write_privkey_format(state, SSH_FILE_FORMAT_DEFAULT);
+}
+
+#ifdef HAVE_LIBCRYPTO
+static void
+torture_pki_ecdsa_write_privkey_pem(void **state)
+{
+ torture_pki_ecdsa_write_privkey_format(state, SSH_FILE_FORMAT_PEM);
+}
+
+static void
+torture_pki_ecdsa_write_privkey_openssh(void **state)
+{
+ torture_pki_ecdsa_write_privkey_format(state, SSH_FILE_FORMAT_OPENSSH);
+}
+
+static void
+torture_pki_ecdsa_import_export_privkey_base64_pem(void **state)
+{
+ torture_pki_ecdsa_import_export_privkey_base64_format(state,
+ SSH_FILE_FORMAT_PEM);
+}
+
+static void
+torture_pki_ecdsa_import_export_privkey_base64_openssh(void **state)
+{
+ torture_pki_ecdsa_import_export_privkey_base64_format(state,
+ SSH_FILE_FORMAT_OPENSSH);
+}
#endif /* HAVE_LIBCRYPTO */
static void torture_pki_ecdsa_name(void **state, const char *expected_name)
@@ -913,11 +989,11 @@ static void torture_pki_ecdsa_name(void **state, const char *expected_name)
(void) state; /* unused */
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, NULL, NULL, NULL, &key);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(key);
- etype_char =ssh_pki_key_ecdsa_name(key);
- assert_true(strcmp(etype_char, expected_name) == 0);
+ etype_char = ssh_pki_key_ecdsa_name(key);
+ assert_string_equal(etype_char, expected_name);
SSH_KEY_FREE(key);
}
@@ -958,15 +1034,18 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_pubkey_file,
setup_openssh_ecdsa_key_521,
teardown),
- cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
- setup_ecdsa_key_256,
- teardown),
- cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
- setup_ecdsa_key_384,
- teardown),
- cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
- setup_ecdsa_key_521,
- teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_default,
+ setup_ecdsa_key_256,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_default,
+ setup_ecdsa_key_384,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_default,
+ setup_ecdsa_key_521,
+ teardown),
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_comment,
setup_ecdsa_key_256,
teardown),
@@ -985,15 +1064,18 @@ int torture_run_tests(void) {
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),
- cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
- setup_openssh_ecdsa_key_384,
- teardown),
- cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
- setup_openssh_ecdsa_key_521,
- teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_default,
+ setup_openssh_ecdsa_key_256,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_default,
+ setup_openssh_ecdsa_key_384,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_default,
+ setup_openssh_ecdsa_key_521,
+ teardown),
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_publickey_from_privatekey,
setup_ecdsa_key_256,
teardown),
@@ -1058,7 +1140,6 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_cert_verify,
setup_ecdsa_key_521,
teardown),
-#ifdef HAVE_LIBCRYPTO
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey,
setup_ecdsa_key_256,
teardown),
@@ -1068,6 +1149,49 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey,
setup_ecdsa_key_521,
teardown),
+#ifdef HAVE_LIBCRYPTO
+ cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey_pem,
+ setup_ecdsa_key_256,
+ teardown),
+ cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey_pem,
+ setup_ecdsa_key_384,
+ teardown),
+ cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey_pem,
+ setup_ecdsa_key_521,
+ teardown),
+ cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey_openssh,
+ setup_ecdsa_key_256,
+ teardown),
+ cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey_openssh,
+ setup_ecdsa_key_384,
+ teardown),
+ cmocka_unit_test_setup_teardown(torture_pki_ecdsa_write_privkey_openssh,
+ setup_ecdsa_key_521,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_pem,
+ setup_ecdsa_key_256,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_pem,
+ setup_ecdsa_key_384,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_pem,
+ setup_ecdsa_key_521,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_openssh,
+ setup_ecdsa_key_256,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_openssh,
+ setup_ecdsa_key_384,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ecdsa_import_export_privkey_base64_openssh,
+ setup_ecdsa_key_521,
+ teardown),
#endif /* HAVE_LIBCRYPTO */
cmocka_unit_test(torture_pki_sign_data_ecdsa),
cmocka_unit_test(torture_pki_fail_sign_with_incompatible_hash),
diff --git a/tests/unittests/torture_pki_ecdsa_uri.c b/tests/unittests/torture_pki_ecdsa_uri.c
index 0fa6ecf3..fd3088b8 100644
--- a/tests/unittests/torture_pki_ecdsa_uri.c
+++ b/tests/unittests/torture_pki_ecdsa_uri.c
@@ -6,23 +6,19 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_pki.h"
#include "torture_key.h"
-#include "pki.c"
#define LIBSSH_ECDSA_TESTKEY "libssh_testkey.id_"
#define LIBSSH_ECDSA_TESTKEY_PEM "libssh_testkey_pem.id_"
-#define SOFTHSM_CONF "softhsm.conf"
-#define PUB_URI_FMT_256 "pkcs11:token=ecdsa256;object=ecdsa256;type=public"
-#define PRIV_URI_FMT_256 "pkcs11:token=ecdsa256;object=ecdsa256;type=private?pin-value=1234"
-#define PUB_URI_FMT_384 "pkcs11:token=ecdsa384;object=ecdsa384;type=public"
-#define PRIV_URI_FMT_384 "pkcs11:token=ecdsa384;object=ecdsa384;type=private?pin-value=1234"
-#define PUB_URI_FMT_521 "pkcs11:token=ecdsa521;object=ecdsa521;type=public"
-#define PRIV_URI_FMT_521 "pkcs11:token=ecdsa521;object=ecdsa521;type=private?pin-value=1234"
-#define PRIV_URI_FMT_256_NO_PUB "pkcs11:token=ecdsa256_no_pub_uri;object=ecdsa256_no_pub_uri;type=private?pin-value=1234"
-#define PRIV_URI_FMT_384_NO_PUB "pkcs11:token=ecdsa384_no_pub_uri;object=ecdsa384_no_pub_uri;type=private?pin-value=1234"
-#define PRIV_URI_FMT_521_NO_PUB "pkcs11:token=ecdsa521_no_pub_uri;object=ecdsa521_no_pub_uri;type=private?pin-value=1234"
+#define LABEL_256 "ecdsa256"
+#define LABEL_384 "ecdsa384"
+#define LABEL_521 "ecdsa521"
+#define PUB_URI_FMT "pkcs11:token=%s;object=%s;type=public"
+#define PRIV_URI_FMT "pkcs11:token=%s;object=%s;type=private?pin-value=1234"
+#define PRIV_URI_NO_PUB_FMT "pkcs11:token=%s_no_pub_uri;object=%s_no_pub_uri;type=private?pin-value=1234"
/** PKCS#11 URIs with invalid fields**/
@@ -31,7 +27,7 @@
#define PUB_URI_FMT_384_INVALID_TOKEN "pkcs11:token=ecdsa521;object=ecdsa384;type=public"
#define PUB_URI_FMT_521_INVALID_OBJECT "pkcs11:token=ecdsa521;object=ecdsa384;type=public"
-const char template[] = "temp_dir_XXXXXX";
+const char template[] = "/tmp/temp_dir_XXXXXX";
const unsigned char INPUT[] = "1234567890123456789012345678901234567890"
"123456789012345678901234";
struct pki_st {
@@ -80,7 +76,6 @@ static int setup_directory_structure(void **state)
struct pki_st *test_state = NULL;
char *temp_dir;
int rc;
- char conf_path[1024] = {0};
test_state = (struct pki_st *)malloc(sizeof(struct pki_st));
assert_non_null(test_state);
@@ -93,15 +88,13 @@ static int setup_directory_structure(void **state)
rc = torture_change_dir(temp_dir);
assert_int_equal(rc, 0);
+ SAFE_FREE(temp_dir);
test_state->temp_dir = torture_get_current_working_dir();
assert_non_null(test_state->temp_dir);
*state = test_state;
- snprintf(conf_path, sizeof(conf_path), "%s/softhsm.conf", test_state->temp_dir);
- setenv("SOFTHSM2_CONF", conf_path, 1);
-
setup_tokens_ecdsa(state, 256, "ecdsa256", "1");
setup_tokens_ecdsa(state, 384, "ecdsa384", "1");
setup_tokens_ecdsa(state, 521, "ecdsa521", "1");
@@ -117,7 +110,7 @@ static int teardown_directory_structure(void **state)
struct pki_st *test_state = *state;
int rc;
- unsetenv("SOFTHSM2_CONF");
+ torture_cleanup_tokens(test_state->temp_dir);
rc = torture_change_dir(test_state->orig_dir);
assert_int_equal(rc, 0);
@@ -132,39 +125,47 @@ static int teardown_directory_structure(void **state)
return 0;
}
-static void torture_pki_ecdsa_import_pubkey_uri(void **state, const char *uri)
+static void torture_pki_ecdsa_import_pubkey_uri(void **state, const char *label)
{
+ char uri[128] = {0};
ssh_key pubkey = NULL;
int rc;
+ rc = snprintf(uri, sizeof(uri), PUB_URI_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(uri) - 1);
+
rc = ssh_pki_import_pubkey_file(uri, &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_key_is_public(pubkey);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(pubkey);
}
static void torture_pki_ecdsa_import_pubkey_uri_256(void **state)
{
- torture_pki_ecdsa_import_pubkey_uri(state, PUB_URI_FMT_256);
+ torture_pki_ecdsa_import_pubkey_uri(state, LABEL_256);
}
static void torture_pki_ecdsa_import_pubkey_uri_384(void **state)
{
- torture_pki_ecdsa_import_pubkey_uri(state, PUB_URI_FMT_384);
+ torture_pki_ecdsa_import_pubkey_uri(state, LABEL_384);
}
static void torture_pki_ecdsa_import_pubkey_uri_521(void **state)
{
- torture_pki_ecdsa_import_pubkey_uri(state, PUB_URI_FMT_521);
+ torture_pki_ecdsa_import_pubkey_uri(state, LABEL_521);
}
-static void torture_pki_ecdsa_publickey_from_privatekey_uri(void **state, const char *uri, const char *type)
+static void
+torture_pki_ecdsa_publickey_from_privatekey_uri(void **state,
+ const char *label,
+ const char *type)
{
int rc;
+ char uri[128] = {0};
ssh_key privkey = NULL;
ssh_key pubkey = NULL;
ssh_string pblob = NULL;
@@ -175,38 +176,38 @@ static void torture_pki_ecdsa_publickey_from_privatekey_uri(void **state, const
char pub_filename_generated[1024];
char pub_filename_pem[1024];
+ rc = snprintf(uri, sizeof(uri), PRIV_URI_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(uri) - 1);
+
rc = ssh_pki_import_privkey_file(uri,
NULL,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
- assert_true(rc == 0);
assert_non_null(privkey);
rc = ssh_pki_export_pubkey_blob(privkey, &pblob);
assert_return_code(rc, errno);
- assert_true(rc == SSH_OK);
assert_non_null(pblob);
+ ssh_string_free(pblob);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
- assert_true(rc == SSH_OK);
assert_non_null(pubkey);
snprintf(pub_filename, sizeof(pub_filename), "%s%s%s", LIBSSH_ECDSA_TESTKEY, type, ".pub");
snprintf(pub_filename_generated, sizeof(pub_filename_generated), "%s%s%s",
- LIBSSH_ECDSA_TESTKEY_PEM, type, "generated.pub");
+ LIBSSH_ECDSA_TESTKEY_PEM, type, "generated.pub");
snprintf(pub_filename_pem, sizeof(pub_filename_pem), "%s%s%s", LIBSSH_ECDSA_TESTKEY_PEM, type, ".pub");
rc = torture_read_one_line(pub_filename,
pubkey_original,
sizeof(pubkey_original));
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
rc = ssh_pki_export_pubkey_file(pubkey, pub_filename_generated);
assert_return_code(rc, errno);
- assert_true(rc == 0);
/* remove the public key, generate it from the private key and write it. */
unlink(pub_filename);
@@ -219,28 +220,47 @@ static void torture_pki_ecdsa_publickey_from_privatekey_uri(void **state, const
rc = torture_read_one_line(pub_filename_pem,
pubkey_generated,
sizeof(pubkey_generated));
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
- assert_int_equal(strncmp(pubkey_original, pubkey_generated, strlen(pubkey_original)), 0);
+ assert_memory_equal(pubkey_original, pubkey_generated, strlen(pubkey_original));
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
}
-static void import_pubkey_without_loading_public_uri(void **state, const char *uri, const char *type)
+static void torture_pki_ecdsa_publickey_from_privatekey_uri_256(void **state)
+{
+ torture_pki_ecdsa_publickey_from_privatekey_uri(state, LABEL_256, "ecdsa256");
+}
+
+static void torture_pki_ecdsa_publickey_from_privatekey_uri_384(void **state)
+{
+ torture_pki_ecdsa_publickey_from_privatekey_uri(state, LABEL_384, "ecdsa384");
+}
+
+static void torture_pki_ecdsa_publickey_from_privatekey_uri_521(void **state)
+{
+ torture_pki_ecdsa_publickey_from_privatekey_uri(state, LABEL_521, "ecdsa521");
+}
+
+static void
+import_pubkey_without_loading_public_uri(void **state, const char *label)
{
int rc;
+ char uri[128] = {0};
ssh_key privkey = NULL;
ssh_key pubkey = NULL;
ssh_string pblob = NULL;
+ rc = snprintf(uri, sizeof(uri), PRIV_URI_NO_PUB_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(uri) - 1);
+
rc = ssh_pki_import_privkey_file(uri,
NULL,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
- assert_true(rc == 0);
assert_non_null(privkey);
rc = ssh_pki_export_pubkey_blob(privkey, &pblob);
@@ -252,48 +272,41 @@ static void import_pubkey_without_loading_public_uri(void **state, const char *u
assert_null(pubkey);
SSH_KEY_FREE(privkey);
- SSH_KEY_FREE(pubkey);
-}
-
-static void torture_pki_ecdsa_publickey_from_privatekey_uri_256(void **state)
-{
- torture_pki_ecdsa_publickey_from_privatekey_uri(state, PRIV_URI_FMT_256, "ecdsa256");
-}
-
-static void torture_pki_ecdsa_publickey_from_privatekey_uri_384(void **state)
-{
- torture_pki_ecdsa_publickey_from_privatekey_uri(state, PRIV_URI_FMT_384, "ecdsa384");
-}
-
-static void torture_pki_ecdsa_publickey_from_privatekey_uri_521(void **state)
-{
- torture_pki_ecdsa_publickey_from_privatekey_uri(state, PRIV_URI_FMT_521, "ecdsa521");
}
static void torture_pki_ecdsa_import_pubkey_without_loading_public_uri_256(void **state)
{
- import_pubkey_without_loading_public_uri(state, PRIV_URI_FMT_256_NO_PUB, "ecdsa256_no_pub_uri");
+ import_pubkey_without_loading_public_uri(state, LABEL_256);
}
static void torture_pki_ecdsa_import_pubkey_without_loading_public_uri_384(void **state)
{
- import_pubkey_without_loading_public_uri(state, PRIV_URI_FMT_384_NO_PUB, "ecdsa384_no_pub_uri");
+ import_pubkey_without_loading_public_uri(state, LABEL_384);
}
static void torture_pki_ecdsa_import_pubkey_without_loading_public_uri_521(void **state)
{
- import_pubkey_without_loading_public_uri(state, PRIV_URI_FMT_521_NO_PUB, "ecdsa521_no_pub_uri");
+ import_pubkey_without_loading_public_uri(state, LABEL_521);
}
-static void torture_ecdsa_sign_verify_uri(void **state, const char *uri, enum ssh_digest_e dig_type)
+static void
+torture_ecdsa_sign_verify_uri(void **state,
+ const char *label,
+ enum ssh_digest_e dig_type)
{
int rc;
+ char uri[128] = {0};
ssh_key privkey = NULL, pubkey = NULL;
ssh_signature sign = NULL;
enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN;
const char *type_char = NULL;
const char *etype_char = NULL;
- ssh_session session=ssh_new();
+ ssh_session session = ssh_new();
+
+ assert_non_null(session);
+
+ rc = snprintf(uri, sizeof(uri), PRIV_URI_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(uri) - 1);
rc = ssh_pki_import_privkey_file(uri,
NULL,
@@ -301,12 +314,10 @@ static void torture_ecdsa_sign_verify_uri(void **state, const char *uri, enum ss
NULL,
&privkey);
assert_return_code(rc, errno);
- assert_true(rc == 0);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
- assert_int_equal(rc, SSH_OK);
assert_non_null(pubkey);
sign = pki_do_sign(privkey, INPUT, sizeof(INPUT), dig_type);
@@ -314,13 +325,12 @@ static void torture_ecdsa_sign_verify_uri(void **state, const char *uri, enum ss
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
assert_return_code(rc, errno);
- assert_true(rc == SSH_OK);
type = ssh_key_type(privkey);
type_char = ssh_key_type_to_char(type);
etype_char = ssh_pki_key_ecdsa_name(privkey);
- switch(dig_type) {
+ switch (dig_type) {
case SSH_DIGEST_SHA256:
assert_true(type == SSH_KEYTYPE_ECDSA_P256);
assert_string_equal(type_char, "ecdsa-sha2-nistp256");
@@ -340,6 +350,7 @@ static void torture_ecdsa_sign_verify_uri(void **state, const char *uri, enum ss
printf("Invalid hash type: %d\n", dig_type);
}
+ ssh_free(session);
ssh_signature_free(sign);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
@@ -347,22 +358,24 @@ static void torture_ecdsa_sign_verify_uri(void **state, const char *uri, enum ss
static void torture_ecdsa_sign_verify_uri_256(void **state)
{
- torture_ecdsa_sign_verify_uri(state, PRIV_URI_FMT_256, SSH_DIGEST_SHA256);
+ torture_ecdsa_sign_verify_uri(state, LABEL_256, SSH_DIGEST_SHA256);
}
static void torture_ecdsa_sign_verify_uri_384(void **state)
{
- torture_ecdsa_sign_verify_uri(state, PRIV_URI_FMT_384, SSH_DIGEST_SHA384);
+ torture_ecdsa_sign_verify_uri(state, LABEL_384, SSH_DIGEST_SHA384);
}
static void torture_ecdsa_sign_verify_uri_521(void **state)
{
- torture_ecdsa_sign_verify_uri(state, PRIV_URI_FMT_521, SSH_DIGEST_SHA512);
+ torture_ecdsa_sign_verify_uri(state, LABEL_521, SSH_DIGEST_SHA512);
}
-static void torture_pki_ecdsa_duplicate_key_uri(void **state, const char *priv_uri, const char *pub_uri)
+static void torture_pki_ecdsa_duplicate_key_uri(void **state, const char *label)
{
int rc;
+ char pub_uri[128] = {0};
+ char priv_uri[128] = {0};
char *b64_key = NULL;
char *b64_key_gen = NULL;
ssh_key pubkey = NULL;
@@ -372,12 +385,17 @@ static void torture_pki_ecdsa_duplicate_key_uri(void **state, const char *priv_u
(void) state;
+ rc = snprintf(pub_uri, sizeof(pub_uri), PUB_URI_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(pub_uri) - 1);
+ rc = snprintf(priv_uri, sizeof(priv_uri), PRIV_URI_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(priv_uri) - 1);
+
rc = ssh_pki_import_pubkey_file(pub_uri, &pubkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(b64_key);
rc = ssh_pki_import_privkey_file(priv_uri,
@@ -385,27 +403,27 @@ static void torture_pki_ecdsa_duplicate_key_uri(void **state, const char *priv_u
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
privkey_dup = ssh_key_dup(privkey);
assert_non_null(privkey_dup);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey_dup);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey_dup);
rc = ssh_pki_export_pubkey_base64(pubkey_dup, &b64_key_gen);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(b64_key_gen);
assert_string_equal(b64_key, b64_key_gen);
rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
rc = ssh_key_cmp(pubkey, pubkey_dup, SSH_KEY_CMP_PUBLIC);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
SSH_KEY_FREE(pubkey);
SSH_KEY_FREE(pubkey_dup);
@@ -417,21 +435,23 @@ static void torture_pki_ecdsa_duplicate_key_uri(void **state, const char *priv_u
static void torture_pki_ecdsa_duplicate_key_uri_256(void **state)
{
- torture_pki_ecdsa_duplicate_key_uri(state, PRIV_URI_FMT_256, PUB_URI_FMT_256);
+ torture_pki_ecdsa_duplicate_key_uri(state, LABEL_256);
}
static void torture_pki_ecdsa_duplicate_key_uri_384(void **state)
{
- torture_pki_ecdsa_duplicate_key_uri(state, PRIV_URI_FMT_384, PUB_URI_FMT_384);
+ torture_pki_ecdsa_duplicate_key_uri(state, LABEL_384);
}
static void torture_pki_ecdsa_duplicate_key_uri_521(void **state)
{
- torture_pki_ecdsa_duplicate_key_uri(state, PRIV_URI_FMT_521, PUB_URI_FMT_521);
+ torture_pki_ecdsa_duplicate_key_uri(state, LABEL_521);
}
-static void torture_pki_ecdsa_duplicate_then_demote_uri(void **state, const char *priv_uri)
+static void
+torture_pki_ecdsa_duplicate_then_demote_uri(void **state, const char *label)
{
+ char priv_uri[128] = {0};
ssh_key pubkey = NULL;
ssh_key privkey = NULL;
ssh_key privkey_dup = NULL;
@@ -439,12 +459,15 @@ static void torture_pki_ecdsa_duplicate_then_demote_uri(void **state, const char
(void) state;
+ rc = snprintf(priv_uri, sizeof(priv_uri), PRIV_URI_FMT, label, label);
+ assert_in_range(rc, 0, sizeof(priv_uri) - 1);
+
rc = ssh_pki_import_privkey_file(priv_uri,
NULL,
NULL,
NULL,
&privkey);
- assert_int_equal(rc, 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
privkey_dup = ssh_key_dup(privkey);
@@ -452,7 +475,7 @@ static void torture_pki_ecdsa_duplicate_then_demote_uri(void **state, const char
assert_int_equal(privkey->ecdsa_nid, privkey_dup->ecdsa_nid);
rc = ssh_pki_export_privkey_to_pubkey(privkey_dup, &pubkey);
- assert_int_equal(rc, 0);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
assert_int_equal(pubkey->ecdsa_nid, privkey->ecdsa_nid);
@@ -463,17 +486,17 @@ static void torture_pki_ecdsa_duplicate_then_demote_uri(void **state, const char
static void torture_pki_ecdsa_duplicate_then_demote_uri_256(void **state)
{
- torture_pki_ecdsa_duplicate_then_demote_uri(state, PRIV_URI_FMT_256);
+ torture_pki_ecdsa_duplicate_then_demote_uri(state, LABEL_256);
}
static void torture_pki_ecdsa_duplicate_then_demote_uri_384(void **state)
{
- torture_pki_ecdsa_duplicate_then_demote_uri(state, PRIV_URI_FMT_384);
+ torture_pki_ecdsa_duplicate_then_demote_uri(state, LABEL_384);
}
static void torture_pki_ecdsa_duplicate_then_demote_uri_521(void **state)
{
- torture_pki_ecdsa_duplicate_then_demote_uri(state, PRIV_URI_FMT_521);
+ torture_pki_ecdsa_duplicate_then_demote_uri(state, LABEL_521);
}
static void torture_pki_ecdsa_import_pubkey_uri_invalid_configurations(void **state)
@@ -510,9 +533,6 @@ static void torture_pki_ecdsa_import_pubkey_uri_invalid_configurations(void **st
&pubkey);
assert_int_not_equal(rc, 0);
assert_null(pubkey);
-
- SSH_KEY_FREE(privkey);
- SSH_KEY_FREE(pubkey);
}
int torture_run_tests(void) {
@@ -540,15 +560,16 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_pki_ecdsa_import_pubkey_without_loading_public_uri_384),
cmocka_unit_test(torture_pki_ecdsa_import_pubkey_without_loading_public_uri_521),
};
-
ssh_session session = ssh_new();
int verbosity = SSH_LOG_FUNCTIONS;
- ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+
ssh_init();
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, setup_directory_structure, teardown_directory_structure);
+ ssh_free(session);
ssh_finalize();
return rc;
diff --git a/tests/unittests/torture_pki_ed25519.c b/tests/unittests/torture_pki_ed25519.c
index ff59b190..0142fbe3 100644
--- a/tests/unittests/torture_pki_ed25519.c
+++ b/tests/unittests/torture_pki_ed25519.c
@@ -2,10 +2,10 @@
#define LIBSSH_STATIC
+#include "pki.c"
#include "torture.h"
#include "torture_key.h"
#include "torture_pki.h"
-#include "pki.c"
#include <sys/stat.h>
#include <fcntl.h>
@@ -247,10 +247,10 @@ static void torture_pki_ed25519_import_export_privkey_base64(void **state)
assert_non_null(key);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ED25519);
+ assert_int_equal(type, SSH_KEYTYPE_ED25519);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
rc = ssh_pki_export_privkey_base64(key,
passphrase,
@@ -270,10 +270,10 @@ static void torture_pki_ed25519_import_export_privkey_base64(void **state)
assert_non_null(key);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_ED25519);
+ assert_int_equal(type, SSH_KEYTYPE_ED25519);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_STRING_FREE_CHAR(b64_key);
SSH_KEY_FREE(key);
@@ -317,6 +317,11 @@ static void torture_pki_ed25519_import_cert_file(void **state)
(void) state; /* unused */
+ /* Importing public key as cert should fail */
+ rc = ssh_pki_import_cert_file(LIBSSH_ED25519_TESTKEY ".pub", &cert);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(cert);
+
rc = ssh_pki_import_cert_file(LIBSSH_ED25519_TESTKEY "-cert.pub", &cert);
assert_true(rc == 0);
assert_non_null(cert);
@@ -448,7 +453,7 @@ static void torture_pki_ed25519_generate_key(void **state)
assert_true(strcmp(type_char, "ssh-ed25519") == 0);
/* try an invalid signature */
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
raw_sig_data = ssh_string_data(sign->raw_sig);
#else
raw_sig_data = (uint8_t *)sign->ed25519_sig;
@@ -503,70 +508,83 @@ static void torture_pki_ed25519_cert_verify(void **state)
ssh_free(session);
}
-static void torture_pki_ed25519_write_privkey(void **state)
+static void
+torture_pki_ed25519_write_privkey_format(void **state,
+ enum ssh_file_format_e format)
{
ssh_key origkey = NULL;
ssh_key privkey = NULL;
int rc;
- (void) state; /* unused */
+ (void)state; /* unused */
+
+ /* Skip test if in FIPS mode */
+ if (ssh_fips_mode()) {
+ skip();
+ }
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
- NULL,
- NULL,
- NULL,
- &origkey);
- assert_true(rc == 0);
+ NULL,
+ NULL,
+ NULL,
+ &origkey);
+ assert_int_equal(rc, 0);
assert_non_null(origkey);
unlink(LIBSSH_ED25519_TESTKEY);
- rc = ssh_pki_export_privkey_file(origkey,
- NULL,
- NULL,
- NULL,
- LIBSSH_ED25519_TESTKEY);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ NULL,
+ NULL,
+ NULL,
+ LIBSSH_ED25519_TESTKEY,
+ format);
+ assert_int_equal(rc, 0);
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
- NULL,
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
+ NULL,
+ NULL,
+ NULL,
+ &privkey);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
unlink(LIBSSH_ED25519_TESTKEY);
SSH_KEY_FREE(privkey);
/* do the same with passphrase */
- rc = ssh_pki_export_privkey_file(origkey,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- LIBSSH_ED25519_TESTKEY);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ torture_get_testkey_passphrase(),
+ NULL,
+ NULL,
+ LIBSSH_ED25519_TESTKEY,
+ format);
+ assert_int_equal(rc, 0);
- rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
- NULL,
- NULL,
- NULL,
- &privkey);
- /* opening without passphrase should fail */
- assert_true(rc == SSH_ERROR);
+ /* Opening passphrase protected key will prompt for the pin interactively,
+ * which would hang in the test */
+ if (format != SSH_FILE_FORMAT_PEM) {
+ rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
+ NULL,
+ NULL,
+ NULL,
+ &privkey);
+ /* opening without passphrase should fail */
+ assert_int_equal(rc, SSH_ERROR);
+ }
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- &privkey);
- assert_true(rc == 0);
+ torture_get_testkey_passphrase(),
+ NULL,
+ NULL,
+ &privkey);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
unlink(LIBSSH_ED25519_TESTKEY);
SSH_KEY_FREE(origkey);
@@ -578,16 +596,17 @@ static void torture_pki_ed25519_write_privkey(void **state)
NULL,
NULL,
&origkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(origkey);
unlink(LIBSSH_ED25519_TESTKEY_PASSPHRASE);
- rc = ssh_pki_export_privkey_file(origkey,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- LIBSSH_ED25519_TESTKEY_PASSPHRASE);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ torture_get_testkey_passphrase(),
+ NULL,
+ NULL,
+ LIBSSH_ED25519_TESTKEY_PASSPHRASE,
+ format);
+ assert_int_equal(rc, 0);
/* Test with invalid passphrase */
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY_PASSPHRASE,
@@ -595,23 +614,43 @@ static void torture_pki_ed25519_write_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY_PASSPHRASE,
torture_get_testkey_passphrase(),
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_int_equal(rc, 0);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
}
+static void
+torture_pki_ed25519_write_privkey(void **state)
+{
+ torture_pki_ed25519_write_privkey_format(state, SSH_FILE_FORMAT_DEFAULT);
+}
+
+#ifdef HAVE_LIBCRYPTO
+static void
+torture_pki_ed25519_write_privkey_pem(void **state)
+{
+ torture_pki_ed25519_write_privkey_format(state, SSH_FILE_FORMAT_PEM);
+}
+
+static void
+torture_pki_ed25519_write_privkey_openssh(void **state)
+{
+ torture_pki_ed25519_write_privkey_format(state, SSH_FILE_FORMAT_OPENSSH);
+}
+#endif
+
static void torture_pki_ed25519_sign(void **state)
{
ssh_key privkey = NULL;
@@ -690,7 +729,7 @@ static void torture_pki_ed25519_sign_openssh_privkey_passphrase(void **state)
SSH_STRING_FREE(blob);
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
static void torture_pki_ed25519_sign_pkcs8_privkey(void **state)
{
ssh_key privkey = NULL;
@@ -766,7 +805,7 @@ static void torture_pki_ed25519_sign_pkcs8_privkey_passphrase(void **state)
SSH_KEY_FREE(privkey);
SSH_STRING_FREE(blob);
}
-#endif /* HAVE_OPENSSL_ED25519 */
+#endif /* HAVE_LIBCRYPTO */
static void torture_pki_ed25519_verify(void **state){
ssh_key pubkey = NULL;
@@ -805,7 +844,7 @@ static void torture_pki_ed25519_verify(void **state){
assert_true(rc == SSH_OK);
/* Alter signature and expect verification error */
-#if defined(HAVE_OPENSSL_ED25519)
+#ifdef HAVE_LIBCRYPTO
raw_sig_data = ssh_string_data(sig->raw_sig);
#else
raw_sig_data = (uint8_t *)sig->ed25519_sig;
@@ -1015,9 +1054,16 @@ int torture_run_tests(void) {
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
+#ifdef HAVE_LIBCRYPTO
cmocka_unit_test(torture_pki_ed25519_sign_pkcs8_privkey),
cmocka_unit_test(torture_pki_ed25519_sign_pkcs8_privkey_passphrase),
+ cmocka_unit_test_setup_teardown(torture_pki_ed25519_write_privkey_pem,
+ setup_ed25519_key,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_ed25519_write_privkey_openssh,
+ setup_ed25519_key,
+ teardown),
#endif
cmocka_unit_test(torture_pki_ed25519_verify),
cmocka_unit_test(torture_pki_ed25519_verify_bad),
diff --git a/tests/unittests/torture_pki_rsa.c b/tests/unittests/torture_pki_rsa.c
index a9867069..4da6b148 100644
--- a/tests/unittests/torture_pki_rsa.c
+++ b/tests/unittests/torture_pki_rsa.c
@@ -1,15 +1,16 @@
#include "config.h"
+#include "libssh/libssh.h"
#define LIBSSH_STATIC
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_pki.h"
#include "torture_key.h"
-#include "pki.c"
#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
#define LIBSSH_RSA_TESTKEY_PASSPHRASE "libssh_testkey_passphrase.id_rsa"
@@ -164,7 +165,7 @@ static void torture_pki_rsa_import_privkey_base64_NULL_key(void **state)
NULL,
NULL,
NULL);
- assert_true(rc == -1);
+ assert_int_equal(rc, -1);
}
@@ -178,16 +179,18 @@ static void torture_pki_rsa_import_privkey_base64_NULL_str(void **state)
/* test if it returns -1 if key_str is NULL */
rc = ssh_pki_import_privkey_base64(NULL, passphrase, NULL, NULL, &key);
- assert_true(rc == -1);
+ assert_int_equal(rc, -1);
SSH_KEY_FREE(key);
}
-static void torture_pki_rsa_import_privkey_base64(void **state)
+static void
+torture_pki_rsa_import_export_privkey_base64_format(void **state,
+ enum ssh_file_format_e format)
{
int rc;
- char *key_str = NULL;
- ssh_key key = NULL;
+ char *key_str = NULL, *new_key_str = NULL;
+ ssh_key key = NULL, new_key = NULL;
const char *passphrase = torture_get_testkey_passphrase();
enum ssh_keytypes_e type;
@@ -196,21 +199,62 @@ static void torture_pki_rsa_import_privkey_base64(void **state)
key_str = torture_pki_read_file(LIBSSH_RSA_TESTKEY);
assert_non_null(key_str);
+ /* Import test key */
rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(key);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_RSA);
+ assert_int_equal(type, SSH_KEYTYPE_RSA);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
rc = ssh_key_is_public(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
+
+ /* Export */
+ rc = ssh_pki_export_privkey_base64_format(key,
+ passphrase,
+ NULL,
+ NULL,
+ &new_key_str,
+ format);
+ assert_int_equal(rc, SSH_OK);
+ assert_non_null(new_key_str);
+
+ /* and import again */
+ rc = ssh_pki_import_privkey_base64(new_key_str,
+ passphrase,
+ NULL,
+ NULL,
+ &new_key);
+ assert_int_equal(rc, 0);
+ assert_non_null(new_key);
+
+ type = ssh_key_type(new_key);
+ assert_int_equal(type, SSH_KEYTYPE_RSA);
+
+ rc = ssh_key_is_private(new_key);
+ assert_int_equal(rc, 1);
+
+ rc = ssh_key_is_public(new_key);
+ assert_int_equal(rc, 1);
+
+ rc = ssh_key_cmp(key, new_key, SSH_KEY_CMP_PRIVATE|SSH_KEY_CMP_PUBLIC);
+ assert_int_equal(rc, 0);
free(key_str);
+ free(new_key_str);
SSH_KEY_FREE(key);
+ SSH_KEY_FREE(new_key);
+}
+
+static void
+torture_pki_rsa_import_export_privkey_base64(void **state)
+{
+ torture_pki_rsa_import_export_privkey_base64_format(state,
+ SSH_FILE_FORMAT_DEFAULT);
}
static void torture_pki_rsa_import_privkey_base64_comment(void **state)
@@ -234,17 +278,17 @@ static void torture_pki_rsa_import_privkey_base64_comment(void **state)
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_return_code(rc, errno);
assert_non_null(key);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_RSA);
+ assert_int_equal(type, SSH_KEYTYPE_RSA);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
rc = ssh_key_is_public(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
free(key_str);
free(file_str);
@@ -272,17 +316,17 @@ static void torture_pki_rsa_import_privkey_base64_whitespace(void **state)
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_return_code(rc, errno);
assert_non_null(key);
type = ssh_key_type(key);
- assert_true(type == SSH_KEYTYPE_RSA);
+ assert_int_equal(type, SSH_KEYTYPE_RSA);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
rc = ssh_key_is_public(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
free(key_str);
free(file_str);
@@ -303,14 +347,14 @@ static void torture_pki_rsa_publickey_from_privatekey(void **state)
NULL,
NULL,
&key);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
SSH_KEY_FREE(key);
@@ -330,14 +374,19 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
ssh_key privkey = NULL;
ssh_key cert = NULL;
- (void) state; /* unused */
+ (void)state; /* unused */
+
+ /* Importing public key as cert should fail */
+ rc = ssh_pki_import_cert_file(LIBSSH_RSA_TESTKEY ".pub", &cert);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_null(cert);
rc = ssh_pki_import_cert_file(LIBSSH_RSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(cert);
rc = ssh_pki_import_pubkey_file(LIBSSH_RSA_TESTKEY ".pub", &pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0),
@@ -345,32 +394,48 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
/* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(pubkey, NULL);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
/* A public key doesn't have a cert, copy should fail. */
assert_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
/* Copying the cert to non-cert keys should work fine. */
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(privkey->cert);
/* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
+
+ SSH_KEY_FREE(privkey);
+ SSH_KEY_FREE(pubkey);
+
+ /* Generate different key and try to assign it this certificate */
+ rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &privkey);
+ assert_return_code(rc, errno);
+ assert_non_null(privkey);
+ rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
+ assert_return_code(rc, errno);
+ assert_non_null(pubkey);
+
+ rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
+ assert_int_equal(rc, SSH_ERROR);
SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
@@ -385,14 +450,14 @@ static void torture_pki_rsa_import_cert_file(void **state) {
(void) state; /* unused */
rc = ssh_pki_import_cert_file(LIBSSH_RSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(cert);
type = ssh_key_type(cert);
- assert_true(type == SSH_KEYTYPE_RSA_CERT01);
+ assert_int_equal(type, SSH_KEYTYPE_RSA_CERT01);
rc = ssh_key_is_public(cert);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(cert);
}
@@ -417,7 +482,7 @@ static void torture_pki_rsa_publickey_base64(void **state)
}
type = ssh_key_type_from_name(q);
- assert_true(type == SSH_KEYTYPE_RSA);
+ assert_int_equal(type, SSH_KEYTYPE_RSA);
q = ++p;
while (p != NULL && *p != '\0' && *p != ' ') p++;
@@ -426,11 +491,11 @@ static void torture_pki_rsa_publickey_base64(void **state)
}
rc = ssh_pki_import_pubkey_base64(q, type, &key);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(b64_key);
assert_string_equal(q, b64_key);
@@ -457,20 +522,20 @@ static void torture_pki_rsa_generate_pubkey_from_privkey(void **state) {
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_RSA_TESTKEY ".pub");
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub",
pubkey_generated,
sizeof(pubkey_generated));
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
len = torture_pubkey_len(torture_get_testkey_pub(SSH_KEYTYPE_RSA));
assert_memory_equal(torture_get_testkey_pub(SSH_KEYTYPE_RSA),
@@ -494,11 +559,11 @@ static void torture_pki_rsa_duplicate_key(void **state)
(void) state;
rc = ssh_pki_import_pubkey_file(LIBSSH_RSA_TESTKEY ".pub", &pubkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(b64_key);
rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY,
@@ -506,27 +571,27 @@ static void torture_pki_rsa_duplicate_key(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
privkey_dup = ssh_key_dup(privkey);
assert_non_null(privkey_dup);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey_dup);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey_dup);
rc = ssh_pki_export_pubkey_base64(pubkey_dup, &b64_key_gen);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(b64_key_gen);
assert_string_equal(b64_key, b64_key_gen);
rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
rc = ssh_key_cmp(pubkey, pubkey_dup, SSH_KEY_CMP_PUBLIC);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
SSH_KEY_FREE(pubkey);
SSH_KEY_FREE(pubkey_dup);
@@ -541,12 +606,16 @@ static void torture_pki_rsa_generate_key(void **state)
int rc;
ssh_key key = NULL, pubkey = NULL;
ssh_signature sign = NULL;
- ssh_session session=ssh_new();
+ ssh_session session = ssh_new();
+ int verbosity = torture_libssh_verbosity();
+
(void) state;
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+
if (!ssh_fips_mode()) {
rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -554,7 +623,7 @@ static void torture_pki_rsa_generate_key(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
ssh_signature_free(sign);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
@@ -563,7 +632,7 @@ static void torture_pki_rsa_generate_key(void **state)
}
rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -571,7 +640,7 @@ static void torture_pki_rsa_generate_key(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
ssh_signature_free(sign);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
@@ -579,7 +648,7 @@ static void torture_pki_rsa_generate_key(void **state)
pubkey = NULL;
rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 4096, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
assert_int_equal(rc, SSH_OK);
@@ -587,7 +656,7 @@ static void torture_pki_rsa_generate_key(void **state)
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
ssh_signature_free(sign);
SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey);
@@ -609,11 +678,11 @@ static void torture_pki_rsa_sha2(void **state)
/* Setup */
rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, NULL, NULL, NULL, &key);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(key);
rc = ssh_pki_import_cert_file(LIBSSH_RSA_TESTKEY "-cert.pub", &cert);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(cert);
/* Get the public key to verify signature */
@@ -665,6 +734,44 @@ static void torture_pki_rsa_sha2(void **state)
ssh_free(session);
}
+static void torture_pki_rsa_key_size(void **state)
+{
+ int rc;
+ ssh_key key = NULL, pubkey = NULL;
+ ssh_signature sign = NULL;
+ ssh_session session=ssh_new();
+ unsigned int length = 4096;
+
+ (void) state;
+
+ rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key);
+ assert_return_code(rc, errno);
+ 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, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
+ assert_non_null(sign);
+ rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
+ assert_ssh_return_code(session, rc);
+
+ /* Set the minimum RSA key size to 4k */
+ rc = ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &length);
+ assert_ssh_return_code(session, rc);
+
+ /* the verification should fail now */
+ rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
+ assert_int_equal(rc, SSH_ERROR);
+
+ ssh_signature_free(sign);
+ SSH_KEY_FREE(key);
+ SSH_KEY_FREE(pubkey);
+ key = NULL;
+ pubkey = NULL;
+
+ ssh_free(session);
+}
+
static int test_sign_verify_data(ssh_key key,
enum ssh_digest_e hash_type,
const unsigned char *input,
@@ -765,8 +872,9 @@ static void torture_pki_fail_sign_with_incompatible_hash(void **state)
SSH_KEY_FREE(key);
}
-#ifdef HAVE_LIBCRYPTO
-static void torture_pki_rsa_write_privkey(void **state)
+static void
+torture_pki_rsa_write_privkey_format(void **state,
+ enum ssh_file_format_e format)
{
ssh_key origkey = NULL;
ssh_key privkey = NULL;
@@ -779,28 +887,29 @@ static void torture_pki_rsa_write_privkey(void **state)
NULL,
NULL,
&origkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(origkey);
unlink(LIBSSH_RSA_TESTKEY);
- rc = ssh_pki_export_privkey_file(origkey,
- NULL,
- NULL,
- NULL,
- LIBSSH_RSA_TESTKEY);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ NULL,
+ NULL,
+ NULL,
+ LIBSSH_RSA_TESTKEY,
+ format);
+ assert_return_code(rc, errno);
rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY,
NULL,
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
@@ -811,16 +920,17 @@ static void torture_pki_rsa_write_privkey(void **state)
NULL,
NULL,
&origkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(origkey);
unlink(LIBSSH_RSA_TESTKEY_PASSPHRASE);
- rc = ssh_pki_export_privkey_file(origkey,
- torture_get_testkey_passphrase(),
- NULL,
- NULL,
- LIBSSH_RSA_TESTKEY_PASSPHRASE);
- assert_true(rc == 0);
+ rc = ssh_pki_export_privkey_file_format(origkey,
+ torture_get_testkey_passphrase(),
+ NULL,
+ NULL,
+ LIBSSH_RSA_TESTKEY_PASSPHRASE,
+ format);
+ assert_return_code(rc, errno);
/* Test with invalid passphrase */
rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY_PASSPHRASE,
@@ -828,7 +938,7 @@ static void torture_pki_rsa_write_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == SSH_ERROR);
+ assert_int_equal(rc, SSH_ERROR);
assert_null(privkey);
rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY_PASSPHRASE,
@@ -836,15 +946,47 @@ static void torture_pki_rsa_write_privkey(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
SSH_KEY_FREE(origkey);
SSH_KEY_FREE(privkey);
}
+
+static void
+torture_pki_rsa_write_privkey(void **state)
+{
+ torture_pki_rsa_write_privkey_format(state, SSH_FILE_FORMAT_DEFAULT);
+}
+
+#if defined(HAVE_LIBCRYPTO)
+static void
+torture_pki_rsa_write_privkey_pem(void **state)
+{
+ torture_pki_rsa_write_privkey_format(state, SSH_FILE_FORMAT_PEM);
+}
+
+static void
+torture_pki_rsa_write_privkey_openssh(void **state)
+{
+ torture_pki_rsa_write_privkey_format(state, SSH_FILE_FORMAT_OPENSSH);
+}
+
+static void
+torture_pki_rsa_import_export_privkey_base64_pem(void **state)
+{
+ torture_pki_rsa_import_export_privkey_base64_format(state,
+ SSH_FILE_FORMAT_PEM);
+}
+static void
+torture_pki_rsa_import_export_privkey_base64_openssh(void **state)
+{
+ torture_pki_rsa_import_export_privkey_base64_format(state,
+ SSH_FILE_FORMAT_OPENSSH);
+}
#endif /* HAVE_LIBCRYPTO */
static void torture_pki_rsa_import_privkey_base64_passphrase(void **state)
@@ -865,7 +1007,7 @@ static void torture_pki_rsa_import_privkey_base64_passphrase(void **state)
assert_non_null(key);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(key);
@@ -875,7 +1017,7 @@ static void torture_pki_rsa_import_privkey_base64_passphrase(void **state)
NULL,
NULL,
&key);
- assert_true(rc == -1);
+ assert_int_equal(rc, -1);
SSH_KEY_FREE(key);
#ifndef HAVE_LIBCRYPTO
@@ -886,7 +1028,7 @@ static void torture_pki_rsa_import_privkey_base64_passphrase(void **state)
NULL,
NULL,
&key);
- assert_true(rc == -1);
+ assert_int_equal(rc, -1);
SSH_KEY_FREE(key);
#endif
}
@@ -913,7 +1055,7 @@ torture_pki_rsa_import_openssh_privkey_base64_passphrase(void **state)
assert_non_null(key);
rc = ssh_key_is_private(key);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(key);
@@ -923,7 +1065,7 @@ torture_pki_rsa_import_openssh_privkey_base64_passphrase(void **state)
NULL,
NULL,
&key);
- assert_true(rc == -1);
+ assert_int_equal(rc, -1);
SSH_KEY_FREE(key);
/* test if it returns -1 if passphrase is NULL */
@@ -933,7 +1075,7 @@ torture_pki_rsa_import_openssh_privkey_base64_passphrase(void **state)
NULL,
NULL,
&key);
- assert_true(rc == -1);
+ assert_int_equal(rc, -1);
SSH_KEY_FREE(key);
}
@@ -952,7 +1094,7 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64_NULL_str,
setup_rsa_key,
teardown),
- cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64,
+ cmocka_unit_test_setup_teardown(torture_pki_rsa_import_export_privkey_base64,
setup_rsa_key,
teardown),
cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64_comment,
@@ -961,9 +1103,10 @@ int torture_run_tests(void) {
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),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_rsa_import_export_privkey_base64,
+ setup_openssh_rsa_key,
+ teardown),
cmocka_unit_test_setup_teardown(torture_pki_rsa_publickey_from_privatekey,
setup_rsa_key,
teardown),
@@ -985,10 +1128,25 @@ int torture_run_tests(void) {
setup_rsa_key,
teardown),
cmocka_unit_test(torture_pki_rsa_generate_key),
-#if defined(HAVE_LIBCRYPTO)
+ cmocka_unit_test(torture_pki_rsa_key_size),
cmocka_unit_test_setup_teardown(torture_pki_rsa_write_privkey,
setup_rsa_key,
teardown),
+#if defined(HAVE_LIBCRYPTO)
+ cmocka_unit_test_setup_teardown(torture_pki_rsa_write_privkey_pem,
+ setup_rsa_key,
+ teardown),
+ cmocka_unit_test_setup_teardown(torture_pki_rsa_write_privkey_openssh,
+ setup_rsa_key,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_rsa_import_export_privkey_base64_pem,
+ setup_openssh_rsa_key,
+ teardown),
+ cmocka_unit_test_setup_teardown(
+ torture_pki_rsa_import_export_privkey_base64_openssh,
+ setup_openssh_rsa_key,
+ teardown),
#endif /* HAVE_LIBCRYPTO */
cmocka_unit_test(torture_pki_sign_data_rsa),
cmocka_unit_test(torture_pki_fail_sign_with_incompatible_hash),
diff --git a/tests/unittests/torture_pki_rsa_uri.c b/tests/unittests/torture_pki_rsa_uri.c
index 370c6ec3..46c9a083 100644
--- a/tests/unittests/torture_pki_rsa_uri.c
+++ b/tests/unittests/torture_pki_rsa_uri.c
@@ -6,18 +6,17 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_pki.h"
#include "torture_key.h"
-#include "pki.c"
#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
#define LIBSSH_RSA_TESTKEY_PASSPHRASE "libssh_testkey_passphrase.id_rsa"
-#define SOFTHSM_CONF "softhsm.conf"
#define PUB_URI_FMT "pkcs11:token=%s;object=%s;type=public"
#define PRIV_URI_FMT "pkcs11:token=%s;object=%s;type=private?pin-value=%s"
-const char template[] = "temp_dir_XXXXXX";
+const char template[] = "/tmp/temp_dir_XXXXXX";
const unsigned char INPUT[] = "1234567890123456789012345678901234567890"
"123456789012345678901234";
struct pki_st {
@@ -33,7 +32,6 @@ struct pki_st {
static int setup_tokens(void **state)
{
- char conf_path[1024] = {0};
char keys_path[1024] = {0};
char keys_path_pub[1024] = {0};
char *cwd = NULL;
@@ -85,10 +83,6 @@ static int setup_tokens(void **state)
torture_setup_tokens(cwd, keys_path, obj_tempname, "1");
- snprintf(conf_path, sizeof(conf_path), "%s/softhsm.conf", cwd);
-
- setenv("SOFTHSM2_CONF", conf_path, 1);
-
return 0;
}
@@ -126,6 +120,8 @@ static int teardown_directory_structure(void **state)
struct pki_st *test_state = *state;
int rc;
+ torture_cleanup_tokens(test_state->temp_dir);
+
rc = torture_change_dir(test_state->orig_dir);
assert_int_equal(rc, 0);
@@ -142,8 +138,6 @@ static int teardown_directory_structure(void **state)
SAFE_FREE(test_state->pub_uri_invalid_token);
SAFE_FREE(test_state);
- unsetenv("SOFTHSM2_CONF");
-
return 0;
}
@@ -157,7 +151,7 @@ static void torture_pki_rsa_import_pubkey_uri(void **state)
assert_non_null(pubkey);
rc = ssh_key_is_public(pubkey);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(pubkey);
}
@@ -173,11 +167,11 @@ static void torture_pki_rsa_import_privkey_uri(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_key_is_private(privkey);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
SSH_KEY_FREE(privkey);
}
@@ -196,18 +190,18 @@ static void torture_pki_sign_verify_uri(void **state)
NULL,
NULL,
&privkey);
- assert_int_equal(rc, SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_pki_import_pubkey_file(test_state->pub_uri, &pubkey);
- assert_int_equal(rc, SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
sign = pki_do_sign(privkey, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
assert_non_null(sign);
rc = ssh_pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
ssh_signature_free(sign);
SSH_KEY_FREE(privkey);
@@ -228,14 +222,14 @@ static void torture_pki_rsa_publickey_from_privatekey_uri(void **state)
NULL,
NULL,
&privkey);
- assert_true(rc == 0);
+ assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_key_is_private(privkey);
- assert_true(rc == 1);
+ assert_int_equal(rc, 1);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
- assert_true(rc == SSH_OK);
+ assert_return_code(rc, errno);
assert_non_null(pubkey);
SSH_KEY_FREE(privkey);
@@ -259,26 +253,25 @@ static void torture_pki_rsa_uri_invalid_configurations(void **state)
assert_null(pubkey);
rc = ssh_pki_import_privkey_file(test_state->priv_uri_invalid_object,
- NULL,
- NULL,
- NULL,
- &privkey);
+ NULL,
+ NULL,
+ NULL,
+ &privkey);
assert_int_not_equal(rc, 0);
assert_null(privkey);
rc = ssh_pki_import_privkey_file(test_state->priv_uri_invalid_token,
- NULL,
- NULL,
- NULL,
- &privkey);
+ NULL,
+ NULL,
+ NULL,
+ &privkey);
assert_int_not_equal(rc, 0);
assert_null(privkey);
-
- SSH_KEY_FREE(pubkey);
- SSH_KEY_FREE(privkey);
}
-int torture_run_tests(void) {
+int
+torture_run_tests(void)
+{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_pki_rsa_import_pubkey_uri),
@@ -290,11 +283,16 @@ int torture_run_tests(void) {
ssh_session session = ssh_new();
int verbosity = SSH_LOG_FUNCTIONS;
- ssh_options_set(session,SSH_OPTIONS_LOG_VERBOSITY,&verbosity);
+
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_init();
torture_filter_tests(tests);
- rc = cmocka_run_group_tests(tests, setup_directory_structure, teardown_directory_structure);
+ rc = cmocka_run_group_tests(tests,
+ setup_directory_structure,
+ teardown_directory_structure);
+
+ ssh_free(session);
ssh_finalize();
diff --git a/tests/unittests/torture_session_keys.c b/tests/unittests/torture_session_keys.c
index 6ae58831..11993ee0 100644
--- a/tests/unittests/torture_session_keys.c
+++ b/tests/unittests/torture_session_keys.c
@@ -48,8 +48,9 @@ struct ssh_cipher_struct fake_out_cipher = {
};
struct ssh_crypto_struct test_crypto = {
- .digest_len = 32,
+ .session_id_len = 32,
.session_id = secret,
+ .digest_len = 32,
.secret_hash = secret,
.in_cipher = &fake_in_cipher,
.out_cipher = &fake_out_cipher,
@@ -72,6 +73,7 @@ static void torture_session_keys(UNUSED_PARAM(void **state))
assert_int_equal(rc, 0);
test_crypto.shared_secret = ssh_make_string_bn(k_string);
+ SSH_STRING_FREE(k_string);
rc = ssh_generate_session_keys(&session);
assert_int_equal(rc, 0);
@@ -83,7 +85,13 @@ 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);
+ bignum_safe_free(test_crypto.shared_secret);
+ SAFE_FREE(test_crypto.encryptIV);
+ SAFE_FREE(test_crypto.decryptIV);
+ SAFE_FREE(test_crypto.encryptkey);
+ SAFE_FREE(test_crypto.decryptkey);
+ SAFE_FREE(test_crypto.encryptMAC);
+ SAFE_FREE(test_crypto.decryptMAC);
}
int torture_run_tests(void) {
diff --git a/tests/unittests/torture_threads_pki_rsa.c b/tests/unittests/torture_threads_pki_rsa.c
index a04a7462..5ec8055f 100644
--- a/tests/unittests/torture_threads_pki_rsa.c
+++ b/tests/unittests/torture_threads_pki_rsa.c
@@ -26,10 +26,10 @@
#include <sys/stat.h>
#include <fcntl.h>
+#include "pki.c"
#include "torture.h"
#include "torture_pki.h"
#include "torture_key.h"
-#include "pki.c"
#include <pthread.h>
diff --git a/tests/unittests/torture_tokens.c b/tests/unittests/torture_tokens.c
index 6b52b847..438538de 100644
--- a/tests/unittests/torture_tokens.c
+++ b/tests/unittests/torture_tokens.c
@@ -265,6 +265,68 @@ static void torture_append_without_duplicate(UNUSED_PARAM(void **state))
}
}
+static void torture_remove_all_matching (UNUSED_PARAM(void** state)) {
+ char *p;
+
+ p = ssh_remove_all_matching(NULL, NULL);
+ assert_null(p);
+
+ p = ssh_remove_all_matching("don't remove", NULL);
+ assert_non_null(p);
+ assert_string_equal(p, "don't remove");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "b");
+ assert_non_null(p);
+ assert_string_equal(p, "a,c");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "a,b");
+ assert_non_null(p);
+ assert_string_equal(p, "c");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "d");
+ assert_non_null(p);
+ assert_string_equal(p, "a,b,c");
+ free(p);
+
+ p = ssh_remove_all_matching("a,b,c", "a,b,c");
+ assert_null(p);
+}
+
+static void torture_prefix_without_duplicates (UNUSED_PARAM(void** state)) {
+ char *p;
+
+ p = ssh_prefix_without_duplicates(NULL, NULL);
+ assert_null(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", NULL);
+ assert_non_null(p);
+ assert_string_equal(p, "a,b,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "a");
+ assert_non_null(p);
+ assert_string_equal(p, "a,b,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "b");
+ assert_non_null(p);
+ assert_string_equal(p, "b,a,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "x");
+ assert_non_null(p);
+ assert_string_equal(p, "x,a,b,c");
+ free(p);
+
+ p = ssh_prefix_without_duplicates("a,b,c", "c,x");
+ assert_non_null(p);
+ assert_string_equal(p, "c,x,a,b");
+ free(p);
+}
+
int torture_run_tests(void)
{
@@ -275,6 +337,8 @@ int torture_run_tests(void)
cmocka_unit_test(torture_find_all_matching),
cmocka_unit_test(torture_remove_duplicate),
cmocka_unit_test(torture_append_without_duplicate),
+ cmocka_unit_test(torture_remove_all_matching),
+ cmocka_unit_test(torture_prefix_without_duplicates),
};
ssh_init();
diff --git a/tests/unittests/torture_unit_server.c b/tests/unittests/torture_unit_server.c
new file mode 100644
index 00000000..2fd4be72
--- /dev/null
+++ b/tests/unittests/torture_unit_server.c
@@ -0,0 +1,195 @@
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <libssh/libssh.h>
+#include <libssh/bind.h>
+#include "torture.h"
+#include "torture_key.h"
+
+#define TEST_SERVER_PORT 2222
+
+#if 0
+struct test_state {
+ const char *hostkey;
+ char *hostkey_path;
+ enum ssh_keytypes_e key_type;
+ int fd;
+};
+
+static int setup(void **state)
+{
+ struct test_state *ts = NULL;
+ mode_t mask;
+ int rc;
+
+ ssh_threads_set_callbacks(ssh_threads_get_pthread());
+ rc = ssh_init();
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
+ ts = malloc(sizeof(struct test_state));
+ assert_non_null(ts);
+
+ ts->hostkey_path = strdup("/tmp/libssh_hostkey_XXXXXX");
+
+ mask = umask(S_IRWXO | S_IRWXG);
+ ts->fd = mkstemp(ts->hostkey_path);
+ umask(mask);
+ assert_return_code(ts->fd, errno);
+ close(ts->fd);
+
+ ts->key_type = SSH_KEYTYPE_ECDSA_P256;
+ ts->hostkey = torture_get_testkey(ts->key_type, 0);
+
+ torture_write_file(ts->hostkey_path, ts->hostkey);
+
+ *state = ts;
+
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ struct test_state *ts = (struct test_state *)*state;
+
+ unlink(ts->hostkey);
+ free(ts->hostkey_path);
+ free(ts);
+
+ ssh_finalize();
+
+ return 0;
+}
+
+/* TODO the signals are handled by cmocka so they are not testable her :( */
+static void *int_thread(void *arg)
+{
+ usleep(1);
+ kill(getpid(), SIGUSR1);
+ return NULL;
+}
+
+static void *client_thread(void *arg)
+{
+ unsigned int test_port = TEST_SERVER_PORT;
+ int rc;
+ ssh_session session;
+ ssh_channel channel;
+
+ /* unused */
+ (void)arg;
+
+ usleep(200);
+ session = torture_ssh_session(NULL, "localhost",
+ &test_port,
+ "foo", "bar");
+ assert_non_null(session);
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_int_equal(rc, SSH_OK);
+
+ ssh_free(session);
+ return NULL;
+}
+
+static void test_ssh_accept_interrupt(void **state)
+{
+ struct test_state *ts = (struct test_state *)*state;
+ int rc;
+ pthread_t client_pthread, interrupt_pthread;
+ ssh_bind sshbind = NULL;
+ ssh_session server;
+
+ /* Create server */
+ sshbind = torture_ssh_bind("localhost",
+ TEST_SERVER_PORT,
+ ts->key_type,
+ ts->hostkey_path);
+ assert_non_null(sshbind);
+
+ server = ssh_new();
+ assert_non_null(server);
+
+ /* Send interrupt in 1 second */
+ rc = pthread_create(&interrupt_pthread, NULL, int_thread, NULL);
+ assert_return_code(rc, errno);
+
+ rc = pthread_join(interrupt_pthread, NULL);
+ assert_int_equal(rc, 0);
+
+ rc = ssh_bind_accept(sshbind, server);
+ assert_int_equal(rc, SSH_ERROR);
+ assert_int_equal(ssh_get_error_code(sshbind), SSH_EINTR);
+
+ /* Get client to connect now */
+ rc = pthread_create(&client_pthread, NULL, client_thread, NULL);
+ assert_return_code(rc, errno);
+
+ /* Now, try again */
+ rc = ssh_bind_accept(sshbind, server);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Cleanup */
+ ssh_bind_free(sshbind);
+
+ rc = pthread_join(client_pthread, NULL);
+ assert_int_equal(rc, 0);
+}
+#endif
+
+
+static void test_default_hostkey_paths(void **state)
+{
+ int rc;
+ ssh_bind sshbind = NULL;
+
+ /* state not used */
+ (void)state;
+
+ /* Create server */
+ rc = ssh_init();
+ assert_int_equal(rc, 0);
+
+ sshbind = ssh_bind_new();
+ assert_non_null(sshbind);
+
+ /* This will fail because we don't have permission to import keys unless we run as root
+ * TODO: Implement some filesystem wrapper, that would allow this check to pass by
+ * reading the keys from some accessible test location */
+ ssh_bind_listen(sshbind);
+
+ assert_string_equal(sshbind->rsakey, "/etc/ssh/ssh_host_rsa_key");
+ assert_string_equal(sshbind->ecdsakey, "/etc/ssh/ssh_host_ecdsa_key");
+ assert_string_equal(sshbind->ed25519key, "/etc/ssh/ssh_host_ed25519_key");
+
+ /* Cleanup */
+ ssh_bind_free(sshbind);
+ ssh_finalize();
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_default_hostkey_paths),
+ /* Not working correctly the signals are not testable under cmocka
+ cmocka_unit_test_setup_teardown(test_ssh_accept_interrupt,
+ setup,
+ teardown) */
+ };
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+ return rc;
+}
diff --git a/tests/valgrind.supp b/tests/valgrind.supp
index e74d2c25..7289d90c 100644
--- a/tests/valgrind.supp
+++ b/tests/valgrind.supp
@@ -147,3 +147,14 @@
fun:FIPS_mode_set
fun:OPENSSL_init_library
}
+# Cmocka
+{
+ This looks like leak from cmocka when the forked server is not properly terminated
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ fun:calloc
+ ...
+ fun:_cmocka_run_group_tests
+ fun:torture_run_tests
+ fun:main
+}