mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-05 04:40:31 +09:00
Compare commits
343 Commits
libssh-0.1
...
63fbf00efe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63fbf00efe | ||
|
|
ae33ced0dc | ||
|
|
ee6e2c69e1 | ||
|
|
cefc4f8c97 | ||
|
|
b64e7f67d3 | ||
|
|
491cd81a32 | ||
|
|
3444f4c449 | ||
|
|
80541ab828 | ||
|
|
b042477f83 | ||
|
|
950abbbd81 | ||
|
|
b9c6701c68 | ||
|
|
a94df4bb8f | ||
|
|
41b8b3326c | ||
|
|
a9c8f942a5 | ||
|
|
d307bfa239 | ||
|
|
66e8491f73 | ||
|
|
e93c1f6a61 | ||
|
|
358553e976 | ||
|
|
07d099f652 | ||
|
|
f3d70e54e9 | ||
|
|
74d1bf51b5 | ||
|
|
00f1d6fac2 | ||
|
|
029754efb3 | ||
|
|
a49e0c2a84 | ||
|
|
8966e577ab | ||
|
|
dc45b8f3f1 | ||
|
|
c932790b82 | ||
|
|
8a0aa17bca | ||
|
|
ecb11f1a18 | ||
|
|
6aea779918 | ||
|
|
a51384fe4e | ||
|
|
c55140272f | ||
|
|
607dad040b | ||
|
|
55bb909252 | ||
|
|
08cbbea461 | ||
|
|
8c4e337ab7 | ||
|
|
8541b6584f | ||
|
|
2f77727796 | ||
|
|
a3c5d3b256 | ||
|
|
59a502ede6 | ||
|
|
c94e2efcf1 | ||
|
|
3d3b12891f | ||
|
|
6ca59307d4 | ||
|
|
e8bbd194c7 | ||
|
|
df4e907dff | ||
|
|
c99261437f | ||
|
|
53ac23ded4 | ||
|
|
ffed80f8c0 | ||
|
|
9ada7aa0e4 | ||
|
|
d357a9f3e2 | ||
|
|
c9d95ab0c7 | ||
|
|
ccff22d378 | ||
|
|
4310a696f2 | ||
|
|
771e19a7a9 | ||
|
|
118a747acd | ||
|
|
5691e0f609 | ||
|
|
5a6e2fd02a | ||
|
|
e8099375fe | ||
|
|
d00f267bc6 | ||
|
|
35d337834b | ||
|
|
ba1e8303f8 | ||
|
|
ef50a3c0f0 | ||
|
|
e7cffe7e1b | ||
|
|
d1bf9068a9 | ||
|
|
737f9ecc3c | ||
|
|
cc667021e5 | ||
|
|
f9f8c939bc | ||
|
|
aab6ce364a | ||
|
|
0cec257077 | ||
|
|
957efe51a2 | ||
|
|
bb85492d4f | ||
|
|
22c1b6970c | ||
|
|
09155adb19 | ||
|
|
95f8cbc7f0 | ||
|
|
3423399f98 | ||
|
|
ccbec9c275 | ||
|
|
ed52c88a03 | ||
|
|
0f0ac314d2 | ||
|
|
95e4c39e8a | ||
|
|
8c89633a45 | ||
|
|
8069679033 | ||
|
|
c2e9d39dbe | ||
|
|
ab44f606b2 | ||
|
|
444982b38a | ||
|
|
3df61a4e86 | ||
|
|
961c79637c | ||
|
|
7eefbbd478 | ||
|
|
c4c28c6473 | ||
|
|
08a32ac381 | ||
|
|
62762bbbc9 | ||
|
|
ab3e08c2b5 | ||
|
|
809898b980 | ||
|
|
51bd08027e | ||
|
|
0b4b71cc11 | ||
|
|
5d3ef7261c | ||
|
|
9817392e26 | ||
|
|
168302b9d6 | ||
|
|
82c8bbc504 | ||
|
|
1ea1782036 | ||
|
|
c17112f070 | ||
|
|
28c0056bca | ||
|
|
7e4f08e22a | ||
|
|
aeb0b2ec6f | ||
|
|
67cf8e3702 | ||
|
|
309f36fa83 | ||
|
|
7a2a743a39 | ||
|
|
ccb8cf88c8 | ||
|
|
b43392c31d | ||
|
|
5fc65e7270 | ||
|
|
8310b8cc2b | ||
|
|
b0063b52d8 | ||
|
|
33a947dcb0 | ||
|
|
72c282434b | ||
|
|
ba9642882d | ||
|
|
a6b73219e2 | ||
|
|
e2afe196d8 | ||
|
|
32833b40bc | ||
|
|
bc4804aa9b | ||
|
|
acb158e827 | ||
|
|
faf9caafc6 | ||
|
|
8dc29f140b | ||
|
|
7501ca1e08 | ||
|
|
2eb2af4426 | ||
|
|
5d27f69494 | ||
|
|
6fc1bf6901 | ||
|
|
a85813e6e6 | ||
|
|
f039edd85d | ||
|
|
1229ad650b | ||
|
|
937552aed2 | ||
|
|
f6709b03e6 | ||
|
|
96595d1674 | ||
|
|
c799a18d89 | ||
|
|
babd891e82 | ||
|
|
320e5154b2 | ||
|
|
986e0c593f | ||
|
|
d38007c4be | ||
|
|
c22bfa792f | ||
|
|
926d45b6dd | ||
|
|
681a5aaa26 | ||
|
|
e322e8f50c | ||
|
|
a4118ddc06 | ||
|
|
db7f101d1c | ||
|
|
ae8881dfe5 | ||
|
|
00f09acbec | ||
|
|
74eb01f26d | ||
|
|
4f239f79c6 | ||
|
|
b8e587e498 | ||
|
|
b314fd3e04 | ||
|
|
d1ad796496 | ||
|
|
e2064b743d | ||
|
|
6d2a3e4eb6 | ||
|
|
7c34fa783d | ||
|
|
2a2c714dfa | ||
|
|
12baa5200a | ||
|
|
f2b64abcbd | ||
|
|
4135154b6d | ||
|
|
ca4c874a9e | ||
|
|
c7b6ffad0e | ||
|
|
c1fb0d872d | ||
|
|
3a167a89b5 | ||
|
|
dfa9421e01 | ||
|
|
efc5bc633f | ||
|
|
3a4ba8b763 | ||
|
|
47db54b7c1 | ||
|
|
d1c2d3db9d | ||
|
|
dcb65fe584 | ||
|
|
d758990d39 | ||
|
|
bfae56634c | ||
|
|
3d0226cadc | ||
|
|
0bcd7d12d8 | ||
|
|
bd10ec1162 | ||
|
|
69c169e4cb | ||
|
|
f0b9db586b | ||
|
|
c735b44f83 | ||
|
|
3b4b8033de | ||
|
|
0068fdd594 | ||
|
|
344235c954 | ||
|
|
d00f7b1bf9 | ||
|
|
b14018ecab | ||
|
|
5e47b1c1c2 | ||
|
|
9ce885b168 | ||
|
|
184dad101d | ||
|
|
04a58919f8 | ||
|
|
b106211d92 | ||
|
|
af10857aa3 | ||
|
|
f3b389d112 | ||
|
|
18e7423e70 | ||
|
|
8c8d3ceef7 | ||
|
|
0d0ed4b1f8 | ||
|
|
8dc234c909 | ||
|
|
2e686c5cea | ||
|
|
d3706d5940 | ||
|
|
d92a057090 | ||
|
|
1434f24911 | ||
|
|
cce600f980 | ||
|
|
95150b1137 | ||
|
|
65b2591b91 | ||
|
|
a5e9529ca7 | ||
|
|
49a355c272 | ||
|
|
84d02e7440 | ||
|
|
0b91ba779c | ||
|
|
d02163546d | ||
|
|
a93e84efb9 | ||
|
|
a59d587060 | ||
|
|
6c4e4a9e1c | ||
|
|
aa681c268e | ||
|
|
fe381d6aa4 | ||
|
|
1f76cc0c6a | ||
|
|
bf2b8954e8 | ||
|
|
7e3935e7d2 | ||
|
|
d38b471fd8 | ||
|
|
735a4368c2 | ||
|
|
a25f9d211d | ||
|
|
3a52bf1679 | ||
|
|
f7bdd779d6 | ||
|
|
8ef249a4a4 | ||
|
|
d9da8f212d | ||
|
|
9613e9508d | ||
|
|
6b9a6529bd | ||
|
|
b14cde6d2a | ||
|
|
e01c32f41e | ||
|
|
dd6a711354 | ||
|
|
c1a7de78d1 | ||
|
|
9735f074ba | ||
|
|
b2b56151c0 | ||
|
|
de7903a633 | ||
|
|
a089513e40 | ||
|
|
ec9d7d13fd | ||
|
|
f14568262a | ||
|
|
257e8eb2c1 | ||
|
|
99fcd56135 | ||
|
|
8922e43578 | ||
|
|
e36ca61e36 | ||
|
|
02c092d3d9 | ||
|
|
520f758902 | ||
|
|
12b8eed093 | ||
|
|
3372c2ad78 | ||
|
|
6b83aa9a40 | ||
|
|
7f045e2d91 | ||
|
|
2b916b3b88 | ||
|
|
a10553ae57 | ||
|
|
d1ce336ae3 | ||
|
|
79ac8b85d8 | ||
|
|
9a9cafeed5 | ||
|
|
a0a5292692 | ||
|
|
1a64c577f6 | ||
|
|
c03d0d4823 | ||
|
|
d5456931cc | ||
|
|
dc18b41357 | ||
|
|
0f5dec7fb7 | ||
|
|
0cda1c0e83 | ||
|
|
1ea9708409 | ||
|
|
39fcaac3ca | ||
|
|
dab51d8e20 | ||
|
|
4becc8eb82 | ||
|
|
3468cc0dc5 | ||
|
|
4bd8d8d362 | ||
|
|
083a4781d8 | ||
|
|
7e3263d995 | ||
|
|
fbf02d5936 | ||
|
|
16fd55b4b2 | ||
|
|
799557384d | ||
|
|
c6be50cc97 | ||
|
|
af90168624 | ||
|
|
9dbd1ec80b | ||
|
|
9b9a2ea97d | ||
|
|
e9b76ff1bd | ||
|
|
e9046fc069 | ||
|
|
0cd749a533 | ||
|
|
e795849299 | ||
|
|
d887e1320a | ||
|
|
9eaa7a6394 | ||
|
|
4af88c84d4 | ||
|
|
00fce9cb6c | ||
|
|
a547b7f115 | ||
|
|
8bf908a56f | ||
|
|
b7018c17c7 | ||
|
|
a15c977cdc | ||
|
|
91e228b08b | ||
|
|
cbcd6d6f46 | ||
|
|
49b0c859f9 | ||
|
|
79ba546cde | ||
|
|
0ea982d4ec | ||
|
|
c043122655 | ||
|
|
5da8963c1d | ||
|
|
f3d80833fe | ||
|
|
874b75429f | ||
|
|
f8a6b1e2b3 | ||
|
|
5b9b901e48 | ||
|
|
2966a4a33c | ||
|
|
867630750c | ||
|
|
9a40d51162 | ||
|
|
259721e523 | ||
|
|
4bc40a47a3 | ||
|
|
f0b55391a8 | ||
|
|
d2e5b69b02 | ||
|
|
2971e122d0 | ||
|
|
e2524538f6 | ||
|
|
d0ecb5388c | ||
|
|
72b6fad284 | ||
|
|
258c2eef3b | ||
|
|
a02268a254 | ||
|
|
f16b3539da | ||
|
|
0306581f1c | ||
|
|
d8f00aca20 | ||
|
|
e0aa182e7e | ||
|
|
0e756306f0 | ||
|
|
904c3e024c | ||
|
|
b58cb9f72b | ||
|
|
861590192f | ||
|
|
9ad2f6b3b1 | ||
|
|
ef8e90863b | ||
|
|
d29ed23010 | ||
|
|
46d7417620 | ||
|
|
c73a8a824e | ||
|
|
7712c7d0f9 | ||
|
|
d7a0cbcfbb | ||
|
|
cb0237e85b | ||
|
|
5b0f480acd | ||
|
|
629ba3fd34 | ||
|
|
48d474f78c | ||
|
|
e298600303 | ||
|
|
8295945011 | ||
|
|
8363929104 | ||
|
|
71e1baeb5f | ||
|
|
82b363f294 | ||
|
|
8fb2c5d2fd | ||
|
|
9ce53b6972 | ||
|
|
7b89ff760a | ||
|
|
362ab3a684 | ||
|
|
ea97d41bbb | ||
|
|
c85268c38b | ||
|
|
c9cfeb9b83 | ||
|
|
deedc0e108 | ||
|
|
57073d588a | ||
|
|
d416ef533f | ||
|
|
2743b510ac | ||
|
|
41d370864e | ||
|
|
7e4ea0d111 | ||
|
|
b0b2e8fefd | ||
|
|
b804aa9286 | ||
|
|
ab10f5c2f7 | ||
|
|
9634668258 |
@@ -26,3 +26,4 @@ BreakAfterReturnType: ExceptShortType
|
||||
AlwaysBreakAfterReturnType: AllDefinitions
|
||||
AlignEscapedNewlines: Left
|
||||
ForEachMacros: ['ssh_callbacks_iterate']
|
||||
AlignConsecutiveMacros: 'Consecutive'
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ tags
|
||||
/build
|
||||
/obj*
|
||||
doc/tags.xml
|
||||
.DS_Store
|
||||
|
||||
139
.gitlab-ci.yml
139
.gitlab-ci.yml
@@ -27,13 +27,17 @@ workflow:
|
||||
when: never
|
||||
- if: '$CI_COMMIT_BRANCH'
|
||||
|
||||
.build:
|
||||
stage: build
|
||||
.build_options:
|
||||
variables:
|
||||
CMAKE_DEFAULT_OPTIONS: "-DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON"
|
||||
CMAKE_DEFAULT_DEBUG_OPTIONS: "-DCMAKE_C_FLAGS='-O0 -g -ggdb' -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 -DGSSAPI_TESTING=ON -DWITH_BENCHMARKS=ON -DFUZZ_TESTING=ON"
|
||||
CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS $CMAKE_BUILD_OPTIONS $CMAKE_TEST_OPTIONS
|
||||
|
||||
.build:
|
||||
extends: .build_options
|
||||
stage: build
|
||||
before_script: &build
|
||||
- uname -a
|
||||
- cat /etc/os-release
|
||||
@@ -121,7 +125,7 @@ review:
|
||||
###############################################################################
|
||||
# CentOS builds #
|
||||
###############################################################################
|
||||
centos10s/openssl_3.2.x/x86_64:
|
||||
centos10s/openssl_3.5.x/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
|
||||
extends: .tests
|
||||
variables:
|
||||
@@ -132,7 +136,7 @@ centos10s/openssl_3.2.x/x86_64:
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure
|
||||
|
||||
centos10s/openssl_3.2.x/x86_64/fips:
|
||||
centos10s/openssl_3.5.x/x86_64/fips:
|
||||
extends: .fips
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
|
||||
variables:
|
||||
@@ -143,7 +147,7 @@ centos10s/openssl_3.2.x/x86_64/fips:
|
||||
make -j$(nproc) &&
|
||||
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
|
||||
|
||||
centos9s/openssl_3.0.x/x86_64:
|
||||
centos9s/openssl_3.5.x/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
|
||||
extends: .tests
|
||||
variables:
|
||||
@@ -160,7 +164,7 @@ centos9s/mbedtls_2.x/x86_64:
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_BLOWFISH_CIPHER=OFF"
|
||||
|
||||
centos9s/openssl_3.0.x/x86_64/fips:
|
||||
centos9s/openssl_3.5.x/x86_64/fips:
|
||||
extends: .fips
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
|
||||
script:
|
||||
@@ -225,15 +229,15 @@ fedora/coverage:
|
||||
coverage_format: cobertura
|
||||
path: obj/coverage_xml.xml
|
||||
|
||||
fedora/openssl_3.0.x/x86_64:
|
||||
fedora/openssl_3.x/x86_64:
|
||||
extends: .fedora
|
||||
|
||||
fedora/openssl_3.0.x/x86_64/pkcs11-provider:
|
||||
fedora/openssl_3.x/x86_64/pkcs11-provider:
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
|
||||
extends: .fedora
|
||||
|
||||
fedora/openssl_3.0.x/x86_64/minimal:
|
||||
fedora/openssl_3.x/x86_64/minimal:
|
||||
extends: .fedora
|
||||
variables:
|
||||
script:
|
||||
@@ -405,7 +409,7 @@ fedora/mingw32:
|
||||
paths:
|
||||
- obj-csbuild/
|
||||
|
||||
fedora/csbuild/openssl_3.0.x:
|
||||
fedora/csbuild/openssl_3.x:
|
||||
extends: .csbuild
|
||||
script:
|
||||
- csbuild
|
||||
@@ -435,6 +439,62 @@ fedora/csbuild/mbedtls:
|
||||
--color
|
||||
--print-current --print-fixed
|
||||
|
||||
###############################################################################
|
||||
# Fedora abidiff #
|
||||
###############################################################################
|
||||
|
||||
fedora/abidiff:
|
||||
stage: analysis
|
||||
variables:
|
||||
GIT_DEPTH: "100"
|
||||
CMAKE_OPTIONS: $CMAKE_DEFAULT_DEBUG_OPTIONS $CMAKE_BUILD_OPTIONS
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
|
||||
before_script:
|
||||
- uname -a
|
||||
- cat /etc/os-release
|
||||
- mount
|
||||
- df -h
|
||||
- cat /proc/swaps
|
||||
- free -h
|
||||
- |
|
||||
# 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")
|
||||
|
||||
- mkdir -p obj-${CI_COMMIT_BEFORE_SHA}
|
||||
- mkdir -p obj-${CI_COMMIT_SHA}
|
||||
|
||||
- export INSTALL_DIR1=$(pwd)/install/${CI_COMMIT_BEFORE_SHA}
|
||||
- export INSTALL_DIR2=$(pwd)/install/${CI_COMMIT_SHA}
|
||||
script:
|
||||
- git checkout ${CI_COMMIT_BEFORE_SHA}
|
||||
- pushd obj-${CI_COMMIT_BEFORE_SHA}
|
||||
- cmake ${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR1} .. &&
|
||||
make -j$(nproc) && make -j$(nproc) install
|
||||
- popd
|
||||
- ls -l ${INSTALL_DIR1}/lib*/
|
||||
- git checkout ${CI_COMMIT_SHA}
|
||||
- pushd obj-${CI_COMMIT_SHA}
|
||||
- cmake ${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR2} .. &&
|
||||
make -j$(nproc) && make -j$(nproc) install
|
||||
- popd
|
||||
- ls -l ${INSTALL_DIR2}/lib*/
|
||||
- ./.gitlab-ci/checkabi.sh ${INSTALL_DIR1} ${INSTALL_DIR2}
|
||||
tags:
|
||||
- saas-linux-small-amd64
|
||||
except:
|
||||
- tags
|
||||
only:
|
||||
- merge_requests
|
||||
|
||||
###############################################################################
|
||||
# Ubuntu builds #
|
||||
@@ -447,7 +507,7 @@ ubuntu/openssl_3.0.x/x86_64:
|
||||
###############################################################################
|
||||
# Alpine builds #
|
||||
###############################################################################
|
||||
alpine/openssl_3.0.x/musl:
|
||||
alpine/openssl_3.x/musl:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$ALPINE_BUILD
|
||||
extends: .tests
|
||||
script:
|
||||
@@ -464,10 +524,10 @@ alpine/openssl_3.0.x/musl:
|
||||
###############################################################################
|
||||
# Tumbleweed builds #
|
||||
###############################################################################
|
||||
tumbleweed/openssl_3.0.x/x86_64/gcc:
|
||||
tumbleweed/openssl_3.x/x86_64/gcc:
|
||||
extends: .tumbleweed
|
||||
|
||||
tumbleweed/openssl_3.0.x/x86/gcc:
|
||||
tumbleweed/openssl_3.x/x86/gcc:
|
||||
extends: .tumbleweed
|
||||
script:
|
||||
- cmake
|
||||
@@ -480,12 +540,12 @@ tumbleweed/openssl_3.0.x/x86/gcc:
|
||||
-DUNIT_TESTING=ON .. &&
|
||||
make -j$(nproc)
|
||||
|
||||
tumbleweed/openssl_3.0.x/x86_64/gcc7:
|
||||
tumbleweed/openssl_3.x/x86_64/gcc7:
|
||||
extends: .tumbleweed
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7"
|
||||
|
||||
tumbleweed/openssl_3.0.x/x86/gcc7:
|
||||
tumbleweed/openssl_3.x/x86/gcc7:
|
||||
extends: .tumbleweed
|
||||
script:
|
||||
- cmake
|
||||
@@ -497,7 +557,7 @@ tumbleweed/openssl_3.0.x/x86/gcc7:
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure
|
||||
|
||||
tumbleweed/openssl_3.0.x/x86_64/clang:
|
||||
tumbleweed/openssl_3.x/x86_64/clang:
|
||||
extends: .tumbleweed
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
|
||||
@@ -507,6 +567,11 @@ tumbleweed/mbedtls-3.6.x/x86_64/gcc:
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DKRB5_CONFIG=/usr/lib/mit/bin/krb5-config -DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_BLOWFISH_CIPHER=OFF "
|
||||
|
||||
tumbleweed/mbedtls-3.6.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 -DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON -DWITH_BLOWFISH_CIPHER=OFF "
|
||||
|
||||
tumbleweed/static-analysis:
|
||||
extends: .tests
|
||||
stage: analysis
|
||||
@@ -568,9 +633,7 @@ freebsd/openssl_1.1.1/x86_64:
|
||||
# 2024-05-13: These jobs run out of the stages as they take extremely long and
|
||||
# usually timeout with the update to Gitlab 17.0
|
||||
.vs:
|
||||
stage: analysis
|
||||
needs: []
|
||||
allow_failure: true
|
||||
stage: test
|
||||
cache:
|
||||
key: vcpkg.${CI_JOB_NAME}
|
||||
paths:
|
||||
@@ -662,3 +725,41 @@ coverity:
|
||||
when: on_failure
|
||||
paths:
|
||||
- obj/cov-int/*.txt
|
||||
|
||||
###############################################################################
|
||||
# MacOS #
|
||||
###############################################################################
|
||||
.macos:
|
||||
tags:
|
||||
- saas-macos-medium-m1
|
||||
image: macos-14-xcode-15
|
||||
before_script:
|
||||
- echo "MacOS runner started"
|
||||
- brew update
|
||||
- brew install cmake openssl cmocka doxygen
|
||||
- mkdir obj && cd obj
|
||||
only:
|
||||
- branches@libssh/libssh-mirror
|
||||
- branches@cryptomilk/libssh-mirror
|
||||
- branches@jjelen/libssh-mirror
|
||||
- branches@marco.fortina/libssh-mirror
|
||||
|
||||
# TODO add -DFUZZ_TESTING=ON clang cant find _LLVMFuzzerInitialize on arm64
|
||||
macos-m1:
|
||||
extends: .macos
|
||||
variables:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
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"
|
||||
CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS $CMAKE_BUILD_OPTIONS $CMAKE_TEST_OPTIONS
|
||||
stage: test
|
||||
script:
|
||||
- cmake $CMAKE_OPTIONS .. &&
|
||||
make -j$(sysctl -n hw.logicalcpu) &&
|
||||
ctest --output-on-failure
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
when: on_failure
|
||||
paths:
|
||||
- obj/
|
||||
|
||||
42
.gitlab-ci/checkabi.sh
Executable file
42
.gitlab-ci/checkabi.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
INSTALL_DIR1=${1}
|
||||
INSTALL_DIR2=${2}
|
||||
|
||||
abidiff \
|
||||
--headers-dir1 "${INSTALL_DIR1}/include/libssh/" \
|
||||
--headers-dir2 "${INSTALL_DIR2}/include/libssh/" \
|
||||
"${INSTALL_DIR1}/lib64/libssh.so" \
|
||||
"${INSTALL_DIR2}/lib64/libssh.so" \
|
||||
--fail-no-debug-info
|
||||
abiret=$?
|
||||
|
||||
ABIDIFF_ERROR=$(((abiret & 0x01) != 0))
|
||||
ABIDIFF_USAGE_ERROR=$(((abiret & 0x02) != 0))
|
||||
ABIDIFF_ABI_CHANGE=$(((abiret & 0x04) != 0))
|
||||
ABIDIFF_ABI_INCOMPATIBLE_CHANGE=$(((abiret & 0x08) != 0))
|
||||
ABIDIFF_UNKNOWN_BIT_SET=$(((abiret & 0xf0) != 0))
|
||||
|
||||
if [ $ABIDIFF_ERROR -ne 0 ]; then
|
||||
echo "abidiff reported ABIDIFF_ERROR."
|
||||
exit 1
|
||||
fi
|
||||
if [ $ABIDIFF_USAGE_ERROR -ne 0 ]; then
|
||||
echo "abidiff reported ABIDIFF_USAGE_ERROR."
|
||||
exit 1
|
||||
fi
|
||||
if [ $ABIDIFF_UNKNOWN_BIT_SET -ne 0 ]; then
|
||||
echo "abidiff reported ABIDIFF_UNKNOWN_BIT_SET."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $ABIDIFF_ABI_INCOMPATIBLE_CHANGE -ne 0 ]; then
|
||||
echo "abidiff result ABIDIFF_ABI_INCOMPATIBLE_CHANGE, this breaks the API!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $ABIDIFF_ABI_CHANGE -ne 0 ]; then
|
||||
echo "Ignoring abidiff result ABI_CHANGE"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
16
.gitlab/merge_request_templates/default.md
Normal file
16
.gitlab/merge_request_templates/default.md
Normal file
@@ -0,0 +1,16 @@
|
||||
Add a description of the new feature/bug fix. Reference any relevant bugs.
|
||||
|
||||
## Checklist
|
||||
* [ ] Commits have `Signed-off-by:` with name/author being identical to the commit author
|
||||
* [ ] Code modified for feature
|
||||
* [ ] Test suite updated with functionality tests
|
||||
* [ ] Test suite updated with negative tests
|
||||
* [ ] Documentation updated
|
||||
* [ ] The project pipelines timeout is [extended](https://docs.gitlab.com/ee/ci/pipelines/settings.html#set-a-limit-for-how-long-jobs-can-run) at least to 2 hours.
|
||||
|
||||
## Reviewer's checklist:
|
||||
* [ ] Any issues marked for closing are addressed
|
||||
* [ ] There is a test suite reasonably covering new functionality or modifications
|
||||
* [ ] Function naming, parameters, return values, types, etc., are consistent and according to [CONTRIBUTING.md](https://gitlab.com/libssh/libssh-mirror/-/blob/master/CONTRIBUTING.md)
|
||||
* [ ] This feature/change has adequate documentation added
|
||||
* [ ] No obvious mistakes in the code
|
||||
29
CHANGELOG
29
CHANGELOG
@@ -1,35 +1,6 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
version 0.11.2 (released 2025-06-24)
|
||||
* Security:
|
||||
* CVE-2025-4877 - Write beyond bounds in binary to base64 conversion
|
||||
* CVE-2025-4878 - Use of uninitialized variable in privatekey_from_file()
|
||||
* CVE-2025-5318 - Likely read beyond bounds in sftp server handle management
|
||||
* CVE-2025-5351 - Double free in functions exporting keys
|
||||
* CVE-2025-5372 - ssh_kdf() returns a success code on certain failures
|
||||
* CVE-2025-5449 - Likely read beyond bounds in sftp server message decoding
|
||||
* CVE-2025-5987 - Invalid return code for chacha20 poly1305 with OpenSSL
|
||||
* Compatibility
|
||||
* Fixed compatibility with CPM.cmake
|
||||
* Compatibility with OpenSSH 10.0
|
||||
* Tests compatibility with new Dropbear releases
|
||||
* Removed p11-kit remoting from the pkcs11 testsuite
|
||||
* Bugfixes
|
||||
* Implement missing packet filter for DH GEX
|
||||
* Properly process the SSH2_MSG_DEBUG message
|
||||
* Allow escaping quotes in quoted arguments to ssh configuration
|
||||
* Do not fail with unknown match keywords in ssh configuration
|
||||
* Process packets before selecting signature algorithm during authentication
|
||||
* Do not fail hard when the SFTP status message is not sent by noncompliant
|
||||
servers
|
||||
|
||||
version 0.11.1 (released 2024-08-30)
|
||||
* Fixed default TTY modes that are set when stdin is not connected to tty (#270)
|
||||
* Fixed zlib cleanup procedure, which could crash on i386
|
||||
* Various test fixes improving their stability
|
||||
* Fixed cygwin build
|
||||
|
||||
version 0.11.0 (released 2024-07-31)
|
||||
* Deprecations and Removals:
|
||||
* Dropped support for DSA
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.12.0)
|
||||
cmake_minimum_required(VERSION 3.14.0)
|
||||
|
||||
# Specify search path for CMake modules to be loaded by include()
|
||||
# and find_package()
|
||||
@@ -9,7 +9,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
|
||||
include(DefineCMakeDefaults)
|
||||
include(DefineCompilerFlags)
|
||||
|
||||
project(libssh VERSION 0.11.2 LANGUAGES C)
|
||||
project(libssh VERSION 0.11.00 LANGUAGES C)
|
||||
|
||||
# global needed variable
|
||||
set(APPLICATION_NAME ${PROJECT_NAME})
|
||||
@@ -21,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.10.2")
|
||||
set(LIBRARY_VERSION "4.10.0")
|
||||
set(LIBRARY_SOVERSION "4")
|
||||
|
||||
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
|
||||
@@ -41,6 +41,8 @@ macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source buil
|
||||
# Copy library files to a lib sub-directory
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||
|
||||
set(LIBSSSH_PC_REQUIRES_PRIVATE "")
|
||||
|
||||
# search for libraries
|
||||
if (WITH_ZLIB)
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -66,6 +68,7 @@ find_package(Threads)
|
||||
|
||||
if (WITH_GSSAPI)
|
||||
find_package(GSSAPI)
|
||||
list(APPEND LIBSSH_PC_REQUIRES_PRIVATE ${GSSAPI_PC_REQUIRES})
|
||||
endif (WITH_GSSAPI)
|
||||
|
||||
if (WITH_NACL)
|
||||
@@ -196,7 +199,7 @@ if (WITH_COVERAGE)
|
||||
NAME "coverage"
|
||||
EXECUTABLE make test
|
||||
DEPENDENCIES ssh tests)
|
||||
set(GCOVR_ADDITIONAL_ARGS --xml-pretty --exclude-unreachable-branches --print-summary)
|
||||
set(GCOVR_ADDITIONAL_ARGS --xml-pretty --exclude-unreachable-branches --print-summary --gcov-ignore-parse-errors)
|
||||
setup_target_for_coverage_gcovr_xml(
|
||||
NAME "coverage_xml"
|
||||
EXECUTABLE make test
|
||||
@@ -249,9 +252,15 @@ message(STATUS "Benchmarks: ${WITH_BENCHMARKS}")
|
||||
message(STATUS "Symbol versioning: ${WITH_SYMBOL_VERSIONING}")
|
||||
message(STATUS "Allow ABI break: ${WITH_ABI_BREAK}")
|
||||
message(STATUS "Release is final: ${WITH_FINAL}")
|
||||
if (WITH_HERMETIC_USR)
|
||||
message(STATUS "User global client config: ${USR_GLOBAL_CLIENT_CONFIG}")
|
||||
endif ()
|
||||
message(STATUS "Global client config: ${GLOBAL_CLIENT_CONFIG}")
|
||||
if (WITH_SERVER)
|
||||
message(STATUS "Global bind config: ${GLOBAL_BIND_CONFIG}")
|
||||
if (WITH_HERMETIC_USR)
|
||||
message(STATUS "User global bind config: ${USR_GLOBAL_BIND_CONFIG}")
|
||||
endif ()
|
||||
message(STATUS "Global bind config: ${GLOBAL_BIND_CONFIG}")
|
||||
endif()
|
||||
message(STATUS "********************************************")
|
||||
|
||||
|
||||
@@ -117,6 +117,25 @@ libssh Developer's Certificate of Origin for each patch, or inside each
|
||||
patch. Just the sign-off message is all that is required once we've
|
||||
received the initial email.
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Contributing patches through Merge Request workflow on Gitlab allows us to run
|
||||
various checks on various configuration as part of Gitlab CI. Unfortunately,
|
||||
some pipelines are slower (as they involve building dependencies) so the default
|
||||
timeout of 1 hour needs to be extended at least to 2 hours. This can be done in
|
||||
project settings of your libssh fork:
|
||||
|
||||
https://docs.gitlab.com/ee/ci/pipelines/settings.html#set-a-limit-for-how-long-jobs-can-run
|
||||
|
||||
Otherwise you will encounter errors like these, usually on visualstudio builds:
|
||||
|
||||
```
|
||||
ERROR: Job failed: execution took longer than 1h0m0s seconds
|
||||
The script exceeded the maximum execution time set for the job
|
||||
```
|
||||
|
||||
Note, that the built dependencies are cached so after successful build in your
|
||||
namespace, the rebuilds should be much faster.
|
||||
|
||||
# Coding conventions in the libssh tree
|
||||
|
||||
@@ -517,6 +536,37 @@ Bad example:
|
||||
break;
|
||||
}
|
||||
|
||||
## ABI Versioning and Symbol Management
|
||||
|
||||
To maintain [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) stability
|
||||
and ensure backward compatibility, libssh uses **symbol versioning** to track and manage
|
||||
exported functions and variables. This allows libssh to introduce new symbols or modify
|
||||
existing functions in an ABI-compatible way.
|
||||
|
||||
When introducing a new symbol:
|
||||
|
||||
1. Use the `LIBSSH_API` macro to mark the symbol as part of the public API.
|
||||
2. If you have [abimap](https://github.com/ansasaki/abimap) installed, the new symbols are
|
||||
automatically generated in the `src/libssh_dev.map` file in the **build** directory and used automatically for building the updated library. But, depending on the version of `abimap` under use, you may face linker errors like: `unable to find version dependency LIBSSH_4_9_0`. In this case, you need to manually replace the existing `src/libssh.map` file with the generated `libssh_dev.map` file to update the symbol versioning.
|
||||
3. If you do not have abimap installed, the modified/added symbols must manually be added to the
|
||||
`src/libssh.map` file. The symbols must be added in the following format (assuming that 4_10_0 is the latest released version):
|
||||
|
||||
```
|
||||
LIBSSH_AFTER_4_10_0
|
||||
{
|
||||
global:
|
||||
new_function;
|
||||
new_variable;
|
||||
} LIBSSH_4_10_0;
|
||||
```
|
||||
4. After following either of the above steps, the library can be successfully built and
|
||||
tested without any linker errors.
|
||||
|
||||
5. When submitting the patch, make sure that any new symbols have been added to `libssh.map` as described in step 3, so that the new additions may not be excluded from the next release due to human error.
|
||||
|
||||
Also, to maintain ABI compatibility, existing symbols must not be removed. Instead, they can
|
||||
be marked as deprecated using the `LIBSSH_DEPRECATED` macro. This allows the symbol to be
|
||||
removed in a future release without breaking the ABI.
|
||||
|
||||
Have fun and happy libssh hacking!
|
||||
|
||||
|
||||
@@ -104,6 +104,11 @@ if (OPENSSL_FOUND)
|
||||
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
|
||||
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
|
||||
|
||||
# Check for ML-KEM768 availability (OpenSSL 3.5+)
|
||||
if (OPENSSL_VERSION VERSION_GREATER_EQUAL "3.5.0")
|
||||
set(HAVE_MLKEM 1)
|
||||
endif ()
|
||||
|
||||
unset(CMAKE_REQUIRED_INCLUDES)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
endif()
|
||||
@@ -226,6 +231,7 @@ if (GCRYPT_FOUND)
|
||||
endif (GCRYPT_VERSION VERSION_GREATER "1.4.6")
|
||||
if (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
|
||||
set(HAVE_GCRYPT_CHACHA_POLY 1)
|
||||
set(HAVE_GCRYPT_CURVE25519 1)
|
||||
endif (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
|
||||
endif (GCRYPT_FOUND)
|
||||
|
||||
@@ -236,6 +242,13 @@ if (MBEDTLS_FOUND)
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MBEDTLS_INCLUDE_DIR}/mbedtls")
|
||||
check_include_file(chacha20.h HAVE_MBEDTLS_CHACHA20_H)
|
||||
check_include_file(poly1305.h HAVE_MBEDTLS_POLY1305_H)
|
||||
if (MBEDTLS_VERSION VERSION_LESS "3.0.0")
|
||||
check_symbol_exists(MBEDTLS_ECP_DP_CURVE25519_ENABLED "config.h" HAVE_MBEDTLS_CURVE25519)
|
||||
else()
|
||||
check_symbol_exists(MBEDTLS_ECP_DP_CURVE25519_ENABLED "mbedtls_config.h" HAVE_MBEDTLS_CURVE25519)
|
||||
endif()
|
||||
|
||||
|
||||
if (WITH_BLOWFISH_CIPHER)
|
||||
check_include_file(blowfish.h HAVE_BLOWFISH)
|
||||
endif()
|
||||
|
||||
@@ -27,6 +27,7 @@ option(WITH_INSECURE_NONE "Enable insecure none cipher and MAC algorithms (not s
|
||||
option(WITH_EXEC "Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)." ON)
|
||||
option(FUZZ_TESTING "Build with fuzzer for the server and client (automatically enables none cipher!)" OFF)
|
||||
option(PICKY_DEVELOPER "Build with picky developer flags" OFF)
|
||||
option(WITH_HERMETIC_USR "Build with support for hermetic /usr/" OFF)
|
||||
|
||||
if (WITH_ZLIB)
|
||||
set(WITH_LIBZ ON)
|
||||
@@ -59,6 +60,11 @@ if (NOT GLOBAL_CLIENT_CONFIG)
|
||||
set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config")
|
||||
endif (NOT GLOBAL_CLIENT_CONFIG)
|
||||
|
||||
if (WITH_HERMETIC_USR)
|
||||
set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
|
||||
set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
|
||||
endif (WITH_HERMETIC_USR)
|
||||
|
||||
if (FUZZ_TESTING)
|
||||
set(WITH_INSECURE_NONE ON)
|
||||
endif (FUZZ_TESTING)
|
||||
|
||||
6
INSTALL
6
INSTALL
@@ -38,14 +38,16 @@ First, you need to configure the compilation, using CMake. Go inside the
|
||||
`build` dir. Create it if it doesn't exist.
|
||||
|
||||
GNU/Linux, MacOS X, MSYS/MinGW:
|
||||
|
||||
cmake -DUNIT_TESTING=ON -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
|
||||
make
|
||||
|
||||
On Windows you should choose a makefile generator with -G or use
|
||||
|
||||
cmake-gui.exe ..
|
||||
|
||||
To enable building tests use -DUNIT_TESTING=ON. For this, the
|
||||
[cmocka](https://cmocka.org) dependency is required.
|
||||
|
||||
To enable additional client tests against a local OpenSSH server, add the
|
||||
compile option -DCLIENT_TESTING=ON. These tests require an OpenSSH
|
||||
server package and some wrapper libraries (see optional requirements) to
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
|
||||
# GSSAPI_LIBRARIES - Link these to use GSSAPI
|
||||
# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI
|
||||
# GSSAPI_PC_REQUIRES - pkg-config module name if found, needed for
|
||||
# Requires.private for static linking
|
||||
#
|
||||
#=============================================================================
|
||||
# Copyright (c) 2013 Andreas Schneider <asn@cryptomilk.org>
|
||||
@@ -24,12 +26,23 @@
|
||||
#=============================================================================
|
||||
#
|
||||
|
||||
set(_mit_modname "mit-krb5-gssapi")
|
||||
set(_heimdal_modname "heimdal-gssapi")
|
||||
|
||||
if(NOT _GSSAPI_ROOT_HINTS AND NOT _GSSAPI_ROOT_PATHS)
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(_GSSAPI ${_mit_modname} ${_heimdal_modname})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(GSSAPI_ROOT_DIR
|
||||
NAMES
|
||||
include/gssapi.h
|
||||
include/gssapi/gssapi.h
|
||||
HINTS
|
||||
${_GSSAPI_ROOT_HINTS}
|
||||
"${_GSSAPI_INCLUDEDIR}"
|
||||
PATHS
|
||||
${_GSSAPI_ROOT_PATHS}
|
||||
)
|
||||
@@ -317,9 +330,15 @@ endif (GSSAPI_FLAVOR_HEIMDAL)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR)
|
||||
|
||||
if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
|
||||
set(GSSAPI_FOUND TRUE)
|
||||
endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
|
||||
if(GSSAPI_FOUND)
|
||||
if(_GSSAPI_FOUND) # via pkg-config
|
||||
if (GSSAPI_FLAVOR_MIT)
|
||||
set(GSSAPI_PC_REQUIRES ${_mit_modname})
|
||||
elseif (GSSAPI_FLAVOR_HEIMDAL)
|
||||
set(GSSAPI_PC_REQUIRES ${_heimdal_modname})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
|
||||
# show the GSSAPI_INCLUDE_DIR and GSSAPI_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(GSSAPI_INCLUDE_DIR GSSAPI_LIBRARIES)
|
||||
|
||||
@@ -72,21 +72,23 @@ find_library(MBEDTLS_X509_LIBRARY
|
||||
set(MBEDTLS_LIBRARIES ${MBEDTLS_SSL_LIBRARY} ${MBEDTLS_CRYPTO_LIBRARY}
|
||||
${MBEDTLS_X509_LIBRARY})
|
||||
|
||||
# mbedtls 2.8
|
||||
if (MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h")
|
||||
# mbedtls 2.8
|
||||
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" _mbedtls_version_str REGEX
|
||||
"^#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"[0-9]+.[0-9]+.[0-9]+\"")
|
||||
|
||||
string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+.[0-9]+.[0-9]+).*"
|
||||
string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+\\.[0-9]+\\.[0-9]+).*$"
|
||||
"\\1" MBEDTLS_VERSION "${_mbedtls_version_str}")
|
||||
elseif (MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h")
|
||||
# mbedtls 3.6
|
||||
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" _mbedtls_version_str REGEX
|
||||
endif()
|
||||
|
||||
# mbedtls 3.6
|
||||
if (NOT MBEDTLS_VERSION AND MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h")
|
||||
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h" _mbedtls_version_str REGEX
|
||||
"^#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"[0-9]+.[0-9]+.[0-9]+\"")
|
||||
|
||||
string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+.[0-9]+.[0-9]+).*"
|
||||
string(REGEX REPLACE "^.*MBEDTLS_VERSION_STRING.*([0-9]+\\.[0-9]+\\.[0-9]+).*$"
|
||||
"\\1" MBEDTLS_VERSION "${_mbedtls_version_str}")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
if (MBEDTLS_VERSION)
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
#cmakedefine SOURCEDIR "${SOURCEDIR}"
|
||||
|
||||
/* Global bind configuration file path */
|
||||
#cmakedefine USR_GLOBAL_BIND_CONFIG "${USR_GLOBAL_BIND_CONFIG}"
|
||||
#cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}"
|
||||
|
||||
/* Global client configuration file path */
|
||||
#cmakedefine USR_GLOBAL_CLIENT_CONFIG "${USR_GLOBAL_CLIENT_CONFIG}"
|
||||
#cmakedefine GLOBAL_CLIENT_CONFIG "${GLOBAL_CLIENT_CONFIG}"
|
||||
|
||||
/************************** HEADER FILES *************************/
|
||||
@@ -85,6 +87,9 @@
|
||||
/* Define to 1 if you have elliptic curve cryptography in openssl */
|
||||
#cmakedefine HAVE_OPENSSL_ECC 1
|
||||
|
||||
/* Define to 1 if mbedTLS supports curve25519 */
|
||||
#cmakedefine HAVE_MBEDTLS_CURVE25519 1
|
||||
|
||||
/* Define to 1 if you have elliptic curve cryptography in gcrypt */
|
||||
#cmakedefine HAVE_GCRYPT_ECC 1
|
||||
|
||||
@@ -97,6 +102,9 @@
|
||||
/* Define to 1 if you have gcrypt with ChaCha20/Poly1305 support */
|
||||
#cmakedefine HAVE_GCRYPT_CHACHA_POLY 1
|
||||
|
||||
/* Define to 1 if you have gcrypt with curve25519 support */
|
||||
#cmakedefine HAVE_GCRYPT_CURVE25519
|
||||
|
||||
/*************************** FUNCTIONS ***************************/
|
||||
|
||||
/* Define to 1 if you have the `EVP_chacha20' function. */
|
||||
@@ -183,6 +191,9 @@
|
||||
/* Define to 1 if we have support for blowfish */
|
||||
#cmakedefine HAVE_BLOWFISH 1
|
||||
|
||||
/* Define to 1 if we have support for ML-KEM */
|
||||
#cmakedefine HAVE_MLKEM 1
|
||||
|
||||
/*************************** LIBRARIES ***************************/
|
||||
|
||||
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||
|
||||
@@ -91,4 +91,10 @@ that it used:
|
||||
}
|
||||
@endcode
|
||||
|
||||
Warning: In a single channel, only ONE command can be executed!
|
||||
If you want to executed multiple commands, allocate separate channels for
|
||||
them or consider opening interactive shell.
|
||||
Attempting to run multiple consecutive commands in one channel will fail.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,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>Key Exchange Methods</strong>: <i>sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com, mlkem768x25519-sha256, 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
|
||||
- <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
|
||||
|
||||
@@ -303,7 +303,6 @@ span.lineno {
|
||||
padding-right: 4px;
|
||||
text-align: right;
|
||||
color: black;
|
||||
height: 100px;
|
||||
white-space: pre;
|
||||
border-right: 3px solid #1d7567;
|
||||
background-color: #323232; }
|
||||
|
||||
@@ -66,7 +66,6 @@ int authenticate_kbdint(ssh_session session, const char *password)
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
if ((p = strchr(buffer, '\n'))) {
|
||||
*p = '\0';
|
||||
}
|
||||
@@ -75,7 +74,7 @@ int authenticate_kbdint(ssh_session session, const char *password)
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
memset(buffer, 0, strlen(buffer));
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
} else {
|
||||
if (password && strstr(prompt, "Password:")) {
|
||||
answer = password;
|
||||
@@ -147,7 +146,7 @@ int authenticate_console(ssh_session session)
|
||||
|
||||
// Try to authenticate
|
||||
rc = ssh_userauth_none(session, NULL);
|
||||
if (rc == SSH_AUTH_ERROR) {
|
||||
if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
|
||||
error(session);
|
||||
return rc;
|
||||
}
|
||||
@@ -156,7 +155,7 @@ int authenticate_console(ssh_session session)
|
||||
while (rc != SSH_AUTH_SUCCESS) {
|
||||
if (method & SSH_AUTH_METHOD_GSSAPI_MIC){
|
||||
rc = ssh_userauth_gssapi(session);
|
||||
if(rc == SSH_AUTH_ERROR) {
|
||||
if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
|
||||
error(session);
|
||||
return rc;
|
||||
} else if (rc == SSH_AUTH_SUCCESS) {
|
||||
@@ -166,7 +165,7 @@ int authenticate_console(ssh_session session)
|
||||
// Try to authenticate with public key first
|
||||
if (method & SSH_AUTH_METHOD_PUBLICKEY) {
|
||||
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
|
||||
if (rc == SSH_AUTH_ERROR) {
|
||||
if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
|
||||
error(session);
|
||||
return rc;
|
||||
} else if (rc == SSH_AUTH_SUCCESS) {
|
||||
@@ -206,7 +205,7 @@ int authenticate_console(ssh_session session)
|
||||
// Try to authenticate with keyboard interactive";
|
||||
if (method & SSH_AUTH_METHOD_INTERACTIVE) {
|
||||
rc = authenticate_kbdint(session, NULL);
|
||||
if (rc == SSH_AUTH_ERROR) {
|
||||
if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
|
||||
error(session);
|
||||
return rc;
|
||||
} else if (rc == SSH_AUTH_SUCCESS) {
|
||||
@@ -221,7 +220,7 @@ int authenticate_console(ssh_session session)
|
||||
// Try to authenticate with password
|
||||
if (method & SSH_AUTH_METHOD_PASSWORD) {
|
||||
rc = ssh_userauth_password(session, NULL, password);
|
||||
if (rc == SSH_AUTH_ERROR) {
|
||||
if (rc == SSH_AUTH_ERROR || !ssh_is_connected(session)) {
|
||||
error(session);
|
||||
return rc;
|
||||
} else if (rc == SSH_AUTH_SUCCESS) {
|
||||
|
||||
@@ -21,47 +21,57 @@ clients must be made or how a client should react.
|
||||
#include "examples_common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
ssh_session connect_ssh(const char *host, const char *user,int verbosity){
|
||||
ssh_session session = NULL;
|
||||
int auth=0;
|
||||
ssh_session connect_ssh(const char *host, const char *port, const char *user, int verbosity)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
int auth = 0;
|
||||
|
||||
session=ssh_new();
|
||||
if (session == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(user != NULL){
|
||||
if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
session = ssh_new();
|
||||
if (session == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) {
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
if(ssh_connect(session)){
|
||||
fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
|
||||
if (user != NULL) {
|
||||
if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (port != NULL) {
|
||||
if (ssh_options_set(session, SSH_OPTIONS_PORT_STR, port) < 0) {
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) {
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
if (ssh_connect(session)) {
|
||||
fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
if (verify_knownhost(session) < 0) {
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
auth = authenticate_console(session);
|
||||
if (auth == SSH_AUTH_SUCCESS) {
|
||||
return session;
|
||||
} else if (auth == SSH_AUTH_DENIED) {
|
||||
fprintf(stderr, "Authentication failed\n");
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error while authenticating : %s\n",
|
||||
ssh_get_error(session));
|
||||
}
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
if(verify_knownhost(session)<0){
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
auth=authenticate_console(session);
|
||||
if(auth==SSH_AUTH_SUCCESS){
|
||||
return session;
|
||||
} else if(auth==SSH_AUTH_DENIED){
|
||||
fprintf(stderr,"Authentication failed\n");
|
||||
} else {
|
||||
fprintf(stderr,"Error while authenticating : %s\n",ssh_get_error(session));
|
||||
}
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ clients must be made or how a client should react.
|
||||
#include <libssh/libssh.h>
|
||||
|
||||
/** Zero a structure */
|
||||
#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
|
||||
#define ZERO_STRUCT(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
int authenticate_console(ssh_session session);
|
||||
int authenticate_kbdint(ssh_session session, const char *password);
|
||||
int verify_knownhost(ssh_session session);
|
||||
ssh_session connect_ssh(const char *hostname, const char *user, int verbosity);
|
||||
ssh_session connect_ssh(const char *hostname, const char *port, const char *user, int verbosity);
|
||||
|
||||
#endif /* EXAMPLES_COMMON_H_ */
|
||||
|
||||
@@ -11,7 +11,7 @@ int main(void) {
|
||||
int rbytes, wbytes, total = 0;
|
||||
int rc;
|
||||
|
||||
session = connect_ssh("localhost", NULL, 0);
|
||||
session = connect_ssh("localhost", NULL, NULL, 0);
|
||||
if (session == NULL) {
|
||||
ssh_finalize();
|
||||
return 1;
|
||||
|
||||
@@ -350,7 +350,12 @@ int main(int argc, char *argv[])
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (arguments.action_list && arguments.file) {
|
||||
if (arguments.file == NULL) {
|
||||
fprintf(stderr, "Error: Missing argument file\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (arguments.action_list) {
|
||||
list_fingerprint(arguments.file);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ static char **sources = NULL;
|
||||
static int nsources;
|
||||
static char *destination = NULL;
|
||||
static int verbosity = 0;
|
||||
static char *port = NULL;
|
||||
|
||||
struct location {
|
||||
int is_ssh;
|
||||
@@ -49,9 +50,10 @@ enum {
|
||||
static void usage(const char *argv0) {
|
||||
fprintf(stderr, "Usage : %s [options] [[user@]host1:]file1 ... \n"
|
||||
" [[user@]host2:]destination\n"
|
||||
"sample scp client - libssh-%s\n",
|
||||
// "Options :\n",
|
||||
// " -r : use RSA to verify host public key\n",
|
||||
"sample scp client - libssh-%s\n"
|
||||
"Options :\n"
|
||||
" -P : use port to connect to remote host\n"
|
||||
" -v : increase verbosity of libssh. Can be used multiple times\n",
|
||||
argv0,
|
||||
ssh_version(0));
|
||||
exit(0);
|
||||
@@ -60,11 +62,14 @@ static void usage(const char *argv0) {
|
||||
static int opts(int argc, char **argv) {
|
||||
int i;
|
||||
|
||||
while((i = getopt(argc, argv, "v")) != -1) {
|
||||
while((i = getopt(argc, argv, "P:v")) != -1) {
|
||||
switch(i) {
|
||||
case 'v':
|
||||
verbosity++;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown option %c\n", optopt);
|
||||
usage(argv[0]);
|
||||
@@ -119,6 +124,10 @@ static struct location *parse_location(char *loc)
|
||||
struct location *location = NULL;
|
||||
char *ptr = NULL;
|
||||
|
||||
if (loc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
location = malloc(sizeof(struct location));
|
||||
if (location == NULL) {
|
||||
return NULL;
|
||||
@@ -179,7 +188,7 @@ static void close_location(struct location *loc) {
|
||||
|
||||
static int open_location(struct location *loc, int flag) {
|
||||
if (loc->is_ssh && flag == WRITE) {
|
||||
loc->session = connect_ssh(loc->host, loc->user, verbosity);
|
||||
loc->session = connect_ssh(loc->host, port, loc->user, verbosity);
|
||||
if (!loc->session) {
|
||||
fprintf(stderr, "Couldn't connect to %s\n", loc->host);
|
||||
return -1;
|
||||
@@ -205,7 +214,7 @@ static int open_location(struct location *loc, int flag) {
|
||||
}
|
||||
return 0;
|
||||
} else if (loc->is_ssh && flag == READ) {
|
||||
loc->session = connect_ssh(loc->host, loc->user, verbosity);
|
||||
loc->session = connect_ssh(loc->host, port, loc->user, verbosity);
|
||||
if (!loc->session) {
|
||||
fprintf(stderr, "Couldn't connect to %s\n", loc->host);
|
||||
return -1;
|
||||
|
||||
@@ -148,6 +148,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
||||
ssh_bind sshbind = state->input;
|
||||
static int no_default_keys = 0;
|
||||
static int rsa_already_set = 0, ecdsa_already_set = 0;
|
||||
static int verbosity = 0;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
@@ -176,8 +177,10 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
||||
strncpy(authorizedkeys, arg, DEF_STR_SIZE - 1);
|
||||
break;
|
||||
case 'v':
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR,
|
||||
"3");
|
||||
verbosity++;
|
||||
ssh_bind_options_set(sshbind,
|
||||
SSH_BIND_OPTIONS_LOG_VERBOSITY,
|
||||
&verbosity);
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (state->arg_num >= 1)
|
||||
@@ -213,10 +216,7 @@ 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;
|
||||
struct channel_data_struct {
|
||||
sftp_session sftp;
|
||||
};
|
||||
|
||||
@@ -378,18 +378,11 @@ static void handle_session(ssh_event event, ssh_session session)
|
||||
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) {
|
||||
if (ssh_event_dopoll(event, 100) == 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));
|
||||
} while (ssh_channel_is_open(sdata.channel) &&
|
||||
!ssh_channel_is_eof(sdata.channel));
|
||||
|
||||
ssh_channel_send_eof(sdata.channel);
|
||||
ssh_channel_close(sdata.channel);
|
||||
|
||||
@@ -32,151 +32,163 @@ static const char *createcommand =
|
||||
"cd /tmp/libssh_tests && date > a && date > b && mkdir c && date > d";
|
||||
static char *host = NULL;
|
||||
|
||||
static void usage(const char *argv0){
|
||||
fprintf(stderr,"Usage : %s [options] host\n"
|
||||
"sample tiny scp downloader client - libssh-%s\n"
|
||||
"This program will create files in /tmp and try to fetch them\n",
|
||||
// "Options :\n",
|
||||
// " -r : use RSA to verify host public key\n",
|
||||
argv0,
|
||||
ssh_version(0));
|
||||
exit(0);
|
||||
static void usage(const char *argv0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage : %s [options] host\n"
|
||||
"sample tiny scp downloader client - libssh-%s\n"
|
||||
"This program will create files in /tmp and try to fetch them\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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
host = argv[optind];
|
||||
if (host == NULL)
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_files(ssh_session session)
|
||||
{
|
||||
ssh_channel channel = ssh_channel_new(session);
|
||||
char buffer[1];
|
||||
int rc;
|
||||
|
||||
if (channel == NULL) {
|
||||
fprintf(stderr, "Error creating channel: %s\n", ssh_get_error(session));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ssh_channel_open_session(channel) != SSH_OK) {
|
||||
fprintf(stderr, "Error creating channel: %s\n", ssh_get_error(session));
|
||||
ssh_channel_free(channel);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ssh_channel_request_exec(channel, createcommand) != SSH_OK) {
|
||||
fprintf(stderr,
|
||||
"Error executing command: %s\n",
|
||||
ssh_get_error(session));
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
while (!ssh_channel_is_eof(channel)) {
|
||||
rc = ssh_channel_read(channel, buffer, 1, 1);
|
||||
if (rc != 1) {
|
||||
fprintf(stderr, "Error reading from channel\n");
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = write(1, buffer, 1);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error writing to buffer\n");
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
}
|
||||
|
||||
static int fetch_files(ssh_session session)
|
||||
{
|
||||
int size;
|
||||
char buffer[BUF_SIZE];
|
||||
int mode;
|
||||
char *filename = NULL;
|
||||
int r;
|
||||
ssh_scp scp = ssh_scp_new(session,
|
||||
SSH_SCP_READ | SSH_SCP_RECURSIVE,
|
||||
"/tmp/libssh_tests/*");
|
||||
if (ssh_scp_init(scp) != SSH_OK) {
|
||||
fprintf(stderr, "error initializing scp: %s\n", ssh_get_error(session));
|
||||
ssh_scp_free(scp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
host = argv[optind];
|
||||
if(host == NULL)
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
printf("Trying to download 3 files (a,b,d) and 1 directory (c)\n");
|
||||
do {
|
||||
r = ssh_scp_pull_request(scp);
|
||||
switch (r) {
|
||||
case SSH_SCP_REQUEST_NEWFILE:
|
||||
size = ssh_scp_request_get_size(scp);
|
||||
filename = strdup(ssh_scp_request_get_filename(scp));
|
||||
mode = ssh_scp_request_get_permissions(scp);
|
||||
printf("downloading file %s, size %d, perms 0%o\n",
|
||||
filename,
|
||||
size,
|
||||
mode);
|
||||
free(filename);
|
||||
ssh_scp_accept_request(scp);
|
||||
r = ssh_scp_read(scp, buffer, sizeof(buffer));
|
||||
if (r == SSH_ERROR) {
|
||||
fprintf(stderr,
|
||||
"Error reading scp: %s\n",
|
||||
ssh_get_error(session));
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
return -1;
|
||||
}
|
||||
printf("done\n");
|
||||
break;
|
||||
case SSH_ERROR:
|
||||
fprintf(stderr, "Error: %s\n", ssh_get_error(session));
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
return -1;
|
||||
case SSH_SCP_REQUEST_WARNING:
|
||||
fprintf(stderr, "Warning: %s\n", ssh_scp_request_get_warning(scp));
|
||||
break;
|
||||
case SSH_SCP_REQUEST_NEWDIR:
|
||||
filename = strdup(ssh_scp_request_get_filename(scp));
|
||||
mode = ssh_scp_request_get_permissions(scp);
|
||||
printf("downloading directory %s, perms 0%o\n", filename, mode);
|
||||
free(filename);
|
||||
ssh_scp_accept_request(scp);
|
||||
break;
|
||||
case SSH_SCP_REQUEST_ENDDIR:
|
||||
printf("End of directory\n");
|
||||
break;
|
||||
case SSH_SCP_REQUEST_EOF:
|
||||
printf("End of requests\n");
|
||||
goto end;
|
||||
}
|
||||
} while (1);
|
||||
end:
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_files(ssh_session session){
|
||||
ssh_channel channel=ssh_channel_new(session);
|
||||
char buffer[1];
|
||||
int rc;
|
||||
|
||||
if(channel == NULL){
|
||||
fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if(ssh_channel_open_session(channel) != SSH_OK){
|
||||
fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session));
|
||||
ssh_channel_free(channel);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if(ssh_channel_request_exec(channel,createcommand) != SSH_OK){
|
||||
fprintf(stderr,"Error executing command: %s\n",ssh_get_error(session));
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
while(!ssh_channel_is_eof(channel)){
|
||||
rc = ssh_channel_read(channel,buffer,1,1);
|
||||
if (rc != 1) {
|
||||
fprintf(stderr, "Error reading from channel\n");
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = write(1, buffer, 1);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error writing to buffer\n");
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
}
|
||||
|
||||
|
||||
static int fetch_files(ssh_session session){
|
||||
int size;
|
||||
char buffer[BUF_SIZE];
|
||||
int mode;
|
||||
char *filename = NULL;
|
||||
int r;
|
||||
ssh_scp scp=ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE, "/tmp/libssh_tests/*");
|
||||
if(ssh_scp_init(scp) != SSH_OK){
|
||||
fprintf(stderr,"error initializing scp: %s\n",ssh_get_error(session));
|
||||
ssh_scp_free(scp);
|
||||
return -1;
|
||||
}
|
||||
printf("Trying to download 3 files (a,b,d) and 1 directory (c)\n");
|
||||
do {
|
||||
|
||||
r=ssh_scp_pull_request(scp);
|
||||
switch(r){
|
||||
case SSH_SCP_REQUEST_NEWFILE:
|
||||
size=ssh_scp_request_get_size(scp);
|
||||
filename=strdup(ssh_scp_request_get_filename(scp));
|
||||
mode=ssh_scp_request_get_permissions(scp);
|
||||
printf("downloading file %s, size %d, perms 0%o\n",filename,size,mode);
|
||||
free(filename);
|
||||
ssh_scp_accept_request(scp);
|
||||
r=ssh_scp_read(scp,buffer,sizeof(buffer));
|
||||
if(r==SSH_ERROR){
|
||||
fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(session));
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
return -1;
|
||||
}
|
||||
printf("done\n");
|
||||
break;
|
||||
case SSH_ERROR:
|
||||
fprintf(stderr,"Error: %s\n",ssh_get_error(session));
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
return -1;
|
||||
case SSH_SCP_REQUEST_WARNING:
|
||||
fprintf(stderr,"Warning: %s\n",ssh_scp_request_get_warning(scp));
|
||||
break;
|
||||
case SSH_SCP_REQUEST_NEWDIR:
|
||||
filename=strdup(ssh_scp_request_get_filename(scp));
|
||||
mode=ssh_scp_request_get_permissions(scp);
|
||||
printf("downloading directory %s, perms 0%o\n",filename,mode);
|
||||
free(filename);
|
||||
ssh_scp_accept_request(scp);
|
||||
break;
|
||||
case SSH_SCP_REQUEST_ENDDIR:
|
||||
printf("End of directory\n");
|
||||
break;
|
||||
case SSH_SCP_REQUEST_EOF:
|
||||
printf("End of requests\n");
|
||||
goto end;
|
||||
}
|
||||
} while (1);
|
||||
end:
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
ssh_session session = NULL;
|
||||
if(opts(argc,argv)<0)
|
||||
return EXIT_FAILURE;
|
||||
session=connect_ssh(host,NULL,verbosity);
|
||||
if(session == NULL)
|
||||
return EXIT_FAILURE;
|
||||
create_files(session);
|
||||
fetch_files(session);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
ssh_finalize();
|
||||
return 0;
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
if (opts(argc, argv) < 0)
|
||||
return EXIT_FAILURE;
|
||||
session = connect_ssh(host, NULL, NULL, verbosity);
|
||||
if (session == NULL)
|
||||
return EXIT_FAILURE;
|
||||
create_files(session);
|
||||
fetch_files(session);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
ssh_finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,60 +5,60 @@
|
||||
|
||||
#define LIMIT 0x100000000UL
|
||||
|
||||
int main(void) {
|
||||
ssh_session session = NULL;
|
||||
ssh_channel channel;
|
||||
char buffer[1024*1024];
|
||||
int rc;
|
||||
uint64_t total=0;
|
||||
uint64_t lastshown=4096;
|
||||
session = connect_ssh("localhost", NULL, 0);
|
||||
if (session == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
channel = ssh_channel_new(session);
|
||||
if (channel == NULL) {
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = ssh_channel_open_session(channel);
|
||||
if (rc < 0) {
|
||||
ssh_channel_close(channel);
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = ssh_channel_request_exec(channel, "cat > /dev/null");
|
||||
if (rc < 0) {
|
||||
ssh_channel_close(channel);
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
while ((rc = ssh_channel_write(channel, buffer, sizeof(buffer))) > 0) {
|
||||
total += rc;
|
||||
if(total/2 >= lastshown){
|
||||
printf("written %llx\n", (long long unsigned int) total);
|
||||
lastshown=total;
|
||||
int main(void)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
ssh_channel channel = NULL;
|
||||
char buffer[1024 * 1024] = {0};
|
||||
int rc;
|
||||
uint64_t total = 0;
|
||||
uint64_t lastshown = 4096;
|
||||
session = connect_ssh("localhost", NULL, NULL, 0);
|
||||
if (session == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if(total > LIMIT)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
printf("error : %s\n",ssh_get_error(session));
|
||||
channel = ssh_channel_new(session);
|
||||
if (channel == NULL) {
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = ssh_channel_open_session(channel);
|
||||
if (rc < 0) {
|
||||
ssh_channel_close(channel);
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = ssh_channel_request_exec(channel, "cat > /dev/null");
|
||||
if (rc < 0) {
|
||||
ssh_channel_close(channel);
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((rc = ssh_channel_write(channel, buffer, sizeof(buffer))) > 0) {
|
||||
total += rc;
|
||||
if (total / 2 >= lastshown) {
|
||||
printf("written %llx\n", (long long unsigned int)total);
|
||||
lastshown = total;
|
||||
}
|
||||
if (total > LIMIT)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
printf("error : %s\n", ssh_get_error(session));
|
||||
ssh_channel_close(channel);
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
|
||||
ssh_disconnect(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
|
||||
ssh_disconnect(session);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/sftp.h>
|
||||
|
||||
|
||||
#include "examples_common.h"
|
||||
#define MAXCMD 10
|
||||
@@ -112,8 +110,8 @@ static int opts(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
while((i = getopt(argc,argv,"T:P:F:")) != -1) {
|
||||
switch(i){
|
||||
while ((i = getopt(argc, argv, "T:P:F:")) != -1) {
|
||||
switch (i) {
|
||||
case 'P':
|
||||
pcap_file = optarg;
|
||||
break;
|
||||
@@ -159,16 +157,14 @@ static void cfmakeraw(struct termios *termios_p)
|
||||
|
||||
static void do_cleanup(int i)
|
||||
{
|
||||
/* unused variable */
|
||||
(void) i;
|
||||
(void)i;
|
||||
|
||||
tcsetattr(0, TCSANOW, &terminal);
|
||||
tcsetattr(0, TCSANOW, &terminal);
|
||||
}
|
||||
|
||||
static void do_exit(int i)
|
||||
{
|
||||
/* unused variable */
|
||||
(void) i;
|
||||
(void)i;
|
||||
|
||||
do_cleanup(0);
|
||||
exit(0);
|
||||
@@ -179,7 +175,7 @@ static int signal_delayed = 0;
|
||||
#ifdef SIGWINCH
|
||||
static void sigwindowchanged(int i)
|
||||
{
|
||||
(void) i;
|
||||
(void)i;
|
||||
signal_delayed = 1;
|
||||
}
|
||||
#endif
|
||||
@@ -213,18 +209,18 @@ static void select_loop(ssh_session session,ssh_channel channel)
|
||||
/* stdin */
|
||||
connector_in = ssh_connector_new(session);
|
||||
ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDINOUT);
|
||||
ssh_connector_set_in_fd(connector_in, 0);
|
||||
ssh_connector_set_in_fd(connector_in, STDIN_FILENO);
|
||||
ssh_event_add_connector(event, connector_in);
|
||||
|
||||
/* stdout */
|
||||
connector_out = ssh_connector_new(session);
|
||||
ssh_connector_set_out_fd(connector_out, 1);
|
||||
ssh_connector_set_out_fd(connector_out, STDOUT_FILENO);
|
||||
ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDINOUT);
|
||||
ssh_event_add_connector(event, connector_out);
|
||||
|
||||
/* stderr */
|
||||
connector_err = ssh_connector_new(session);
|
||||
ssh_connector_set_out_fd(connector_err, 2);
|
||||
ssh_connector_set_out_fd(connector_err, STDERR_FILENO);
|
||||
ssh_connector_set_in_channel(connector_err, channel, SSH_CONNECTOR_STDERR);
|
||||
ssh_event_add_connector(event, connector_err);
|
||||
|
||||
@@ -253,7 +249,7 @@ static void shell(ssh_session session)
|
||||
{
|
||||
ssh_channel channel = NULL;
|
||||
struct termios terminal_local;
|
||||
int interactive=isatty(0);
|
||||
int interactive = isatty(0);
|
||||
|
||||
channel = ssh_channel_new(session);
|
||||
if (channel == NULL) {
|
||||
|
||||
@@ -192,9 +192,6 @@ 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 ecdsa_already_set = 0;
|
||||
int key;
|
||||
|
||||
while((key = getopt(argc, argv, "a:e:k:p:P:r:u:v")) != -1) {
|
||||
@@ -202,16 +199,10 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, optarg);
|
||||
} 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
|
||||
option, so let's ensure we keep the keys we're adding
|
||||
by just not setting the default keys */
|
||||
no_default_keys = 1;
|
||||
} else if (key == 'r') {
|
||||
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_HOSTKEY, optarg);
|
||||
ecdsa_already_set = 1;
|
||||
} else if (key == 'a') {
|
||||
strncpy(authorizedkeys, optarg, DEF_STR_SIZE-1);
|
||||
} else if (key == 'u') {
|
||||
@@ -256,12 +247,6 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
|
||||
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, argv[optind]);
|
||||
|
||||
if (!no_default_keys) {
|
||||
set_default_keys(sshbind,
|
||||
rsa_already_set,
|
||||
ecdsa_already_set);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_ARGP_H */
|
||||
|
||||
@@ -40,222 +40,236 @@ clients must be made or how a client should react.
|
||||
#endif
|
||||
|
||||
char *host = NULL;
|
||||
const char *desthost="localhost";
|
||||
const char *port="22";
|
||||
const char *desthost = "localhost";
|
||||
const char *port = "22";
|
||||
|
||||
#ifdef WITH_PCAP
|
||||
#include <libssh/pcap.h>
|
||||
char *pcap_file=NULL;
|
||||
char *pcap_file = NULL;
|
||||
#endif
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr,"Usage : sshnetcat [user@]host forwarded_host forwarded_port\n");
|
||||
exit(1);
|
||||
fprintf(stderr,
|
||||
"Usage : sshnetcat [user@]host forwarded_host forwarded_port\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int opts(int argc, char **argv){
|
||||
static int opts(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
while((i=getopt(argc,argv,"P:"))!=-1){
|
||||
switch(i){
|
||||
while ((i = getopt(argc, argv, "P:")) != -1) {
|
||||
switch (i) {
|
||||
#ifdef WITH_PCAP
|
||||
case 'P':
|
||||
pcap_file=optarg;
|
||||
break;
|
||||
case 'P':
|
||||
pcap_file = optarg;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr,"unknown option %c\n",optopt);
|
||||
usage();
|
||||
default:
|
||||
fprintf(stderr, "unknown option %c\n", optopt);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
if(optind < argc)
|
||||
host=argv[optind++];
|
||||
if(optind < argc)
|
||||
desthost=argv[optind++];
|
||||
if(optind < argc)
|
||||
port=argv[optind++];
|
||||
if(host==NULL)
|
||||
if (optind < argc)
|
||||
host = argv[optind++];
|
||||
if (optind < argc)
|
||||
desthost = argv[optind++];
|
||||
if (optind < argc)
|
||||
port = argv[optind++];
|
||||
if (host == NULL)
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void select_loop(ssh_session session,ssh_channel channel){
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
char buffer[BUF_SIZE];
|
||||
/* channels will be set to the channels to poll.
|
||||
* outchannels will contain the result of the poll
|
||||
*/
|
||||
ssh_channel channels[2], outchannels[2];
|
||||
int lus;
|
||||
int eof=0;
|
||||
int maxfd;
|
||||
int ret;
|
||||
while(channel){
|
||||
do{
|
||||
static void select_loop(ssh_session session, ssh_channel channel)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
char buffer[BUF_SIZE];
|
||||
/* channels will be set to the channels to poll.
|
||||
* outchannels will contain the result of the poll
|
||||
*/
|
||||
ssh_channel channels[2], outchannels[2];
|
||||
int lus;
|
||||
int eof = 0;
|
||||
int maxfd;
|
||||
int ret;
|
||||
while (channel) {
|
||||
do {
|
||||
int fd;
|
||||
|
||||
ZERO_STRUCT(fds);
|
||||
FD_ZERO(&fds);
|
||||
if(!eof)
|
||||
FD_SET(0,&fds);
|
||||
timeout.tv_sec=30;
|
||||
timeout.tv_usec=0;
|
||||
FD_ZERO(&fds);
|
||||
if (!eof)
|
||||
FD_SET(0, &fds);
|
||||
timeout.tv_sec = 30;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
fd = ssh_get_fd(session);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Error getting the session file descriptor: %s\n",
|
||||
ssh_get_error(session));
|
||||
fprintf(stderr,
|
||||
"Error getting the session file descriptor: %s\n",
|
||||
ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
FD_SET(fd, &fds);
|
||||
maxfd = fd + 1;
|
||||
|
||||
channels[0]=channel; // set the first channel we want to read from
|
||||
channels[1]=NULL;
|
||||
ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout);
|
||||
if(ret==EINTR)
|
||||
continue;
|
||||
if(FD_ISSET(0,&fds)){
|
||||
lus=read(0,buffer,sizeof(buffer));
|
||||
if(lus)
|
||||
ssh_channel_write(channel,buffer,lus);
|
||||
else {
|
||||
eof=1;
|
||||
ssh_channel_send_eof(channel);
|
||||
}
|
||||
}
|
||||
if(channel && ssh_channel_is_closed(channel)){
|
||||
ssh_channel_free(channel);
|
||||
channel=NULL;
|
||||
channels[0]=NULL;
|
||||
}
|
||||
if(outchannels[0]){
|
||||
while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,0)){
|
||||
lus = ssh_channel_read(channel,buffer,sizeof(buffer),0);
|
||||
if(lus==-1){
|
||||
fprintf(stderr, "Error reading channel: %s\n",
|
||||
ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
if(lus==0){
|
||||
ssh_channel_free(channel);
|
||||
channel=channels[0]=NULL;
|
||||
} else {
|
||||
ret = write(1, buffer, lus);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing to stdin: %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,1)){ /* stderr */
|
||||
lus = ssh_channel_read(channel, buffer, sizeof(buffer), 1);
|
||||
if(lus==-1){
|
||||
fprintf(stderr, "Error reading channel: %s\n",
|
||||
ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
if(lus==0){
|
||||
ssh_channel_free(channel);
|
||||
channel=channels[0]=NULL;
|
||||
} else {
|
||||
ret = write(2, buffer, lus);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing to stderr: %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
channels[0] = channel; // set the first channel we want to read from
|
||||
channels[1] = NULL;
|
||||
ret = ssh_select(channels, outchannels, maxfd, &fds, &timeout);
|
||||
if (ret == EINTR)
|
||||
continue;
|
||||
if (FD_ISSET(0, &fds)) {
|
||||
lus = read(0, buffer, sizeof(buffer));
|
||||
if (lus)
|
||||
ssh_channel_write(channel, buffer, lus);
|
||||
else {
|
||||
eof = 1;
|
||||
ssh_channel_send_eof(channel);
|
||||
}
|
||||
}
|
||||
if (channel && ssh_channel_is_closed(channel)) {
|
||||
ssh_channel_free(channel);
|
||||
channel = NULL;
|
||||
channels[0] = NULL;
|
||||
}
|
||||
if (outchannels[0]) {
|
||||
while (channel && ssh_channel_is_open(channel) &&
|
||||
ssh_channel_poll(channel, 0)) {
|
||||
lus = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
|
||||
if (lus == -1) {
|
||||
fprintf(stderr,
|
||||
"Error reading channel: %s\n",
|
||||
ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(channel && ssh_channel_is_closed(channel)){
|
||||
ssh_channel_free(channel);
|
||||
channel=NULL;
|
||||
}
|
||||
} while (ret==EINTR || ret==SSH_EINTR);
|
||||
|
||||
}
|
||||
if (lus == 0) {
|
||||
ssh_channel_free(channel);
|
||||
channel = channels[0] = NULL;
|
||||
} else {
|
||||
ret = write(1, buffer, lus);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr,
|
||||
"Error writing to stdin: %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (channel && ssh_channel_is_open(channel) &&
|
||||
ssh_channel_poll(channel, 1)) { /* stderr */
|
||||
lus = ssh_channel_read(channel, buffer, sizeof(buffer), 1);
|
||||
if (lus == -1) {
|
||||
fprintf(stderr,
|
||||
"Error reading channel: %s\n",
|
||||
ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
if (lus == 0) {
|
||||
ssh_channel_free(channel);
|
||||
channel = channels[0] = NULL;
|
||||
} else {
|
||||
ret = write(2, buffer, lus);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr,
|
||||
"Error writing to stderr: %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (channel && ssh_channel_is_closed(channel)) {
|
||||
ssh_channel_free(channel);
|
||||
channel = NULL;
|
||||
}
|
||||
} while (ret == EINTR || ret == SSH_EINTR);
|
||||
}
|
||||
}
|
||||
|
||||
static void forwarding(ssh_session session){
|
||||
static void forwarding(ssh_session session)
|
||||
{
|
||||
ssh_channel channel;
|
||||
int r;
|
||||
channel = ssh_channel_new(session);
|
||||
r = ssh_channel_open_forward(channel, desthost, atoi(port), "localhost", 22);
|
||||
if(r<0) {
|
||||
printf("error forwarding port : %s\n",ssh_get_error(session));
|
||||
if (r < 0) {
|
||||
printf("error forwarding port : %s\n", ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
select_loop(session,channel);
|
||||
select_loop(session, channel);
|
||||
}
|
||||
|
||||
static int client(ssh_session session){
|
||||
int auth=0;
|
||||
char *banner = NULL;
|
||||
int state;
|
||||
static int client(ssh_session session)
|
||||
{
|
||||
int auth = 0;
|
||||
char *banner = NULL;
|
||||
int state;
|
||||
|
||||
if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0)
|
||||
return -1;
|
||||
ssh_options_parse_config(session, NULL);
|
||||
if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0)
|
||||
return -1;
|
||||
ssh_options_parse_config(session, NULL);
|
||||
|
||||
if(ssh_connect(session)){
|
||||
fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
|
||||
return -1;
|
||||
}
|
||||
state=verify_knownhost(session);
|
||||
if (state != 0)
|
||||
return -1;
|
||||
ssh_userauth_none(session, NULL);
|
||||
banner=ssh_get_issue_banner(session);
|
||||
if(banner){
|
||||
printf("%s\n",banner);
|
||||
free(banner);
|
||||
}
|
||||
auth=authenticate_console(session);
|
||||
if(auth != SSH_AUTH_SUCCESS){
|
||||
return -1;
|
||||
}
|
||||
forwarding(session);
|
||||
return 0;
|
||||
if (ssh_connect(session)) {
|
||||
fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session));
|
||||
return -1;
|
||||
}
|
||||
state = verify_knownhost(session);
|
||||
if (state != 0)
|
||||
return -1;
|
||||
ssh_userauth_none(session, NULL);
|
||||
banner = ssh_get_issue_banner(session);
|
||||
if (banner) {
|
||||
printf("%s\n", banner);
|
||||
free(banner);
|
||||
}
|
||||
auth = authenticate_console(session);
|
||||
if (auth != SSH_AUTH_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
forwarding(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_PCAP
|
||||
ssh_pcap_file pcap;
|
||||
void set_pcap(ssh_session session);
|
||||
void set_pcap(ssh_session session){
|
||||
if(!pcap_file)
|
||||
return;
|
||||
pcap=ssh_pcap_file_new();
|
||||
if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){
|
||||
printf("Error opening pcap file\n");
|
||||
ssh_pcap_file_free(pcap);
|
||||
pcap=NULL;
|
||||
return;
|
||||
}
|
||||
ssh_set_pcap_file(session,pcap);
|
||||
void set_pcap(ssh_session session)
|
||||
{
|
||||
if (!pcap_file)
|
||||
return;
|
||||
pcap = ssh_pcap_file_new();
|
||||
if (ssh_pcap_file_open(pcap, pcap_file) == SSH_ERROR) {
|
||||
printf("Error opening pcap file\n");
|
||||
ssh_pcap_file_free(pcap);
|
||||
pcap = NULL;
|
||||
return;
|
||||
}
|
||||
ssh_set_pcap_file(session, pcap);
|
||||
}
|
||||
|
||||
void cleanup_pcap(void);
|
||||
void cleanup_pcap(void)
|
||||
{
|
||||
ssh_pcap_file_free(pcap);
|
||||
pcap = NULL;
|
||||
ssh_pcap_file_free(pcap);
|
||||
pcap = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
|
||||
session = ssh_new();
|
||||
|
||||
if(ssh_options_getopt(session, &argc, argv)) {
|
||||
fprintf(stderr, "error parsing command line :%s\n",
|
||||
ssh_get_error(session));
|
||||
usage();
|
||||
if (ssh_options_getopt(session, &argc, argv)) {
|
||||
fprintf(stderr,
|
||||
"error parsing command line :%s\n",
|
||||
ssh_get_error(session));
|
||||
usage();
|
||||
}
|
||||
opts(argc,argv);
|
||||
opts(argc, argv);
|
||||
#ifdef WITH_PCAP
|
||||
set_pcap(session);
|
||||
#endif
|
||||
|
||||
@@ -31,6 +31,7 @@ extern "C" {
|
||||
|
||||
bignum ssh_make_string_bn(ssh_string string);
|
||||
ssh_string ssh_make_bignum_string(bignum num);
|
||||
ssh_string ssh_make_padded_bignum_string(bignum num, size_t pad_len);
|
||||
void ssh_print_bignum(const char *which, const_bignum num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -52,6 +52,7 @@ enum ssh_bind_config_opcode_e {
|
||||
BIND_CFG_MATCH,
|
||||
BIND_CFG_PUBKEY_ACCEPTED_KEY_TYPES,
|
||||
BIND_CFG_HOSTKEY_ALGORITHMS,
|
||||
BIND_CFG_REQUIRED_RSA_SIZE,
|
||||
|
||||
BIND_CFG_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
@@ -74,6 +74,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);
|
||||
|
||||
ssh_buffer ssh_buffer_dup(const ssh_buffer buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -113,6 +113,17 @@ typedef void (*ssh_status_callback) (ssh_session session, float status,
|
||||
typedef void (*ssh_global_request_callback) (ssh_session session,
|
||||
ssh_message message, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief SSH connect status callback. These are functions that report the
|
||||
* status of the connection i,e. a function indicating the completed percentage
|
||||
* of the connection
|
||||
* steps.
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @param status Percentage of connection status, going from 0.0 to 1.0
|
||||
* once connection is done.
|
||||
*/
|
||||
typedef void (*ssh_connect_status_callback)(void *userdata, float status);
|
||||
|
||||
/**
|
||||
* @brief Handles an SSH new channel open X11 request. This happens when the server
|
||||
* sends back an X11 connection attempt. This is a client-side API
|
||||
@@ -181,7 +192,7 @@ struct ssh_callbacks_struct {
|
||||
* This function gets called during connection time to indicate the
|
||||
* percentage of connection steps completed.
|
||||
*/
|
||||
void (*connect_status_function)(void *userdata, float status);
|
||||
ssh_connect_status_callback connect_status_function;
|
||||
/**
|
||||
* This function will be called each time a global request is received.
|
||||
*/
|
||||
@@ -325,6 +336,28 @@ typedef int (*ssh_gssapi_accept_sec_ctx_callback) (ssh_session session,
|
||||
typedef int (*ssh_gssapi_verify_mic_callback) (ssh_session session,
|
||||
ssh_string mic, void *mic_buffer, size_t mic_buffer_size, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Handles an SSH new channel open "direct-tcpip" request. This
|
||||
* happens when the client forwards an incoming TCP connection on a port it
|
||||
* wants to forward to the destination. This is a server-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_direct_tcpip_callback)(
|
||||
ssh_session session,
|
||||
const char *destination_address,
|
||||
int destination_port,
|
||||
const char *originator_address,
|
||||
int originator_port,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* This structure can be used to implement a libssh server, with appropriate callbacks.
|
||||
@@ -375,6 +408,12 @@ struct ssh_server_callbacks_struct {
|
||||
/* This function will be called when a MIC needs to be verified.
|
||||
*/
|
||||
ssh_gssapi_verify_mic_callback gssapi_verify_mic_function;
|
||||
/**
|
||||
* This function will be called when an incoming "direct-tcpip"
|
||||
* request is received.
|
||||
*/
|
||||
ssh_channel_open_request_direct_tcpip_callback
|
||||
channel_open_request_direct_tcpip_function;
|
||||
};
|
||||
typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ enum ssh_config_opcode_e {
|
||||
SOC_CONTROLMASTER,
|
||||
SOC_CONTROLPATH,
|
||||
SOC_CERTIFICATE,
|
||||
SOC_REQUIRED_RSA_SIZE,
|
||||
|
||||
SOC_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
@@ -45,10 +45,14 @@
|
||||
#ifdef HAVE_OPENSSL_ECDH_H
|
||||
#include <openssl/ecdh.h>
|
||||
#endif
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/ecdh.h"
|
||||
#include "libssh/kex.h"
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/sntrup761.h"
|
||||
#ifdef HAVE_MLKEM
|
||||
#include "libssh/mlkem768.h"
|
||||
#endif
|
||||
|
||||
#define DIGEST_MAX_LEN 64
|
||||
|
||||
@@ -56,32 +60,40 @@
|
||||
#define AES_GCM_IVLEN 12
|
||||
|
||||
enum ssh_key_exchange_e {
|
||||
/* diffie-hellman-group1-sha1 */
|
||||
SSH_KEX_DH_GROUP1_SHA1=1,
|
||||
/* diffie-hellman-group14-sha1 */
|
||||
SSH_KEX_DH_GROUP14_SHA1,
|
||||
/* diffie-hellman-group1-sha1 */
|
||||
SSH_KEX_DH_GROUP1_SHA1 = 1,
|
||||
/* diffie-hellman-group14-sha1 */
|
||||
SSH_KEX_DH_GROUP14_SHA1,
|
||||
#ifdef WITH_GEX
|
||||
/* diffie-hellman-group-exchange-sha1 */
|
||||
SSH_KEX_DH_GEX_SHA1,
|
||||
/* diffie-hellman-group-exchange-sha256 */
|
||||
SSH_KEX_DH_GEX_SHA256,
|
||||
/* diffie-hellman-group-exchange-sha1 */
|
||||
SSH_KEX_DH_GEX_SHA1,
|
||||
/* diffie-hellman-group-exchange-sha256 */
|
||||
SSH_KEX_DH_GEX_SHA256,
|
||||
#endif /* WITH_GEX */
|
||||
/* ecdh-sha2-nistp256 */
|
||||
SSH_KEX_ECDH_SHA2_NISTP256,
|
||||
/* ecdh-sha2-nistp384 */
|
||||
SSH_KEX_ECDH_SHA2_NISTP384,
|
||||
/* ecdh-sha2-nistp521 */
|
||||
SSH_KEX_ECDH_SHA2_NISTP521,
|
||||
/* curve25519-sha256@libssh.org */
|
||||
SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG,
|
||||
/* curve25519-sha256 */
|
||||
SSH_KEX_CURVE25519_SHA256,
|
||||
/* diffie-hellman-group16-sha512 */
|
||||
SSH_KEX_DH_GROUP16_SHA512,
|
||||
/* diffie-hellman-group18-sha512 */
|
||||
SSH_KEX_DH_GROUP18_SHA512,
|
||||
/* diffie-hellman-group14-sha256 */
|
||||
SSH_KEX_DH_GROUP14_SHA256,
|
||||
/* ecdh-sha2-nistp256 */
|
||||
SSH_KEX_ECDH_SHA2_NISTP256,
|
||||
/* ecdh-sha2-nistp384 */
|
||||
SSH_KEX_ECDH_SHA2_NISTP384,
|
||||
/* ecdh-sha2-nistp521 */
|
||||
SSH_KEX_ECDH_SHA2_NISTP521,
|
||||
/* curve25519-sha256@libssh.org */
|
||||
SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG,
|
||||
/* curve25519-sha256 */
|
||||
SSH_KEX_CURVE25519_SHA256,
|
||||
/* diffie-hellman-group16-sha512 */
|
||||
SSH_KEX_DH_GROUP16_SHA512,
|
||||
/* diffie-hellman-group18-sha512 */
|
||||
SSH_KEX_DH_GROUP18_SHA512,
|
||||
/* diffie-hellman-group14-sha256 */
|
||||
SSH_KEX_DH_GROUP14_SHA256,
|
||||
/* sntrup761x25519-sha512@openssh.com */
|
||||
SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM,
|
||||
/* sntrup761x25519-sha512 */
|
||||
SSH_KEX_SNTRUP761X25519_SHA512,
|
||||
#ifdef HAVE_MLKEM
|
||||
/* mlkem768x25519-sha256 */
|
||||
SSH_KEX_MLKEM768X25519_SHA256,
|
||||
#endif /* HAVE_MLKEM */
|
||||
};
|
||||
|
||||
enum ssh_cipher_e {
|
||||
@@ -125,9 +137,25 @@ struct ssh_crypto_struct {
|
||||
ssh_string ecdh_server_pubkey;
|
||||
#endif
|
||||
#ifdef HAVE_CURVE25519
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
EVP_PKEY *curve25519_privkey;
|
||||
#elif defined(HAVE_GCRYPT_CURVE25519)
|
||||
gcry_sexp_t curve25519_privkey;
|
||||
#else
|
||||
ssh_curve25519_privkey curve25519_privkey;
|
||||
#endif
|
||||
ssh_curve25519_pubkey curve25519_client_pubkey;
|
||||
ssh_curve25519_pubkey curve25519_server_pubkey;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
ssh_mlkem768_privkey mlkem768_client_privkey;
|
||||
ssh_mlkem768_pubkey mlkem768_client_pubkey;
|
||||
ssh_mlkem768_ciphertext mlkem768_ciphertext;
|
||||
#endif
|
||||
#ifdef HAVE_SNTRUP761
|
||||
ssh_sntrup761_privkey sntrup761_privkey;
|
||||
ssh_sntrup761_pubkey sntrup761_client_pubkey;
|
||||
ssh_sntrup761_ciphertext sntrup761_ciphertext;
|
||||
#endif
|
||||
ssh_string dh_server_signature; /* information used by dh_handshake. */
|
||||
size_t session_id_len;
|
||||
@@ -223,9 +251,6 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
|
||||
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 */
|
||||
|
||||
void compress_cleanup(struct ssh_crypto_struct *crypto);
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned c
|
||||
typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE];
|
||||
typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE];
|
||||
|
||||
int ssh_curve25519_init(ssh_session session);
|
||||
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k);
|
||||
int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k);
|
||||
|
||||
int ssh_client_curve25519_init(ssh_session session);
|
||||
void ssh_client_curve25519_remove_callbacks(ssh_session session);
|
||||
|
||||
@@ -52,10 +52,10 @@ 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);
|
||||
const char *ssh_kex_get_supported_method(uint32_t algo);
|
||||
const char *ssh_kex_get_default_methods(uint32_t algo);
|
||||
const char *ssh_kex_get_fips_methods(uint32_t algo);
|
||||
const char *ssh_kex_get_description(uint32_t algo);
|
||||
const char *ssh_kex_get_supported_method(enum ssh_kex_types_e type);
|
||||
const char *ssh_kex_get_default_methods(enum ssh_kex_types_e type);
|
||||
const char *ssh_kex_get_fips_methods(enum ssh_kex_types_e type);
|
||||
const char *ssh_kex_get_description(enum ssh_kex_types_e type);
|
||||
char *ssh_client_select_hostkeys(ssh_session session);
|
||||
int ssh_send_rekex(ssh_session session);
|
||||
int server_set_kex(ssh_session session);
|
||||
|
||||
@@ -121,6 +121,15 @@ typedef BN_CTX* bignum_CTX;
|
||||
|
||||
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);
|
||||
|
||||
#if defined(WITH_PKCS11_URI)
|
||||
#if defined(WITH_PKCS11_PROVIDER)
|
||||
int pki_load_pkcs11_provider(void);
|
||||
#else
|
||||
ENGINE *pki_get_engine(void);
|
||||
#endif
|
||||
#endif /* WITH_PKCS11_PROVIDER */
|
||||
|
||||
#endif /* HAVE_LIBCRYPTO */
|
||||
|
||||
#endif /* LIBCRYPTO_H_ */
|
||||
|
||||
@@ -49,9 +49,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef int mode_t;
|
||||
@@ -843,6 +844,7 @@ LIBSSH_API int ssh_string_fill(ssh_string str, const void *data, size_t len);
|
||||
do { if ((x) != NULL) { ssh_string_free(x); x = NULL; } } while(0)
|
||||
LIBSSH_API void ssh_string_free(ssh_string str);
|
||||
LIBSSH_API ssh_string ssh_string_from_char(const char *what);
|
||||
LIBSSH_API ssh_string ssh_string_from_data(const void *data, size_t len);
|
||||
LIBSSH_API size_t ssh_string_len(ssh_string str);
|
||||
LIBSSH_API ssh_string ssh_string_new(size_t size);
|
||||
LIBSSH_API const char *ssh_string_get_char(ssh_string str);
|
||||
@@ -850,6 +852,7 @@ LIBSSH_API char *ssh_string_to_char(ssh_string str);
|
||||
#define SSH_STRING_FREE_CHAR(x) \
|
||||
do { if ((x) != NULL) { ssh_string_free_char(x); x = NULL; } } while(0)
|
||||
LIBSSH_API void ssh_string_free_char(char *s);
|
||||
LIBSSH_API int ssh_string_cmp(ssh_string s1, ssh_string s2);
|
||||
|
||||
LIBSSH_API int ssh_getpass(const char *prompt, char *buf, size_t len, int echo,
|
||||
int verify);
|
||||
@@ -874,6 +877,7 @@ LIBSSH_API const char* ssh_get_cipher_in(ssh_session session);
|
||||
LIBSSH_API const char* ssh_get_cipher_out(ssh_session session);
|
||||
LIBSSH_API const char* ssh_get_hmac_in(ssh_session session);
|
||||
LIBSSH_API const char* ssh_get_hmac_out(ssh_session session);
|
||||
LIBSSH_API const char *ssh_get_supported_methods(enum ssh_kex_types_e type);
|
||||
|
||||
LIBSSH_API ssh_buffer ssh_buffer_new(void);
|
||||
LIBSSH_API void ssh_buffer_free(ssh_buffer buffer);
|
||||
@@ -886,6 +890,27 @@ 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);
|
||||
|
||||
/* SSHSIG hashes data independently from the key used, so we use a new enum
|
||||
to avoid confusion. See
|
||||
https://gitlab.com/jas/ietf-sshsig-format/-/blob/cc70a225cbd695d5a6f20aaebdb4b92b0818e43a/ietf-sshsig-format.md#L137
|
||||
*/
|
||||
enum sshsig_digest_e {
|
||||
SSHSIG_DIGEST_SHA2_256 = 0,
|
||||
SSHSIG_DIGEST_SHA2_512 = 1,
|
||||
};
|
||||
|
||||
LIBSSH_API int sshsig_sign(const void *data,
|
||||
size_t data_length,
|
||||
ssh_key privkey,
|
||||
const char *sig_namespace,
|
||||
enum sshsig_digest_e hash_alg,
|
||||
char **signature);
|
||||
LIBSSH_API int sshsig_verify(const void *data,
|
||||
size_t data_length,
|
||||
const char *signature,
|
||||
const char *sig_namespace,
|
||||
ssh_key *sign_key);
|
||||
|
||||
#ifndef LIBSSH_LEGACY_0_4
|
||||
#include "libssh/legacy.h"
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,7 @@ struct ssh_auth_request {
|
||||
int method;
|
||||
char *password;
|
||||
struct ssh_key_struct *pubkey;
|
||||
struct ssh_key_struct *server_pubkey;
|
||||
char *sigtype;
|
||||
enum ssh_publickey_state_e signature_state;
|
||||
char kbdint_response;
|
||||
|
||||
63
include/libssh/mlkem768.h
Normal file
63
include/libssh/mlkem768.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2025 by Red Hat, Inc.
|
||||
*
|
||||
* Author: Sahana Prasad <sahana@redhat.com>
|
||||
* Author: Claude (Anthropic)
|
||||
*
|
||||
* 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 MLKEM768_H_
|
||||
#define MLKEM768_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* ML-KEM768 key and ciphertext sizes as defined in FIPS 203 */
|
||||
#define MLKEM768_PUBLICKEY_SIZE 1184
|
||||
#define MLKEM768_SECRETKEY_SIZE 2400
|
||||
#define MLKEM768_CIPHERTEXT_SIZE 1088
|
||||
#define MLKEM768_SHARED_SECRET_SIZE 32
|
||||
|
||||
/* Hybrid ML-KEM768x25519 combined sizes */
|
||||
#define MLKEM768X25519_CLIENT_PUBKEY_SIZE \
|
||||
(MLKEM768_PUBLICKEY_SIZE + CURVE25519_PUBKEY_SIZE)
|
||||
#define MLKEM768X25519_SERVER_RESPONSE_SIZE \
|
||||
(MLKEM768_CIPHERTEXT_SIZE + CURVE25519_PUBKEY_SIZE)
|
||||
#define MLKEM768X25519_SHARED_SECRET_SIZE \
|
||||
(MLKEM768_SHARED_SECRET_SIZE + CURVE25519_PUBKEY_SIZE)
|
||||
|
||||
typedef unsigned char ssh_mlkem768_pubkey[MLKEM768_PUBLICKEY_SIZE];
|
||||
typedef unsigned char ssh_mlkem768_privkey[MLKEM768_SECRETKEY_SIZE];
|
||||
typedef unsigned char ssh_mlkem768_ciphertext[MLKEM768_CIPHERTEXT_SIZE];
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ML-KEM768x25519 key exchange functions */
|
||||
int ssh_client_mlkem768x25519_init(ssh_session session);
|
||||
void ssh_client_mlkem768x25519_remove_callbacks(ssh_session session);
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
void ssh_server_mlkem768x25519_init(ssh_session session);
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MLKEM768_H_ */
|
||||
@@ -25,6 +25,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ssh_config_parse(ssh_session session, FILE *fp, bool global);
|
||||
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,
|
||||
|
||||
@@ -46,11 +46,24 @@
|
||||
#define MAX_PUBKEY_SIZE 0x100000 /* 1M */
|
||||
#define MAX_PRIVKEY_SIZE 0x400000 /* 4M */
|
||||
|
||||
#define RSA_MIN_KEY_SIZE 1024
|
||||
#define RSA_MIN_FIPS_KEY_SIZE 2048
|
||||
#define RSA_DEFAULT_KEY_SIZE 3072
|
||||
|
||||
#define SSH_KEY_FLAG_EMPTY 0x0
|
||||
#define SSH_KEY_FLAG_PUBLIC 0x0001
|
||||
#define SSH_KEY_FLAG_PRIVATE 0x0002
|
||||
#define SSH_KEY_FLAG_PKCS11_URI 0x0004
|
||||
|
||||
/* Constants matching the Lightweight Secure Shell Signature Format */
|
||||
/* https://datatracker.ietf.org/doc/draft-josefsson-sshsig-format */
|
||||
#define SSHSIG_VERSION 0x01
|
||||
#define SSHSIG_MAGIC_PREAMBLE "SSHSIG"
|
||||
#define SSHSIG_MAGIC_PREAMBLE_LEN (sizeof(SSHSIG_MAGIC_PREAMBLE) - 1)
|
||||
#define SSHSIG_BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----"
|
||||
#define SSHSIG_END_SIGNATURE "-----END SSH SIGNATURE-----"
|
||||
#define SSHSIG_LINE_LENGTH 76
|
||||
|
||||
struct ssh_key_struct {
|
||||
enum ssh_keytypes_e type;
|
||||
int flags;
|
||||
@@ -63,11 +76,12 @@ struct ssh_key_struct {
|
||||
mbedtls_pk_context *pk;
|
||||
mbedtls_ecdsa_context *ecdsa;
|
||||
#elif defined(HAVE_LIBCRYPTO)
|
||||
/* This holds either ENGINE key for PKCS#11 support or just key in
|
||||
* high-level format */
|
||||
/* This holds either ENGINE/PROVIDER key for PKCS#11 support
|
||||
* or just key in high-level format */
|
||||
EVP_PKEY *key;
|
||||
/* keep this around for FIPS mode so we can parse the public keys. We won't
|
||||
* be able to use them nor use the private keys though */
|
||||
uint8_t *ed25519_pubkey;
|
||||
uint8_t *ed25519_privkey;
|
||||
#endif /* HAVE_LIBGCRYPT */
|
||||
#ifndef HAVE_LIBCRYPTO
|
||||
ed25519_pubkey *ed25519_pubkey;
|
||||
@@ -76,6 +90,11 @@ struct ssh_key_struct {
|
||||
ssh_string sk_application;
|
||||
ssh_buffer cert;
|
||||
enum ssh_keytypes_e cert_type;
|
||||
|
||||
/* Security Key specific private data */
|
||||
uint8_t sk_flags;
|
||||
ssh_string sk_key_handle;
|
||||
ssh_string sk_reserved;
|
||||
};
|
||||
|
||||
struct ssh_signature_struct {
|
||||
@@ -127,6 +146,11 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name);
|
||||
((kt) >= SSH_KEYTYPE_ECDSA_P256_CERT01 &&\
|
||||
(kt) <= SSH_KEYTYPE_ED25519_CERT01))
|
||||
|
||||
#define is_sk_key_type(kt) \
|
||||
((kt) == SSH_KEYTYPE_SK_ECDSA || (kt) == SSH_KEYTYPE_SK_ED25519 || \
|
||||
(kt) == SSH_KEYTYPE_SK_ECDSA_CERT01 || \
|
||||
(kt) == SSH_KEYTYPE_SK_ED25519_CERT01)
|
||||
|
||||
/* SSH Signature Functions */
|
||||
ssh_signature ssh_signature_new(void);
|
||||
void ssh_signature_free(ssh_signature sign);
|
||||
|
||||
@@ -61,6 +61,7 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
|
||||
/* SSH Key Functions */
|
||||
ssh_key pki_key_dup_common_init(const ssh_key key, int demote);
|
||||
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_ecdsa(ssh_key key, int parameter);
|
||||
@@ -148,6 +149,7 @@ int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob);
|
||||
int pki_privkey_build_ed25519(ssh_key key,
|
||||
ssh_string pubkey,
|
||||
ssh_string privkey);
|
||||
int pki_pubkey_build_ed25519(ssh_key key, ssh_string pubkey);
|
||||
|
||||
/* PKI Container OpenSSH */
|
||||
ssh_key ssh_pki_openssh_pubkey_import(const char *text_key);
|
||||
@@ -162,6 +164,16 @@ 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);
|
||||
|
||||
/* Security Key Helper Functions */
|
||||
int pki_buffer_pack_sk_priv_data(ssh_buffer buffer, const ssh_key key);
|
||||
int pki_buffer_unpack_sk_priv_data(ssh_buffer buffer, ssh_key key);
|
||||
int pki_sk_signature_buffer_prepare(const ssh_key key,
|
||||
const ssh_signature sig,
|
||||
const unsigned char *input,
|
||||
size_t input_len,
|
||||
ssh_buffer *sk_buffer_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -157,6 +157,7 @@ void ssh_poll_ctx_free(ssh_poll_ctx ctx);
|
||||
int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p);
|
||||
int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s);
|
||||
void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p);
|
||||
bool ssh_poll_is_locked(ssh_poll_handle p);
|
||||
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout);
|
||||
ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session);
|
||||
int ssh_event_add_poll(ssh_event event, ssh_poll_handle p);
|
||||
|
||||
@@ -30,10 +30,11 @@
|
||||
#define _LIBSSH_PRIV_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
#if !defined(HAVE_STRTOULL)
|
||||
# if defined(HAVE___STRTOULL)
|
||||
@@ -164,6 +165,9 @@ int ssh_gettimeofday(struct timeval *__p, void *__t);
|
||||
|
||||
#define gettimeofday ssh_gettimeofday
|
||||
|
||||
struct tm *ssh_localtime(const time_t *timer, struct tm *result);
|
||||
# define localtime_r ssh_localtime
|
||||
|
||||
#define _XCLOSESOCKET closesocket
|
||||
|
||||
# ifdef HAVE_IO_H
|
||||
@@ -329,7 +333,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,
|
||||
size_t len, int dolower);
|
||||
int match_hostname(const char *host, const char *pattern, unsigned int len);
|
||||
int match_hostname(const char *host, const char *pattern, size_t len);
|
||||
#ifndef _WIN32
|
||||
int match_cidr_address_list(const char *address,
|
||||
const char *addrlist,
|
||||
@@ -353,10 +357,10 @@ int ssh_connector_remove_event(ssh_connector connector);
|
||||
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
|
||||
|
||||
/** Zero a structure */
|
||||
#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
|
||||
#define ZERO_STRUCT(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
/** Zero a structure given a pointer to the structure */
|
||||
#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
|
||||
#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((x), 0, sizeof(*(x))); } while(0)
|
||||
|
||||
/** Get the size of an array */
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
@@ -365,6 +369,17 @@ int ssh_connector_remove_event(ssh_connector connector);
|
||||
void explicit_bzero(void *s, size_t n);
|
||||
#endif /* !HAVE_EXPLICIT_BZERO */
|
||||
|
||||
void burn_free(void *ptr, size_t len);
|
||||
|
||||
/** Free memory space after zeroing it */
|
||||
#define BURN_FREE(x, len) \
|
||||
do { \
|
||||
if ((x) != NULL) { \
|
||||
burn_free((x), (len)); \
|
||||
(x) = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* This is a hack to fix warnings. The idea is to use this everywhere that we
|
||||
* get the "discarding const" warning by the compiler. That doesn't actually
|
||||
|
||||
@@ -89,6 +89,9 @@ enum ssh_pending_call_e {
|
||||
#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
|
||||
/* The scp on server can not handle quoted paths. Skip the mitigation for
|
||||
* CVE-2019-14889 when using scp */
|
||||
#define SSH_SESSION_FLAG_SCP_QUOTING_BROKEN 0x0040
|
||||
|
||||
/* codes to use with ssh_handle_packets*() */
|
||||
/* Infinite timeout */
|
||||
@@ -120,6 +123,8 @@ enum ssh_pending_call_e {
|
||||
/* server-sig-algs extension */
|
||||
#define SSH_EXT_SIG_RSA_SHA256 0x02
|
||||
#define SSH_EXT_SIG_RSA_SHA512 0x04
|
||||
/* Host-bound public key authentication extension */
|
||||
#define SSH_EXT_PUBLICKEY_HOSTBOUND 0x08
|
||||
|
||||
/* members that are common to ssh_session and ssh_bind */
|
||||
struct ssh_common_struct {
|
||||
|
||||
@@ -79,6 +79,7 @@ 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;
|
||||
typedef struct sftp_name_id_map_struct *sftp_name_id_map;
|
||||
|
||||
struct sftp_session_struct {
|
||||
ssh_session session;
|
||||
@@ -213,6 +214,22 @@ struct sftp_limits_struct {
|
||||
uint64_t max_open_handles; /** maximum number of active handles allowed by server */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SFTP names map structure to store the mapping between ids and names.
|
||||
*
|
||||
* This is mainly for the use of sftp_get_users_groups_by_id() function.
|
||||
*/
|
||||
struct sftp_name_id_map_struct {
|
||||
/** @brief Count of name-id pairs in the map */
|
||||
uint32_t count;
|
||||
|
||||
/** @brief Array of ids, ids[i] mapped to names[i] */
|
||||
uint32_t *ids;
|
||||
|
||||
/** @brief Array of names, names[i] mapped to ids[i] */
|
||||
char **names;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a new sftp session.
|
||||
*
|
||||
@@ -220,24 +237,30 @@ struct sftp_limits_struct {
|
||||
* with the server inside of the provided ssh session. This function call is
|
||||
* usually followed by the sftp_init(), which initializes SFTP protocol itself.
|
||||
*
|
||||
* @param session The ssh session to use.
|
||||
* @param session The ssh session to use. The session *must* be in
|
||||
* blocking mode since most `sftp_*` functions do not
|
||||
* support the non-blocking API.
|
||||
*
|
||||
* @return A new sftp session or NULL on error.
|
||||
*
|
||||
* @see sftp_free()
|
||||
* @see sftp_init()
|
||||
* @see ssh_set_blocking()
|
||||
*/
|
||||
LIBSSH_API sftp_session sftp_new(ssh_session session);
|
||||
|
||||
/**
|
||||
* @brief Start a new sftp session with an existing channel.
|
||||
*
|
||||
* @param session The ssh session to use.
|
||||
* @param session The ssh session to use. The session *must* be in
|
||||
* blocking mode since most `sftp_*` functions do not
|
||||
* support the non-blocking API.
|
||||
* @param channel An open session channel with subsystem already allocated
|
||||
*
|
||||
* @return A new sftp session or NULL on error.
|
||||
*
|
||||
* @see sftp_free()
|
||||
* @see ssh_set_blocking()
|
||||
*/
|
||||
LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel);
|
||||
|
||||
@@ -1188,6 +1211,58 @@ LIBSSH_API char *sftp_expand_path(sftp_session sftp, const char *path);
|
||||
*/
|
||||
LIBSSH_API char *sftp_home_directory(sftp_session sftp, const char *username);
|
||||
|
||||
/**
|
||||
* @brief Create a new sftp_name_id_map struct.
|
||||
*
|
||||
* @param count The number of ids/names to store in the map.
|
||||
*
|
||||
* @return A pointer to the newly allocated sftp_name_id_map
|
||||
* struct.
|
||||
*/
|
||||
LIBSSH_API sftp_name_id_map sftp_name_id_map_new(uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Free the memory of an allocated `sftp_name_id_map` struct.
|
||||
*
|
||||
* @param map A pointer to the `sftp_name_id_map` struct to free.
|
||||
*/
|
||||
LIBSSH_API void sftp_name_id_map_free(sftp_name_id_map map);
|
||||
|
||||
/**
|
||||
* @brief Retrieves usernames and group names based on provided user and group
|
||||
* IDs.
|
||||
*
|
||||
* The retrieved names are stored in the `names` field of the
|
||||
* `sftp_name_id_map` structure. In case a uid or gid is not found, an empty
|
||||
* string is stored.
|
||||
*
|
||||
* This calls the "users-groups-by-id@openssh.com" extension.
|
||||
* You should check if the extension is supported using:
|
||||
*
|
||||
* @code
|
||||
* int supported = sftp_extension_supported(sftp,
|
||||
* "users-groups-by-id@openssh.com", "1");
|
||||
* @endcode
|
||||
*
|
||||
* @param sftp The SFTP session handle.
|
||||
*
|
||||
* @param users_map A pointer to a `sftp_name_id_map` struct with the user
|
||||
* IDs. Can be NULL if only group names are needed.
|
||||
*
|
||||
* @param groups_map A pointer to a `sftp_name_id_map` struct with the group
|
||||
* IDs. Can be NULL if only user names are needed.
|
||||
*
|
||||
* @return 0 on success, < 0 on error with ssh and sftp error set.
|
||||
*
|
||||
* @note The caller needs to free the memory used for
|
||||
* the maps later using `sftp_name_id_map_free()`.
|
||||
*
|
||||
* @see sftp_get_error()
|
||||
*/
|
||||
LIBSSH_API int sftp_get_users_groups_by_id(sftp_session sftp,
|
||||
sftp_name_id_map users_map,
|
||||
sftp_name_id_map groups_map);
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
/**
|
||||
* @brief Create a new sftp server session.
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#ifndef SFTP_PRIV_H
|
||||
#define SFTP_PRIV_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -62,6 +64,39 @@ int sftp_read_and_dispatch(sftp_session sftp);
|
||||
|
||||
sftp_message sftp_dequeue(sftp_session sftp, uint32_t id);
|
||||
|
||||
/**
|
||||
* @brief Receive the response of an sftp request
|
||||
*
|
||||
* In blocking mode, if the response hasn't arrived at the time of call, this
|
||||
* function waits for the response to arrive.
|
||||
*
|
||||
* @param sftp The sftp session via which the request was sent.
|
||||
*
|
||||
* @param id The request identifier of the request whose
|
||||
* corresponding response is required.
|
||||
*
|
||||
* @param blocking Flag to indicate the operating mode. true indicates
|
||||
* blocking mode and false indicates non-blocking mode
|
||||
*
|
||||
* @param msg_ptr Pointer to the location to store the response message.
|
||||
* In case of success, the message is allocated
|
||||
* dynamically and must be freed (using
|
||||
* sftp_message_free()) by the caller after usage. In case
|
||||
* of failure, this is left untouched.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* @returns SSH_ERROR on failure with the sftp and ssh errors set
|
||||
* @returns SSH_AGAIN in case of non-blocking mode if the response hasn't
|
||||
* arrived yet.
|
||||
*
|
||||
* @warning In blocking mode, this may block indefinitely for an invalid request
|
||||
* identifier.
|
||||
*/
|
||||
int sftp_recv_response_msg(sftp_session sftp,
|
||||
uint32_t id,
|
||||
bool blocking,
|
||||
sftp_message *msg_ptr);
|
||||
|
||||
/*
|
||||
* Assigns a new SFTP ID for new requests and assures there is no collision
|
||||
* between them.
|
||||
|
||||
86
include/libssh/sntrup761.h
Normal file
86
include/libssh/sntrup761.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2013 by Aris Adamantiadis <aris@badcode.be>
|
||||
* Copyright (c) 2023 Simon Josefsson <simon@josefsson.org>
|
||||
* Copyright (c) 2025 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,
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* 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 SNTRUP761_H_
|
||||
#define SNTRUP761_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "curve25519.h"
|
||||
#include "libssh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CURVE25519
|
||||
#define HAVE_SNTRUP761 1
|
||||
#endif
|
||||
|
||||
extern void crypto_hash_sha512(unsigned char *out,
|
||||
const unsigned char *in,
|
||||
unsigned long long inlen);
|
||||
|
||||
/*
|
||||
* Derived from public domain source, written by (in alphabetical order):
|
||||
* - Daniel J. Bernstein
|
||||
* - Chitchanok Chuengsatiansup
|
||||
* - Tanja Lange
|
||||
* - Christine van Vredendaal
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SNTRUP761_SECRETKEY_SIZE 1763
|
||||
#define SNTRUP761_PUBLICKEY_SIZE 1158
|
||||
#define SNTRUP761_CIPHERTEXT_SIZE 1039
|
||||
#define SNTRUP761_SIZE 32
|
||||
|
||||
typedef void sntrup761_random_func(void *ctx, size_t length, uint8_t *dst);
|
||||
|
||||
void sntrup761_keypair(uint8_t *pk,
|
||||
uint8_t *sk,
|
||||
void *random_ctx,
|
||||
sntrup761_random_func *random);
|
||||
void sntrup761_enc(uint8_t *c,
|
||||
uint8_t *k,
|
||||
const uint8_t *pk,
|
||||
void *random_ctx,
|
||||
sntrup761_random_func *random);
|
||||
void sntrup761_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk);
|
||||
|
||||
typedef unsigned char ssh_sntrup761_pubkey[SNTRUP761_PUBLICKEY_SIZE];
|
||||
typedef unsigned char ssh_sntrup761_privkey[SNTRUP761_SECRETKEY_SIZE];
|
||||
typedef unsigned char ssh_sntrup761_ciphertext[SNTRUP761_CIPHERTEXT_SIZE];
|
||||
|
||||
int ssh_client_sntrup761x25519_init(ssh_session session);
|
||||
void ssh_client_sntrup761x25519_remove_callbacks(ssh_session session);
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
void ssh_server_sntrup761x25519_init(ssh_session session);
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SNTRUP761_H_ */
|
||||
@@ -33,7 +33,7 @@ void ssh_socket_cleanup(void);
|
||||
ssh_socket ssh_socket_new(ssh_session session);
|
||||
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);
|
||||
int ssh_socket_set_fd(ssh_socket s, socket_t fd);
|
||||
socket_t ssh_socket_get_fd(ssh_socket s);
|
||||
void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p);
|
||||
int ssh_socket_unix(ssh_socket s, const char *path);
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#define SSH2_MSG_KEXDH_REPLY 31
|
||||
#define SSH2_MSG_KEX_ECDH_INIT 30
|
||||
#define SSH2_MSG_KEX_ECDH_REPLY 31
|
||||
#define SSH2_MSG_ECMQV_INIT 30
|
||||
#define SSH2_MSG_ECMQV_REPLY 31
|
||||
#define SSH2_MSG_KEX_HYBRID_INIT 30
|
||||
#define SSH2_MSG_KEX_HYBRID_REPLY 31
|
||||
|
||||
#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30
|
||||
#define SSH2_MSG_KEX_DH_GEX_GROUP 31
|
||||
|
||||
@@ -129,6 +129,7 @@ int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld, EVP_PKEY **pkey,
|
||||
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);
|
||||
int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote);
|
||||
#endif /* HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -8,3 +8,4 @@ Description: The SSH Library
|
||||
Version: @PROJECT_VERSION@
|
||||
Libs: -L${libdir} -lssh
|
||||
Cflags: -I${includedir}
|
||||
Requires.private: @LIBSSH_PC_REQUIRES_PRIVATE@
|
||||
|
||||
@@ -1 +1 @@
|
||||
4.10.2
|
||||
4.10.0
|
||||
|
||||
@@ -1,445 +0,0 @@
|
||||
_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_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
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_expand_path
|
||||
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_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
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_state
|
||||
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_pty_size_modes
|
||||
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_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
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_request_no_more_sessions
|
||||
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
|
||||
@@ -1,445 +0,0 @@
|
||||
_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_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
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_expand_path
|
||||
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_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
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_state
|
||||
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_pty_size_modes
|
||||
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_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
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_request_no_more_sessions
|
||||
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
|
||||
@@ -87,6 +87,7 @@ set(libssh_SRCS
|
||||
connector.c
|
||||
crypto_common.c
|
||||
curve25519.c
|
||||
sntrup761.c
|
||||
dh.c
|
||||
ecdh.c
|
||||
error.c
|
||||
@@ -174,6 +175,13 @@ if (WITH_GCRYPT)
|
||||
chachapoly.c
|
||||
)
|
||||
endif (NOT HAVE_GCRYPT_CHACHA_POLY)
|
||||
|
||||
if (HAVE_GCRYPT_CURVE25519)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
curve25519_gcrypt.c
|
||||
)
|
||||
endif(HAVE_GCRYPT_CURVE25519)
|
||||
elseif (WITH_MBEDTLS)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
@@ -190,6 +198,7 @@ elseif (WITH_MBEDTLS)
|
||||
external/fe25519.c
|
||||
external/ge25519.c
|
||||
external/sc25519.c
|
||||
external/sntrup761.c
|
||||
)
|
||||
if (NOT (HAVE_MBEDTLS_CHACHA20_H AND HAVE_MBEDTLS_POLY1305_H))
|
||||
set(libssh_SRCS
|
||||
@@ -199,17 +208,24 @@ elseif (WITH_MBEDTLS)
|
||||
chachapoly.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (HAVE_MBEDTLS_CURVE25519)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
curve25519_mbedcrypto.c
|
||||
)
|
||||
endif(HAVE_MBEDTLS_CURVE25519)
|
||||
else (WITH_GCRYPT)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
threads/libcrypto.c
|
||||
pki_crypto.c
|
||||
ecdh_crypto.c
|
||||
curve25519_crypto.c
|
||||
getrandom_crypto.c
|
||||
md_crypto.c
|
||||
libcrypto.c
|
||||
dh_crypto.c
|
||||
external/sntrup761.c
|
||||
)
|
||||
if (NOT HAVE_OPENSSL_EVP_CHACHA20)
|
||||
set(libssh_SRCS
|
||||
@@ -261,14 +277,22 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
|
||||
endif (WITH_GSSAPI AND GSSAPI_FOUND)
|
||||
|
||||
if (NOT WITH_NACL)
|
||||
if (NOT HAVE_LIBCRYPTO)
|
||||
if (NOT (HAVE_LIBCRYPTO OR HAVE_MBEDTLS_CURVE25519 OR HAVE_GCRYPT_CURVE25519))
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
curve25519_fallback.c
|
||||
external/curve25519_ref.c
|
||||
)
|
||||
endif()
|
||||
endif (NOT WITH_NACL)
|
||||
|
||||
if (HAVE_MLKEM)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
mlkem768.c
|
||||
)
|
||||
endif (HAVE_MLKEM)
|
||||
|
||||
# Set the path to the default map file
|
||||
set(MAP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.map")
|
||||
|
||||
|
||||
396
src/agent.c
396
src/agent.c
@@ -67,87 +67,94 @@
|
||||
(((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \
|
||||
((x) == SSH2_AGENT_FAILURE))
|
||||
|
||||
static uint32_t atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read) {
|
||||
char *b = buf;
|
||||
uint32_t pos = 0;
|
||||
ssize_t res;
|
||||
ssh_pollfd_t pfd;
|
||||
ssh_channel channel = agent->channel;
|
||||
socket_t fd;
|
||||
static uint32_t
|
||||
atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read)
|
||||
{
|
||||
char *b = buf;
|
||||
uint32_t pos = 0;
|
||||
ssize_t res;
|
||||
ssh_pollfd_t pfd;
|
||||
ssh_channel channel = agent->channel;
|
||||
socket_t fd;
|
||||
|
||||
/* Using a socket ? */
|
||||
if (channel == NULL) {
|
||||
fd = ssh_socket_get_fd(agent->sock);
|
||||
pfd.fd = fd;
|
||||
pfd.events = do_read ? POLLIN : POLLOUT;
|
||||
/* Using a socket ? */
|
||||
if (channel == NULL) {
|
||||
fd = ssh_socket_get_fd(agent->sock);
|
||||
pfd.fd = fd;
|
||||
pfd.events = do_read ? POLLIN : POLLOUT;
|
||||
|
||||
while (n > pos) {
|
||||
if (do_read) {
|
||||
res = recv(fd, b + pos, n - pos, 0);
|
||||
} else {
|
||||
res = send(fd, b + pos, n - pos, 0);
|
||||
}
|
||||
switch (res) {
|
||||
case -1:
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
while (n > pos) {
|
||||
if (do_read) {
|
||||
res = recv(fd, b + pos, n - pos, 0);
|
||||
} else {
|
||||
res = send(fd, b + pos, n - pos, 0);
|
||||
}
|
||||
switch (res) {
|
||||
case -1:
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
#ifdef EWOULDBLOCK
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
#else
|
||||
if (errno == EAGAIN) {
|
||||
if (errno == EAGAIN) {
|
||||
#endif
|
||||
(void) ssh_poll(&pfd, 1, -1);
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
case 0:
|
||||
/* read returns 0 on end-of-file */
|
||||
errno = do_read ? 0 : EPIPE;
|
||||
return pos;
|
||||
default:
|
||||
pos += (uint32_t) res;
|
||||
(void)ssh_poll(&pfd, 1, -1);
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
case 0:
|
||||
/* read returns 0 on end-of-file */
|
||||
errno = do_read ? 0 : EPIPE;
|
||||
return pos;
|
||||
default:
|
||||
pos += (uint32_t)res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
return pos;
|
||||
} else {
|
||||
/* using an SSH channel */
|
||||
while (n > pos){
|
||||
if (do_read)
|
||||
res = ssh_channel_read(channel,b + pos, n-pos, 0);
|
||||
else
|
||||
res = ssh_channel_write(channel, b+pos, n-pos);
|
||||
if (res == SSH_AGAIN)
|
||||
continue;
|
||||
if (res == SSH_ERROR)
|
||||
return 0;
|
||||
pos += (uint32_t)res;
|
||||
}
|
||||
return pos;
|
||||
/* using an SSH channel */
|
||||
while (n > pos) {
|
||||
if (do_read) {
|
||||
res = ssh_channel_read(channel, b + pos, n - pos, 0);
|
||||
} else {
|
||||
res = ssh_channel_write(channel, b + pos, n - pos);
|
||||
}
|
||||
if (res == SSH_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
if (res == SSH_ERROR) {
|
||||
return 0;
|
||||
}
|
||||
pos += (uint32_t)res;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
ssh_agent ssh_agent_new(struct ssh_session_struct *session) {
|
||||
ssh_agent agent = NULL;
|
||||
ssh_agent ssh_agent_new(struct ssh_session_struct *session)
|
||||
{
|
||||
ssh_agent agent = NULL;
|
||||
|
||||
agent = malloc(sizeof(struct ssh_agent_struct));
|
||||
if (agent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ZERO_STRUCTP(agent);
|
||||
agent = calloc(1, sizeof(struct ssh_agent_struct));
|
||||
if (agent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
agent->count = 0;
|
||||
agent->sock = ssh_socket_new(session);
|
||||
if (agent->sock == NULL) {
|
||||
SAFE_FREE(agent);
|
||||
return NULL;
|
||||
}
|
||||
agent->channel = NULL;
|
||||
return agent;
|
||||
agent->count = 0;
|
||||
agent->sock = ssh_socket_new(session);
|
||||
if (agent->sock == NULL) {
|
||||
SAFE_FREE(agent);
|
||||
return NULL;
|
||||
}
|
||||
agent->channel = NULL;
|
||||
return agent;
|
||||
}
|
||||
|
||||
static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channel){
|
||||
agent->channel = channel;
|
||||
static void agent_set_channel(struct ssh_agent_struct *agent,
|
||||
ssh_channel channel)
|
||||
{
|
||||
agent->channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,15 +175,19 @@ static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channe
|
||||
* @returns SSH_OK in case of success
|
||||
* SSH_ERROR in case of an error
|
||||
*/
|
||||
int ssh_set_agent_channel(ssh_session session, ssh_channel channel){
|
||||
if (!session)
|
||||
return SSH_ERROR;
|
||||
if (!session->agent){
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
agent_set_channel(session->agent, channel);
|
||||
return SSH_OK;
|
||||
int ssh_set_agent_channel(ssh_session session, ssh_channel channel)
|
||||
{
|
||||
if (!session) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (!session->agent) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Session has no active agent");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
agent_set_channel(session->agent, channel);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @brief sets the SSH agent socket.
|
||||
@@ -187,64 +198,71 @@ int ssh_set_agent_channel(ssh_session session, ssh_channel channel){
|
||||
* @returns SSH_OK in case of success
|
||||
* SSH_ERROR in case of an error
|
||||
*/
|
||||
int ssh_set_agent_socket(ssh_session session, socket_t fd){
|
||||
if (!session)
|
||||
return SSH_ERROR;
|
||||
if (!session->agent){
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
int ssh_set_agent_socket(ssh_session session, socket_t fd)
|
||||
{
|
||||
if (!session) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (!session->agent) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Session has no active agent");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ssh_socket_set_fd(session->agent->sock, fd);
|
||||
return SSH_OK;
|
||||
return ssh_socket_set_fd(session->agent->sock, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
void ssh_agent_close(struct ssh_agent_struct *agent) {
|
||||
if (agent == NULL) {
|
||||
return;
|
||||
}
|
||||
void ssh_agent_close(struct ssh_agent_struct *agent)
|
||||
{
|
||||
if (agent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ssh_socket_close(agent->sock);
|
||||
ssh_socket_close(agent->sock);
|
||||
}
|
||||
|
||||
void ssh_agent_free(ssh_agent agent) {
|
||||
if (agent) {
|
||||
if (agent->ident) {
|
||||
SSH_BUFFER_FREE(agent->ident);
|
||||
void ssh_agent_free(ssh_agent agent)
|
||||
{
|
||||
if (agent) {
|
||||
if (agent->ident) {
|
||||
SSH_BUFFER_FREE(agent->ident);
|
||||
}
|
||||
if (agent->sock) {
|
||||
ssh_agent_close(agent);
|
||||
ssh_socket_free(agent->sock);
|
||||
}
|
||||
SAFE_FREE(agent);
|
||||
}
|
||||
if (agent->sock) {
|
||||
ssh_agent_close(agent);
|
||||
ssh_socket_free(agent->sock);
|
||||
}
|
||||
SAFE_FREE(agent);
|
||||
}
|
||||
}
|
||||
|
||||
static int agent_connect(ssh_session session) {
|
||||
const char *auth_sock = NULL;
|
||||
static int agent_connect(ssh_session session)
|
||||
{
|
||||
const char *auth_sock = NULL;
|
||||
|
||||
if (session == NULL || session->agent == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->agent->channel != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (session == NULL || session->agent == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->agent->channel != NULL)
|
||||
return 0;
|
||||
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -268,61 +286,66 @@ static int agent_decode_reply(struct ssh_session_struct *session, int type) {
|
||||
#endif
|
||||
|
||||
static int agent_talk(struct ssh_session_struct *session,
|
||||
struct ssh_buffer_struct *request, struct ssh_buffer_struct *reply) {
|
||||
uint32_t len = 0;
|
||||
uint8_t tmpbuf[4];
|
||||
uint8_t *payload = tmpbuf;
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
struct ssh_buffer_struct *request,
|
||||
struct ssh_buffer_struct *reply)
|
||||
{
|
||||
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: %" PRIu32, len);
|
||||
PUSH_BE_U32(payload, 0, len);
|
||||
len = ssh_buffer_get_len(request);
|
||||
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_TRACE, "atomicio sending request failed: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
/* 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_TRACE,
|
||||
"atomicio sending request failed: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"atomicio sending request length failed: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"atomicio sending request length failed: %s",
|
||||
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_TRACE, "atomicio read response length failed: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
/* wait for response, read the length of the response packet */
|
||||
if (atomicio(session->agent, payload, 4, 1) != 4) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"atomicio read response length failed: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = PULL_BE_U32(payload, 0);
|
||||
if (len > 256 * 1024) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Authentication response too long: %" PRIu32, len);
|
||||
return -1;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
|
||||
len = PULL_BE_U32(payload, 0);
|
||||
if (len > 256 * 1024) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Authentication response too long: %" PRIu32,
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
|
||||
|
||||
payload = ssh_buffer_allocate(reply, len);
|
||||
if (payload == NULL) {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
|
||||
return -1;
|
||||
}
|
||||
payload = ssh_buffer_allocate(reply, len);
|
||||
if (payload == NULL) {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (atomicio(session->agent, payload, len, 1) != len) {
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Error reading response from authentication socket.");
|
||||
/* Rollback the unused space */
|
||||
ssh_buffer_pass_bytes_end(reply, len);
|
||||
return -1;
|
||||
}
|
||||
if (atomicio(session->agent, payload, len, 1) != len) {
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Error reading response from authentication socket.");
|
||||
/* Rollback the unused space */
|
||||
ssh_buffer_pass_bytes_end(reply, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
|
||||
@@ -471,22 +494,23 @@ ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
|
||||
return key;
|
||||
}
|
||||
|
||||
int ssh_agent_is_running(ssh_session session) {
|
||||
if (session == NULL || session->agent == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ssh_socket_is_open(session->agent->sock)) {
|
||||
return 1;
|
||||
} else {
|
||||
if (agent_connect(session) < 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
int ssh_agent_is_running(ssh_session session)
|
||||
{
|
||||
if (session == NULL || session->agent == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (ssh_socket_is_open(session->agent->sock)) {
|
||||
return 1;
|
||||
} else {
|
||||
if (agent_connect(session) < 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssh_string ssh_agent_sign_data(ssh_session session,
|
||||
@@ -500,6 +524,7 @@ ssh_string ssh_agent_sign_data(ssh_session session,
|
||||
unsigned int type = 0;
|
||||
unsigned int flags = 0;
|
||||
uint32_t dlen;
|
||||
size_t request_len;
|
||||
int rc;
|
||||
|
||||
request = ssh_buffer_new();
|
||||
@@ -525,11 +550,14 @@ ssh_string ssh_agent_sign_data(ssh_session session,
|
||||
* - 2 x uint32_t
|
||||
* - 1 x ssh_string (uint8_t + data)
|
||||
*/
|
||||
rc = ssh_buffer_allocate_size(request,
|
||||
sizeof(uint8_t) * 2 +
|
||||
sizeof(uint32_t) * 2 +
|
||||
ssh_string_len(key_blob));
|
||||
request_len = sizeof(uint8_t) * 2 +
|
||||
sizeof(uint32_t) * 2 +
|
||||
ssh_string_len(key_blob);
|
||||
/* this can't overflow the uint32_t as the
|
||||
* STRING_SIZE_MAX is (UINT32_MAX >> 8) + 1 */
|
||||
rc = ssh_buffer_allocate_size(request, (uint32_t)request_len);
|
||||
if (rc < 0) {
|
||||
SSH_STRING_FREE(key_blob);
|
||||
SSH_BUFFER_FREE(request);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
228
src/auth.c
228
src/auth.c
@@ -463,6 +463,108 @@ fail:
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Adds the server's public key to the authentication request.
|
||||
*
|
||||
* This function is used internally when the hostbound public key authentication
|
||||
* extension is enabled. It export the server's public key and adds it to the
|
||||
* authentication buffer.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
*/
|
||||
static int add_hostbound_pubkey(ssh_session session)
|
||||
{
|
||||
int rc;
|
||||
ssh_string server_pubkey_s = NULL;
|
||||
|
||||
if (session == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->current_crypto == NULL ||
|
||||
session->current_crypto->server_pubkey == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid session or server public key");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(session->current_crypto->server_pubkey,
|
||||
&server_pubkey_s);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_s);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
SSH_STRING_FREE(server_pubkey_s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Build a public key authentication request.
|
||||
*
|
||||
* This helper function creates a SSH2_MSG_USERAUTH_REQUEST message for public
|
||||
* key authentication and adds the server's public key if the hostbound
|
||||
* extension is enabled.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
* @param[in] username The username, may be NULL.
|
||||
* @param[in] auth_type Authentication type (0 for key offer, 1 for actual
|
||||
* auth).
|
||||
* @param[in] sig_type_c The signature algorithm name.
|
||||
* @param[in] pubkey_s The public key string.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
*/
|
||||
static int build_pubkey_auth_request(ssh_session session,
|
||||
const char *username,
|
||||
int has_signature,
|
||||
const char *sig_type_c,
|
||||
ssh_string pubkey_s)
|
||||
{
|
||||
int rc;
|
||||
const char *auth_method = "publickey";
|
||||
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
|
||||
auth_method = "publickey-hostbound-v00@openssh.com";
|
||||
}
|
||||
|
||||
/* request */
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bsssbsS",
|
||||
SSH2_MSG_USERAUTH_REQUEST,
|
||||
username ? username : session->opts.username,
|
||||
"ssh-connection",
|
||||
auth_method,
|
||||
has_signature, /* private key? */
|
||||
sig_type_c, /* algo */
|
||||
pubkey_s /* public key */
|
||||
);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
|
||||
rc = add_hostbound_pubkey(session);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to authenticate with the given public key.
|
||||
*
|
||||
@@ -509,17 +611,17 @@ int ssh_userauth_try_publickey(ssh_session session,
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
switch(session->pending_call_state) {
|
||||
case SSH_PENDING_CALL_NONE:
|
||||
break;
|
||||
case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY:
|
||||
goto pending;
|
||||
default:
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Wrong state (%d) during pending SSH call",
|
||||
session->pending_call_state);
|
||||
return SSH_AUTH_ERROR;
|
||||
switch (session->pending_call_state) {
|
||||
case SSH_PENDING_CALL_NONE:
|
||||
break;
|
||||
case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY:
|
||||
goto pending;
|
||||
default:
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Wrong state (%d) during pending SSH call",
|
||||
session->pending_call_state);
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/* Note, that this is intentionally before checking the signature type
|
||||
@@ -536,13 +638,15 @@ int ssh_userauth_try_publickey(ssh_session session,
|
||||
/* Check if the given public key algorithm is allowed */
|
||||
sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type);
|
||||
if (sig_type_c == NULL) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Invalid key type (unknown)");
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
rc = ssh_key_algorithm_allowed(session, sig_type_c);
|
||||
if (!rc) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
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);
|
||||
@@ -550,9 +654,12 @@ int ssh_userauth_try_publickey(ssh_session session,
|
||||
}
|
||||
allowed = ssh_key_size_allowed(session, pubkey);
|
||||
if (!allowed) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
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));
|
||||
"RSA_MIN_SIZE",
|
||||
sig_type_c,
|
||||
ssh_key_size(pubkey));
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
@@ -563,20 +670,10 @@ int ssh_userauth_try_publickey(ssh_session session,
|
||||
}
|
||||
|
||||
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,
|
||||
username ? username : session->opts.username,
|
||||
"ssh-connection",
|
||||
"publickey",
|
||||
0, /* private key ? */
|
||||
sig_type_c, /* algo */
|
||||
pubkey_s /* public key */
|
||||
);
|
||||
rc = build_pubkey_auth_request(session, username, 0, sig_type_c, pubkey_s);
|
||||
if (rc < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SSH_STRING_FREE(pubkey_s);
|
||||
|
||||
session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY;
|
||||
@@ -645,16 +742,17 @@ int ssh_userauth_publickey(ssh_session session,
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
switch(session->pending_call_state) {
|
||||
case SSH_PENDING_CALL_NONE:
|
||||
break;
|
||||
case SSH_PENDING_CALL_AUTH_PUBKEY:
|
||||
goto pending;
|
||||
default:
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Bad call during pending SSH call in ssh_userauth_try_publickey");
|
||||
return SSH_AUTH_ERROR;
|
||||
switch (session->pending_call_state) {
|
||||
case SSH_PENDING_CALL_NONE:
|
||||
break;
|
||||
case SSH_PENDING_CALL_AUTH_PUBKEY:
|
||||
goto pending;
|
||||
default:
|
||||
ssh_set_error(
|
||||
session,
|
||||
SSH_FATAL,
|
||||
"Bad call during pending SSH call in ssh_userauth_try_publickey");
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/* Note, that this is intentionally before checking the signature type
|
||||
@@ -674,13 +772,15 @@ int ssh_userauth_publickey(ssh_session session,
|
||||
/* Check if the given public key algorithm is allowed */
|
||||
sig_type_c = ssh_key_get_signature_algorithm(session, key_type);
|
||||
if (sig_type_c == NULL) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Invalid key type (unknown)");
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
rc = ssh_key_algorithm_allowed(session, sig_type_c);
|
||||
if (!rc) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
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);
|
||||
@@ -688,9 +788,12 @@ int ssh_userauth_publickey(ssh_session session,
|
||||
}
|
||||
allowed = ssh_key_size_allowed(session, privkey);
|
||||
if (!allowed) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
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));
|
||||
"RSA_MIN_SIZE",
|
||||
sig_type_c,
|
||||
ssh_key_size(privkey));
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
@@ -701,16 +804,7 @@ int ssh_userauth_publickey(ssh_session session,
|
||||
}
|
||||
|
||||
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,
|
||||
username ? username : session->opts.username,
|
||||
"ssh-connection",
|
||||
"publickey",
|
||||
1, /* private key */
|
||||
sig_type_c, /* algo */
|
||||
str /* public key or cert */
|
||||
);
|
||||
rc = build_pubkey_auth_request(session, username, 1, sig_type_c, str);
|
||||
if (rc < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -798,14 +892,16 @@ static int ssh_userauth_agent_publickey(ssh_session session,
|
||||
/* Check if the given public key algorithm is allowed */
|
||||
sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type);
|
||||
if (sig_type_c == NULL) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Invalid key type (unknown)");
|
||||
SSH_STRING_FREE(pubkey_s);
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
rc = ssh_key_algorithm_allowed(session, sig_type_c);
|
||||
if (!rc) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
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);
|
||||
@@ -814,27 +910,21 @@ static int ssh_userauth_agent_publickey(ssh_session session,
|
||||
}
|
||||
allowed = ssh_key_size_allowed(session, pubkey);
|
||||
if (!allowed) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
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));
|
||||
"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 */
|
||||
);
|
||||
SSH_STRING_FREE(pubkey_s);
|
||||
rc = build_pubkey_auth_request(session, username, 1, sig_type_c, pubkey_s);
|
||||
if (rc < 0) {
|
||||
goto fail;
|
||||
}
|
||||
SSH_STRING_FREE(pubkey_s);
|
||||
|
||||
/* sign the buffer with the private key */
|
||||
sig_blob = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey);
|
||||
@@ -892,7 +982,7 @@ void ssh_agent_state_free(void *data)
|
||||
if (state) {
|
||||
SSH_STRING_FREE_CHAR(state->comment);
|
||||
ssh_key_free(state->pubkey);
|
||||
free (state);
|
||||
free(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,8 +1008,7 @@ void ssh_agent_state_free(void *data)
|
||||
* authentication. The username should only be set with ssh_options_set() only
|
||||
* before you connect to the server.
|
||||
*/
|
||||
int ssh_userauth_agent(ssh_session session,
|
||||
const char *username)
|
||||
int ssh_userauth_agent(ssh_session session, const char *username)
|
||||
{
|
||||
int rc = SSH_AUTH_ERROR;
|
||||
struct ssh_agent_state_struct *state = NULL;
|
||||
@@ -938,12 +1027,11 @@ int ssh_userauth_agent(ssh_session session,
|
||||
}
|
||||
|
||||
if (!session->agent_state) {
|
||||
session->agent_state = malloc(sizeof(struct ssh_agent_state_struct));
|
||||
session->agent_state = calloc(1, sizeof(struct ssh_agent_state_struct));
|
||||
if (!session->agent_state) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
ZERO_STRUCTP(session->agent_state);
|
||||
session->agent_state->state = SSH_AGENT_STATE_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ error:
|
||||
static int to_block4(unsigned long *block, const char *source, int num)
|
||||
{
|
||||
const char *ptr = NULL;
|
||||
unsigned int i;
|
||||
size_t i;
|
||||
|
||||
*block = 0;
|
||||
if (num < 1) {
|
||||
|
||||
73
src/bignum.c
73
src/bignum.c
@@ -27,40 +27,52 @@
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/string.h"
|
||||
|
||||
ssh_string ssh_make_bignum_string(bignum num) {
|
||||
ssh_string ptr = NULL;
|
||||
size_t pad = 0;
|
||||
size_t len = bignum_num_bytes(num);
|
||||
size_t bits = bignum_num_bits(num);
|
||||
static ssh_string make_bignum_string(bignum num, size_t pad_to_len)
|
||||
{
|
||||
ssh_string ptr = NULL;
|
||||
size_t pad = 0;
|
||||
size_t len = bignum_num_bytes(num);
|
||||
size_t bits = bignum_num_bits(num);
|
||||
|
||||
if (len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If the first bit is set we have a negative number */
|
||||
if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) {
|
||||
pad++;
|
||||
}
|
||||
if (pad_to_len == 0) {
|
||||
/* If the first bit is set we have a negative number */
|
||||
if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) {
|
||||
pad++;
|
||||
}
|
||||
} else {
|
||||
if (len > pad_to_len) {
|
||||
return NULL;
|
||||
}
|
||||
pad = pad_to_len - len;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"%zu bits, %zu bytes, %zu padding",
|
||||
bits, len, pad);
|
||||
SSH_LOG(SSH_LOG_TRACE, "%zu bits, %zu bytes, %zu padding", bits, len, pad);
|
||||
#endif /* DEBUG_CRYPTO */
|
||||
|
||||
ptr = ssh_string_new(len + pad);
|
||||
if (ptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ptr = ssh_string_new(len + pad);
|
||||
if (ptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We have a negative number so we need a leading zero */
|
||||
if (pad) {
|
||||
ptr->data[0] = 0;
|
||||
}
|
||||
/* We have a negative number so we need a leading zero */
|
||||
if (pad) {
|
||||
memset(ptr->data, 0, pad);
|
||||
}
|
||||
|
||||
bignum_bn2bin(num, len, ptr->data + pad);
|
||||
bignum_bn2bin(num, len, ptr->data + pad);
|
||||
|
||||
return ptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ssh_string ssh_make_bignum_string(bignum num)
|
||||
{
|
||||
return make_bignum_string(num, 0);
|
||||
}
|
||||
|
||||
ssh_string ssh_make_padded_bignum_string(bignum num, size_t pad_len)
|
||||
{
|
||||
return make_bignum_string(num, pad_len);
|
||||
}
|
||||
|
||||
bignum ssh_make_string_bn(ssh_string string)
|
||||
@@ -71,10 +83,11 @@ bignum ssh_make_string_bn(ssh_string string)
|
||||
#ifdef DEBUG_CRYPTO
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Importing a %zu bits, %zu bytes object ...",
|
||||
len * 8, len);
|
||||
len * 8,
|
||||
len);
|
||||
#endif /* DEBUG_CRYPTO */
|
||||
|
||||
bignum_bin2bn(string->data, len, &bn);
|
||||
bignum_bin2bn(string->data, (int)len, &bn);
|
||||
|
||||
return bn;
|
||||
}
|
||||
@@ -86,7 +99,9 @@ void ssh_print_bignum(const char *name, const_bignum num)
|
||||
if (num != NULL) {
|
||||
bignum_bn2hex(num, &hex);
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "%s value: %s", name,
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"%s value: %s",
|
||||
name,
|
||||
(hex == NULL) ? "(null)" : (char *)hex);
|
||||
ssh_crypto_free(hex);
|
||||
}
|
||||
|
||||
33
src/bind.c
33
src/bind.c
@@ -218,28 +218,12 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int ssh_bind_listen(ssh_bind sshbind) {
|
||||
int ssh_bind_listen(ssh_bind sshbind)
|
||||
{
|
||||
const char *host = NULL;
|
||||
socket_t fd;
|
||||
int rc;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
/* Apply global bind configurations, if it hasn't been applied before */
|
||||
rc = ssh_bind_options_parse_config(sshbind, NULL);
|
||||
if (rc != 0) {
|
||||
@@ -289,10 +273,10 @@ int ssh_bind_listen(ssh_bind sshbind) {
|
||||
}
|
||||
|
||||
sshbind->bindfd = fd;
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
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)
|
||||
@@ -505,7 +489,10 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
|
||||
ssh_set_error_oom(sshbind);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_socket_set_fd(session->socket, fd);
|
||||
rc = ssh_socket_set_fd(session->socket, fd);
|
||||
if (rc != SSH_OK) {
|
||||
return rc;
|
||||
}
|
||||
handle = ssh_socket_get_poll_handle(session->socket);
|
||||
if (handle == NULL) {
|
||||
ssh_set_error_oom(sshbind);
|
||||
|
||||
@@ -104,6 +104,11 @@ ssh_bind_config_keyword_table[] = {
|
||||
.opcode = BIND_CFG_HOSTKEY_ALGORITHMS,
|
||||
.allowed_in_match = true
|
||||
},
|
||||
{
|
||||
.name = "requiredrsasize",
|
||||
.opcode = BIND_CFG_REQUIRED_RSA_SIZE,
|
||||
.allowed_in_match = true
|
||||
},
|
||||
{
|
||||
.opcode = BIND_CFG_UNKNOWN,
|
||||
}
|
||||
@@ -293,6 +298,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
|
||||
const char *p = NULL;
|
||||
char *s = NULL, *x = NULL;
|
||||
char *keyword = NULL;
|
||||
long l;
|
||||
size_t len;
|
||||
|
||||
int rc = 0;
|
||||
@@ -594,6 +600,18 @@ ssh_bind_config_parse_line(ssh_bind bind,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BIND_CFG_REQUIRED_RSA_SIZE:
|
||||
l = ssh_config_get_long(&s, -1);
|
||||
if (l >= 0 && (*parser_flags & PARSING)) {
|
||||
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSA_MIN_SIZE, &l);
|
||||
if (rc != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"line %d: Failed to set RequiredRSASize value '%ld'",
|
||||
count,
|
||||
l);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BIND_CFG_NOT_ALLOWED_IN_MATCH:
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Option not allowed in Match block: %s, line: %d",
|
||||
keyword, count);
|
||||
@@ -669,7 +687,8 @@ 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;
|
||||
unsigned int line_num = 0;
|
||||
size_t line_len;
|
||||
uint32_t parser_flags;
|
||||
int rv;
|
||||
|
||||
@@ -698,8 +717,10 @@ int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
|
||||
}
|
||||
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);
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"Line %u too long: %zu characters",
|
||||
line_num,
|
||||
line_len);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(line, line_start, line_len);
|
||||
|
||||
141
src/buffer.c
141
src/buffer.c
@@ -407,20 +407,26 @@ void *ssh_buffer_allocate(struct ssh_buffer_struct *buffer, uint32_t len)
|
||||
*
|
||||
* @return 0 on success, < 0 on error.
|
||||
*/
|
||||
int ssh_buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
|
||||
struct ssh_string_struct *string) {
|
||||
uint32_t len = 0;
|
||||
int
|
||||
ssh_buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
|
||||
struct ssh_string_struct *string)
|
||||
{
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
if (string == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (string == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = ssh_string_len(string);
|
||||
if (ssh_buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
len = ssh_string_len(string) + sizeof(uint32_t);
|
||||
/* this can't overflow the uint32_t as the
|
||||
* STRING_SIZE_MAX is (UINT32_MAX >> 8) + 1 */
|
||||
rc = ssh_buffer_add_data(buffer, string, (uint32_t)len);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -611,6 +617,53 @@ uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){
|
||||
return buffer->used - buffer->pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Duplicate an existing buffer.
|
||||
*
|
||||
* Creates a new ssh_buffer and copies all data from the source buffer.
|
||||
* The new buffer preserves the secure flag setting of the source.
|
||||
*
|
||||
* @param[in] buffer The buffer to duplicate. Can be NULL.
|
||||
*
|
||||
* @return A new buffer containing a copy of the data on success,
|
||||
* NULL on failure or if buffer is NULL.
|
||||
*
|
||||
* @see ssh_buffer_free()
|
||||
*/
|
||||
ssh_buffer ssh_buffer_dup(const ssh_buffer buffer)
|
||||
{
|
||||
ssh_buffer new_buffer = NULL;
|
||||
int rc;
|
||||
|
||||
if (buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer_verify(buffer);
|
||||
|
||||
new_buffer = ssh_buffer_new();
|
||||
if (new_buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_buffer->secure = buffer->secure;
|
||||
|
||||
if (ssh_buffer_get_len(buffer) > 0) {
|
||||
rc = ssh_buffer_add_data(new_buffer,
|
||||
ssh_buffer_get(buffer),
|
||||
ssh_buffer_get_len(buffer));
|
||||
if (rc != SSH_OK) {
|
||||
ssh_buffer_free(new_buffer);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_verify(new_buffer);
|
||||
return new_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@@ -823,6 +876,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
|
||||
const char *p = NULL;
|
||||
ssh_string string = NULL;
|
||||
char *cstring = NULL;
|
||||
bignum b = NULL;
|
||||
size_t needed_size = 0;
|
||||
size_t len;
|
||||
size_t count;
|
||||
@@ -867,13 +921,18 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
|
||||
va_arg(ap, void *);
|
||||
count++; /* increase argument count */
|
||||
break;
|
||||
case 'F':
|
||||
case 'B':
|
||||
va_arg(ap, bignum);
|
||||
/*
|
||||
* Use a fixed size for a bignum
|
||||
* (they should normally be around 32)
|
||||
*/
|
||||
needed_size += 64;
|
||||
b = va_arg(ap, bignum);
|
||||
if (*p == 'F') {
|
||||
/* For padded bignum, we know the exact length */
|
||||
len = va_arg(ap, size_t);
|
||||
count++; /* increase argument count */
|
||||
needed_size += sizeof(uint32_t) + len;
|
||||
} else {
|
||||
/* The bignum bytes + 1 for possible padding */
|
||||
needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
cstring = va_arg(ap, char *);
|
||||
@@ -979,24 +1038,40 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
|
||||
case 's':
|
||||
cstring = va_arg(ap, char *);
|
||||
len = strlen(cstring);
|
||||
rc = ssh_buffer_add_u32(buffer, htonl(len));
|
||||
if (len > UINT32_MAX) {
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
o.dword = (uint32_t)len;
|
||||
rc = ssh_buffer_add_u32(buffer, htonl(o.dword));
|
||||
if (rc == SSH_OK){
|
||||
rc = ssh_buffer_add_data(buffer, cstring, len);
|
||||
rc = ssh_buffer_add_data(buffer, cstring, o.dword);
|
||||
}
|
||||
cstring = NULL;
|
||||
break;
|
||||
case 'P':
|
||||
len = va_arg(ap, size_t);
|
||||
if (len > UINT32_MAX) {
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
o.data = va_arg(ap, void *);
|
||||
count++; /* increase argument count */
|
||||
|
||||
rc = ssh_buffer_add_data(buffer, o.data, len);
|
||||
rc = ssh_buffer_add_data(buffer, o.data, (uint32_t)len);
|
||||
o.data = NULL;
|
||||
break;
|
||||
case 'F':
|
||||
case 'B':
|
||||
b = va_arg(ap, bignum);
|
||||
o.string = ssh_make_bignum_string(b);
|
||||
if (*p == 'F') {
|
||||
len = va_arg(ap, size_t);
|
||||
count++; /* increase argument count */
|
||||
o.string = ssh_make_padded_bignum_string(b, len);
|
||||
} else {
|
||||
o.string = ssh_make_bignum_string(b);
|
||||
}
|
||||
if(o.string == NULL){
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
@@ -1007,7 +1082,11 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
|
||||
case 't':
|
||||
cstring = va_arg(ap, char *);
|
||||
len = strlen(cstring);
|
||||
rc = ssh_buffer_add_data(buffer, cstring, len);
|
||||
if (len > UINT32_MAX) {
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
rc = ssh_buffer_add_data(buffer, cstring, (uint32_t)len);
|
||||
cstring = NULL;
|
||||
break;
|
||||
default:
|
||||
@@ -1048,6 +1127,8 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
|
||||
* 'P': size_t, void * (len of data, pointer to data)
|
||||
* only pushes data.
|
||||
* 'B': bignum (pushed as SSH string)
|
||||
* 'F': bignum, size_t (bignum, padded to fixed length,
|
||||
* pushed as SSH string)
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @warning when using 'P' with a constant size (e.g. 8), do not
|
||||
@@ -1187,28 +1268,28 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
|
||||
if (rlen != 4){
|
||||
break;
|
||||
}
|
||||
len = ntohl(u32len);
|
||||
if (len > max_len - 1) {
|
||||
u32len = ntohl(u32len);
|
||||
if (u32len > max_len - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_validate_length(buffer, len);
|
||||
rc = ssh_buffer_validate_length(buffer, u32len);
|
||||
if (rc != SSH_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
*o.cstring = malloc(len + 1);
|
||||
*o.cstring = malloc(u32len + 1);
|
||||
if (*o.cstring == NULL){
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
rlen = ssh_buffer_get_data(buffer, *o.cstring, len);
|
||||
if (rlen != len){
|
||||
rlen = ssh_buffer_get_data(buffer, *o.cstring, u32len);
|
||||
if (rlen != u32len) {
|
||||
SAFE_FREE(*o.cstring);
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
(*o.cstring)[len] = '\0';
|
||||
(*o.cstring)[u32len] = '\0';
|
||||
o.cstring = NULL;
|
||||
rc = SSH_OK;
|
||||
break;
|
||||
@@ -1233,7 +1314,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
rlen = ssh_buffer_get_data(buffer, *o.data, len);
|
||||
rlen = ssh_buffer_get_data(buffer, *o.data, (uint32_t)len);
|
||||
if (rlen != len){
|
||||
SAFE_FREE(*o.data);
|
||||
rc = SSH_ERROR;
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/callbacks.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#define is_callback_valid(session, cb) \
|
||||
(cb->size <= 0 || cb->size > 1024 * sizeof(void *))
|
||||
(cb->size > 0 || cb->size <= 1024 * sizeof(void *))
|
||||
|
||||
/* LEGACY */
|
||||
static void ssh_legacy_log_callback(int priority,
|
||||
@@ -45,8 +45,7 @@ static void ssh_legacy_log_callback(int priority,
|
||||
log_fn(session, priority, buffer, log_data);
|
||||
}
|
||||
|
||||
void
|
||||
_ssh_remove_legacy_log_cb(void)
|
||||
void _ssh_remove_legacy_log_cb(void)
|
||||
{
|
||||
if (ssh_get_log_callback() == ssh_legacy_log_callback) {
|
||||
_ssh_reset_log_cb();
|
||||
@@ -54,26 +53,27 @@ _ssh_remove_legacy_log_cb(void)
|
||||
}
|
||||
}
|
||||
|
||||
int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) {
|
||||
if (session == NULL || cb == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
int ssh_set_callbacks(ssh_session session, ssh_callbacks cb)
|
||||
{
|
||||
if (session == NULL || cb == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (is_callback_valid(session, cb)) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid callback passed in (badly initialized)");
|
||||
return SSH_ERROR;
|
||||
};
|
||||
session->common.callbacks = cb;
|
||||
if (!is_callback_valid(session, cb)) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid callback passed in (badly initialized)");
|
||||
return SSH_ERROR;
|
||||
};
|
||||
session->common.callbacks = cb;
|
||||
|
||||
/* LEGACY */
|
||||
if (ssh_get_log_callback() == NULL && cb->log_function) {
|
||||
ssh_set_log_callback(ssh_legacy_log_callback);
|
||||
ssh_set_log_userdata(session);
|
||||
}
|
||||
/* LEGACY */
|
||||
if (ssh_get_log_callback() == NULL && cb->log_function) {
|
||||
ssh_set_log_callback(ssh_legacy_log_callback);
|
||||
ssh_set_log_userdata(session);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssh_add_set_channel_callbacks(ssh_channel channel,
|
||||
@@ -84,11 +84,11 @@ static int ssh_add_set_channel_callbacks(ssh_channel channel,
|
||||
int rc;
|
||||
|
||||
if (channel == NULL || cb == NULL) {
|
||||
return SSH_ERROR;
|
||||
return SSH_ERROR;
|
||||
}
|
||||
session = channel->session;
|
||||
|
||||
if (is_callback_valid(session, cb)) {
|
||||
if (!is_callback_valid(session, cb)) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid callback passed in (badly initialized)");
|
||||
@@ -96,7 +96,7 @@ static int ssh_add_set_channel_callbacks(ssh_channel channel,
|
||||
};
|
||||
if (channel->callbacks == NULL) {
|
||||
channel->callbacks = ssh_list_new();
|
||||
if (channel->callbacks == NULL){
|
||||
if (channel->callbacks == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
@@ -124,12 +124,12 @@ int ssh_remove_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
|
||||
{
|
||||
struct ssh_iterator *it = NULL;
|
||||
|
||||
if (channel == NULL || channel->callbacks == NULL){
|
||||
if (channel == NULL || channel->callbacks == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
it = ssh_list_find(channel->callbacks, cb);
|
||||
if (it == NULL){
|
||||
if (it == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
@@ -138,19 +138,19 @@ int ssh_remove_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb)
|
||||
{
|
||||
if (session == NULL || cb == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb){
|
||||
if (session == NULL || cb == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (is_callback_valid(session, cb)) {
|
||||
if (!is_callback_valid(session, cb)) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid callback passed in (badly initialized)");
|
||||
return SSH_ERROR;
|
||||
};
|
||||
session->server_callbacks = cb;
|
||||
session->server_callbacks = cb;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
1729
src/channels.c
1729
src/channels.c
File diff suppressed because it is too large
Load Diff
72
src/client.c
72
src/client.c
@@ -46,6 +46,9 @@
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/kex.h"
|
||||
#ifdef HAVE_MLKEM
|
||||
#include "libssh/mlkem768.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef HAVE_PTHREAD
|
||||
@@ -226,7 +229,7 @@ int ssh_send_banner(ssh_session session, int server)
|
||||
terminator);
|
||||
}
|
||||
|
||||
rc = ssh_socket_write(session->socket, buffer, strlen(buffer));
|
||||
rc = ssh_socket_write(session->socket, buffer, (uint32_t)strlen(buffer));
|
||||
if (rc == SSH_ERROR) {
|
||||
goto end;
|
||||
}
|
||||
@@ -235,8 +238,8 @@ int ssh_send_banner(ssh_session session, int server)
|
||||
ssh_pcap_context_write(session->pcap_ctx,
|
||||
SSH_PCAP_DIR_OUT,
|
||||
buffer,
|
||||
strlen(buffer),
|
||||
strlen(buffer));
|
||||
(uint32_t)strlen(buffer),
|
||||
(uint32_t)strlen(buffer));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -254,45 +257,58 @@ end:
|
||||
*/
|
||||
int dh_handshake(ssh_session session)
|
||||
{
|
||||
int rc = SSH_AGAIN;
|
||||
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);
|
||||
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) {
|
||||
switch (session->dh_handshake_state) {
|
||||
case DH_STATE_INIT:
|
||||
switch(session->next_crypto->kex_type){
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
rc = ssh_client_dh_init(session);
|
||||
break;
|
||||
rc = ssh_client_dh_init(session);
|
||||
break;
|
||||
#ifdef WITH_GEX
|
||||
case SSH_KEX_DH_GEX_SHA1:
|
||||
case SSH_KEX_DH_GEX_SHA256:
|
||||
rc = ssh_client_dhgex_init(session);
|
||||
break;
|
||||
rc = ssh_client_dhgex_init(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:
|
||||
rc = ssh_client_ecdh_init(session);
|
||||
break;
|
||||
rc = ssh_client_ecdh_init(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_CURVE25519
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
rc = ssh_client_curve25519_init(session);
|
||||
break;
|
||||
rc = ssh_client_curve25519_init(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_SNTRUP761
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
rc = ssh_client_sntrup761x25519_init(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
rc = ssh_client_mlkem768x25519_init(session);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rc = SSH_ERROR;
|
||||
}
|
||||
rc = SSH_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
case DH_STATE_INIT_SENT:
|
||||
/* wait until ssh_packet_dh_reply is called */
|
||||
break;
|
||||
@@ -300,15 +316,17 @@ int dh_handshake(ssh_session session)
|
||||
/* wait until ssh_packet_newkeys is called */
|
||||
break;
|
||||
case DH_STATE_FINISHED:
|
||||
return SSH_OK;
|
||||
return SSH_OK;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d",
|
||||
session->dh_handshake_state);
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid state in dh_handshake(): %d",
|
||||
session->dh_handshake_state);
|
||||
|
||||
return SSH_ERROR;
|
||||
}
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ssh_service_request_termination(void *s)
|
||||
@@ -593,8 +611,7 @@ int ssh_connect(ssh_session session)
|
||||
|
||||
if (session->opts.fd != SSH_INVALID_SOCKET) {
|
||||
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
|
||||
ssh_socket_set_fd(session->socket, session->opts.fd);
|
||||
ret = SSH_OK;
|
||||
ret = ssh_socket_set_fd(session->socket, session->opts.fd);
|
||||
#ifndef _WIN32
|
||||
#ifdef HAVE_PTHREAD
|
||||
} else if (ssh_libssh_proxy_jumps() &&
|
||||
@@ -836,6 +853,7 @@ error:
|
||||
session->opts.fd = SSH_INVALID_SOCKET;
|
||||
session->session_state = SSH_SESSION_STATE_DISCONNECTED;
|
||||
session->pending_call_state = SSH_PENDING_CALL_NONE;
|
||||
session->packet_state = PACKET_STATE_INIT;
|
||||
|
||||
while ((it = ssh_list_get_iterator(session->channels)) != NULL) {
|
||||
ssh_channel_do_free(ssh_iterator_value(ssh_channel, it));
|
||||
|
||||
72
src/config.c
72
src/config.c
@@ -153,6 +153,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{ "tunneldevice", SOC_NA},
|
||||
{ "xauthlocation", SOC_NA},
|
||||
{ "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES},
|
||||
{ "requiredrsasize", SOC_REQUIRED_RSA_SIZE},
|
||||
{ NULL, SOC_UNKNOWN }
|
||||
};
|
||||
|
||||
@@ -1439,6 +1440,12 @@ ssh_config_parse_line(ssh_session session,
|
||||
ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
|
||||
}
|
||||
break;
|
||||
case SOC_REQUIRED_RSA_SIZE:
|
||||
l = ssh_config_get_long(&s, -1);
|
||||
if (l >= 0 && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &l);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
|
||||
opcode);
|
||||
@@ -1451,6 +1458,32 @@ ssh_config_parse_line(ssh_session session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration from a file pointer
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] fp A valid file pointer
|
||||
* @params[in] global Whether the config is global or not
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
int ssh_config_parse(ssh_session session, FILE *fp, bool global)
|
||||
{
|
||||
char line[MAX_LINE_SIZE] = {0};
|
||||
unsigned int count = 0;
|
||||
int parsing, rv;
|
||||
|
||||
parsing = 1;
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
count++;
|
||||
rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration file and set the options to the given session
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
@@ -1460,36 +1493,32 @@ ssh_config_parse_line(ssh_session session,
|
||||
*/
|
||||
int ssh_config_parse_file(ssh_session session, const char *filename)
|
||||
{
|
||||
char line[MAX_LINE_SIZE] = {0};
|
||||
unsigned int count = 0;
|
||||
FILE *f = NULL;
|
||||
int parsing, rv;
|
||||
FILE *fp = NULL;
|
||||
int rv;
|
||||
bool global = 0;
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
|
||||
#ifdef USR_GLOBAL_CLIENT_CONFIG
|
||||
if (rv != 0) {
|
||||
rv = strcmp(filename, USR_GLOBAL_CLIENT_CONFIG);
|
||||
}
|
||||
#endif
|
||||
|
||||
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, 0, global);
|
||||
if (rv < 0) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
rv = ssh_config_parse(session, fp, global);
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
fclose(fp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration string and set the options to the given session
|
||||
@@ -1504,7 +1533,8 @@ int ssh_config_parse_string(ssh_session session, const char *input)
|
||||
{
|
||||
char line[MAX_LINE_SIZE] = {0};
|
||||
const char *c = input, *line_start = input;
|
||||
unsigned int line_num = 0, line_len;
|
||||
unsigned int line_num = 0;
|
||||
size_t line_len;
|
||||
int parsing, rv;
|
||||
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Reading configuration data from string:");
|
||||
@@ -1526,8 +1556,10 @@ int ssh_config_parse_string(ssh_session session, const char *input)
|
||||
}
|
||||
line_len = c - line_start;
|
||||
if (line_len > MAX_LINE_SIZE - 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Line %u too long: %u characters",
|
||||
line_num, line_len);
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Line %u too long: %zu characters",
|
||||
line_num,
|
||||
line_len);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(line, line_start, line_len);
|
||||
|
||||
@@ -209,7 +209,8 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
bind_itr != NULL;
|
||||
bind_itr = bind_itr->ai_next)
|
||||
{
|
||||
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
|
||||
rc = bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen);
|
||||
if (rc < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Binding local address: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
|
||||
@@ -166,7 +166,7 @@ int ssh_connector_set_out_channel(ssh_connector connector,
|
||||
|
||||
/* Fallback to default value for invalid flags */
|
||||
if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) {
|
||||
connector->in_flags = SSH_CONNECTOR_STDOUT;
|
||||
connector->out_flags = SSH_CONNECTOR_STDOUT;
|
||||
}
|
||||
|
||||
return ssh_add_channel_callbacks(channel, &connector->out_channel_cb);
|
||||
@@ -264,17 +264,17 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
|
||||
}
|
||||
connector->in_available = 1; /* Don't poll on it */
|
||||
return;
|
||||
} else if (r> 0) {
|
||||
} else if (r > 0) {
|
||||
/* loop around ssh_channel_write in case our window reduced due to a race */
|
||||
while (total != r){
|
||||
if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
|
||||
w = ssh_channel_write(connector->out_channel,
|
||||
buffer + total,
|
||||
r - total);
|
||||
(uint32_t)(r - total));
|
||||
} else {
|
||||
w = ssh_channel_write_stderr(connector->out_channel,
|
||||
buffer + total,
|
||||
r - total);
|
||||
(uint32_t)(r - total));
|
||||
}
|
||||
if (w == SSH_ERROR) {
|
||||
return;
|
||||
@@ -294,7 +294,7 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
|
||||
while (total < r) {
|
||||
w = ssh_connector_fd_write(connector,
|
||||
buffer + total,
|
||||
r - total);
|
||||
(uint32_t)(r - total));
|
||||
if (w < 0) {
|
||||
ssh_connector_except(connector, connector->out_fd);
|
||||
return;
|
||||
@@ -340,8 +340,9 @@ ssh_connector_fd_out_cb(ssh_connector connector)
|
||||
} 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);
|
||||
w = ssh_connector_fd_write(connector,
|
||||
buffer + total,
|
||||
(uint32_t)(r - total));
|
||||
if (w < 0) {
|
||||
ssh_connector_except(connector, connector->out_fd);
|
||||
return;
|
||||
@@ -381,15 +382,13 @@ ssh_connector_fd_out_cb(ssh_connector connector)
|
||||
*
|
||||
* @returns 0
|
||||
*/
|
||||
static int ssh_connector_fd_cb(ssh_poll_handle p,
|
||||
static int ssh_connector_fd_cb(UNUSED_PARAM(ssh_poll_handle p),
|
||||
socket_t fd,
|
||||
int revents,
|
||||
void *userdata)
|
||||
{
|
||||
ssh_connector connector = userdata;
|
||||
|
||||
(void)p;
|
||||
|
||||
if (revents & POLLERR) {
|
||||
ssh_connector_except(connector, fd);
|
||||
} else if((revents & (POLLIN|POLLHUP)) && fd == connector->in_fd) {
|
||||
@@ -408,6 +407,10 @@ static int ssh_connector_fd_cb(ssh_poll_handle p,
|
||||
*
|
||||
* @brief Callback called when data is received on channel.
|
||||
*
|
||||
* @param[in] session The SSH session
|
||||
*
|
||||
* @param[in] channel The channel data came from
|
||||
*
|
||||
* @param[in] data Pointer to the data
|
||||
*
|
||||
* @param[in] len Length of data
|
||||
@@ -419,7 +422,7 @@ static int ssh_connector_fd_cb(ssh_poll_handle p,
|
||||
* @returns Amount of data bytes consumed
|
||||
*/
|
||||
static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
ssh_channel channel,
|
||||
UNUSED_PARAM(ssh_channel channel),
|
||||
void *data,
|
||||
uint32_t len,
|
||||
int is_stderr,
|
||||
@@ -429,11 +432,11 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
int w;
|
||||
uint32_t window;
|
||||
|
||||
(void) session;
|
||||
(void) channel;
|
||||
(void) is_stderr;
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE,"connector data on channel");
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Received data (%" PRIu32 ") on channel (%" PRIu32 ":%" PRIu32 ")",
|
||||
len,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
|
||||
if (is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDERR)) {
|
||||
/* ignore stderr */
|
||||
@@ -447,6 +450,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
}
|
||||
|
||||
if (connector->out_wontblock) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Writing won't block");
|
||||
if (connector->out_channel != NULL) {
|
||||
uint32_t window_len;
|
||||
|
||||
@@ -476,9 +480,11 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
ssh_connector_except_channel(connector, connector->out_channel);
|
||||
}
|
||||
} else if (connector->out_fd != SSH_INVALID_SOCKET) {
|
||||
w = ssh_connector_fd_write(connector, data, len);
|
||||
if (w < 0)
|
||||
ssize_t ws = ssh_connector_fd_write(connector, data, len);
|
||||
if (ws < 0) {
|
||||
ssh_connector_except(connector, connector->out_fd);
|
||||
}
|
||||
w = (int)ws;
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL, "output socket or channel closed");
|
||||
return SSH_ERROR;
|
||||
@@ -493,6 +499,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
|
||||
return w;
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Writing would block: wait?");
|
||||
connector->in_available = 1;
|
||||
|
||||
return 0;
|
||||
@@ -510,10 +517,11 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
*
|
||||
* @returns Amount of data bytes consumed
|
||||
*/
|
||||
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
|
||||
ssh_channel channel,
|
||||
uint32_t bytes,
|
||||
void *userdata)
|
||||
static int
|
||||
ssh_connector_channel_write_wontblock_cb(ssh_session session,
|
||||
UNUSED_PARAM(ssh_channel channel),
|
||||
uint32_t bytes,
|
||||
void *userdata)
|
||||
{
|
||||
ssh_connector connector = userdata;
|
||||
uint8_t buffer[CHUNKSIZE];
|
||||
@@ -521,7 +529,12 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
|
||||
|
||||
(void) channel;
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Channel write won't block");
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Write won't block (%" PRIu32 ") on channel (%" PRIu32 ":%" PRIu32 ")",
|
||||
bytes,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
|
||||
if (connector->in_available) {
|
||||
if (connector->in_channel != NULL) {
|
||||
uint32_t len = MIN(CHUNKSIZE, bytes);
|
||||
@@ -532,7 +545,7 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
|
||||
0);
|
||||
if (r == SSH_ERROR) {
|
||||
ssh_connector_except_channel(connector, connector->in_channel);
|
||||
} else if(r == 0 && ssh_channel_is_eof(connector->in_channel)){
|
||||
} else if (r == 0 && ssh_channel_is_eof(connector->in_channel)) {
|
||||
ssh_channel_send_eof(connector->out_channel);
|
||||
} else if (r > 0) {
|
||||
w = ssh_channel_write(connector->out_channel, buffer, r);
|
||||
@@ -603,15 +616,15 @@ int ssh_connector_set_event(ssh_connector connector, ssh_event event)
|
||||
}
|
||||
}
|
||||
if (connector->in_channel != NULL) {
|
||||
rc = ssh_event_add_session(event,
|
||||
ssh_channel_get_session(connector->in_channel));
|
||||
ssh_session session = ssh_channel_get_session(connector->in_channel);
|
||||
rc = ssh_event_add_session(event, session);
|
||||
if (rc != SSH_OK)
|
||||
goto error;
|
||||
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0){
|
||||
connector->in_available = 1;
|
||||
}
|
||||
}
|
||||
if(connector->out_channel != NULL) {
|
||||
if (connector->out_channel != NULL) {
|
||||
ssh_session session = ssh_channel_get_session(connector->out_channel);
|
||||
|
||||
rc = ssh_event_add_session(event, session);
|
||||
|
||||
340
src/curve25519.c
340
src/curve25519.c
@@ -26,119 +26,43 @@
|
||||
#include "libssh/curve25519.h"
|
||||
#ifdef HAVE_CURVE25519
|
||||
|
||||
#ifdef WITH_NACL
|
||||
#include "nacl/crypto_scalarmult_curve25519.h"
|
||||
#endif
|
||||
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/bignum.h"
|
||||
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply);
|
||||
|
||||
static ssh_packet_callback dh_client_callbacks[] = {
|
||||
ssh_packet_client_curve25519_reply
|
||||
ssh_packet_client_curve25519_reply,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = {
|
||||
.start = SSH2_MSG_KEX_ECDH_REPLY,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = dh_client_callbacks,
|
||||
.user = NULL
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
static int ssh_curve25519_init(ssh_session session)
|
||||
int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k)
|
||||
{
|
||||
int rc;
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
|
||||
size_t pkey_len = CURVE25519_PRIVKEY_SIZE;
|
||||
|
||||
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
|
||||
if (pctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_log_hexdump("Session server cookie",
|
||||
session->next_crypto->server_kex.cookie,
|
||||
16);
|
||||
ssh_log_hexdump("Session client cookie",
|
||||
session->next_crypto->client_kex.cookie,
|
||||
16);
|
||||
#endif
|
||||
|
||||
rc = EVP_PKEY_keygen_init(pctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 keygen: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_keygen(pctx, &pkey);
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to generate X25519 keys: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
rc = EVP_PKEY_get_raw_public_key(pkey,
|
||||
session->next_crypto->curve25519_server_pubkey,
|
||||
&pubkey_len);
|
||||
} else {
|
||||
rc = EVP_PKEY_get_raw_public_key(pkey,
|
||||
session->next_crypto->curve25519_client_pubkey,
|
||||
&pubkey_len);
|
||||
}
|
||||
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get X25519 raw public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
EVP_PKEY_free(pkey);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_get_raw_private_key(pkey,
|
||||
session->next_crypto->curve25519_privkey,
|
||||
&pkey_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get X25519 raw private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
EVP_PKEY_free(pkey);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
#else
|
||||
rc = ssh_get_random(session->next_crypto->curve25519_privkey,
|
||||
CURVE25519_PRIVKEY_SIZE, 1);
|
||||
if (rc != 1) {
|
||||
ssh_set_error(session, SSH_FATAL, "PRNG error");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey,
|
||||
session->next_crypto->curve25519_privkey);
|
||||
} else {
|
||||
crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey,
|
||||
session->next_crypto->curve25519_privkey);
|
||||
}
|
||||
#endif /* HAVE_LIBCRYPTO */
|
||||
|
||||
return SSH_OK;
|
||||
rc = curve25519_do_create_k(session, k);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
@@ -180,193 +104,120 @@ void ssh_client_curve25519_remove_callbacks(ssh_session session)
|
||||
static int ssh_curve25519_build_k(ssh_session session)
|
||||
{
|
||||
ssh_curve25519_pubkey k;
|
||||
int rc;
|
||||
|
||||
#ifdef HAVE_LIBCRYPTO
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL, *pubkey = NULL;
|
||||
size_t shared_key_len = sizeof(k);
|
||||
int rc, ret = SSH_ERROR;
|
||||
|
||||
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL,
|
||||
session->next_crypto->curve25519_privkey,
|
||||
CURVE25519_PRIVKEY_SIZE);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create X25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
rc = ssh_curve25519_create_k(session, k);
|
||||
if (rc != SSH_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
pctx = EVP_PKEY_CTX_new(pkey, NULL);
|
||||
if (pctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_derive_init(pctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 key derivation: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL,
|
||||
session->next_crypto->curve25519_client_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
} else {
|
||||
pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL,
|
||||
session->next_crypto->curve25519_server_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
}
|
||||
if (pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create X25519 public key EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_derive_set_peer(pctx, pubkey);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to set peer X25519 public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_derive(pctx, k, &shared_key_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to derive X25519 shared secret: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
ret = SSH_OK;
|
||||
out:
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_PKEY_free(pubkey);
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
if (ret == SSH_ERROR) {
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
if (session->server) {
|
||||
crypto_scalarmult(k, session->next_crypto->curve25519_privkey,
|
||||
session->next_crypto->curve25519_client_pubkey);
|
||||
} else {
|
||||
crypto_scalarmult(k, session->next_crypto->curve25519_privkey,
|
||||
session->next_crypto->curve25519_server_pubkey);
|
||||
}
|
||||
#endif /* HAVE_LIBCRYPTO */
|
||||
|
||||
bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret);
|
||||
bignum_bin2bn(k,
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
&session->next_crypto->shared_secret);
|
||||
if (session->next_crypto->shared_secret == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_log_hexdump("Session server cookie",
|
||||
session->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
|
||||
|
||||
return 0;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
|
||||
* a SSH_MSG_NEWKEYS
|
||||
*/
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
|
||||
ssh_string q_s_string = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
ssh_string signature = NULL;
|
||||
int rc;
|
||||
(void)type;
|
||||
(void)user;
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply)
|
||||
{
|
||||
ssh_string q_s_string = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
ssh_string signature = NULL;
|
||||
int rc;
|
||||
(void)type;
|
||||
(void)user;
|
||||
|
||||
ssh_client_curve25519_remove_callbacks(session);
|
||||
ssh_client_curve25519_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");
|
||||
goto error;
|
||||
}
|
||||
pubkey_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (pubkey_blob == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No public key in packet");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to import next public key");
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to import next public key");
|
||||
goto error;
|
||||
}
|
||||
|
||||
q_s_string = ssh_buffer_get_ssh_string(packet);
|
||||
if (q_s_string == NULL) {
|
||||
ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet");
|
||||
goto error;
|
||||
}
|
||||
if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){
|
||||
ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d",
|
||||
(int)ssh_string_len(q_s_string));
|
||||
SSH_STRING_FREE(q_s_string);
|
||||
goto error;
|
||||
}
|
||||
memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE);
|
||||
SSH_STRING_FREE(q_s_string);
|
||||
q_s_string = ssh_buffer_get_ssh_string(packet);
|
||||
if (q_s_string == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No Q_S ECC point in packet");
|
||||
goto error;
|
||||
}
|
||||
if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Incorrect size for server Curve25519 public key: %zu",
|
||||
ssh_string_len(q_s_string));
|
||||
SSH_STRING_FREE(q_s_string);
|
||||
goto error;
|
||||
}
|
||||
memcpy(session->next_crypto->curve25519_server_pubkey,
|
||||
ssh_string_data(q_s_string),
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
SSH_STRING_FREE(q_s_string);
|
||||
|
||||
signature = ssh_buffer_get_ssh_string(packet);
|
||||
if (signature == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No signature in packet");
|
||||
goto error;
|
||||
}
|
||||
session->next_crypto->dh_server_signature = signature;
|
||||
signature=NULL; /* ownership changed */
|
||||
/* TODO: verify signature now instead of waiting for NEWKEYS */
|
||||
if (ssh_curve25519_build_k(session) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
|
||||
goto error;
|
||||
}
|
||||
signature = ssh_buffer_get_ssh_string(packet);
|
||||
if (signature == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No signature in packet");
|
||||
goto error;
|
||||
}
|
||||
session->next_crypto->dh_server_signature = signature;
|
||||
signature = NULL; /* ownership changed */
|
||||
/* TODO: verify signature now instead of waiting for NEWKEYS */
|
||||
if (ssh_curve25519_build_k(session) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Send the MSG_NEWKEYS */
|
||||
rc = ssh_packet_send_newkeys(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
|
||||
/* Send the MSG_NEWKEYS */
|
||||
rc = ssh_packet_send_newkeys(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
return SSH_PACKET_USED;
|
||||
|
||||
error:
|
||||
session->session_state=SSH_SESSION_STATE_ERROR;
|
||||
return SSH_PACKET_USED;
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init);
|
||||
|
||||
static ssh_packet_callback dh_server_callbacks[]= {
|
||||
ssh_packet_server_curve25519_init
|
||||
static ssh_packet_callback dh_server_callbacks[] = {
|
||||
ssh_packet_server_curve25519_init,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_curve25519_server_callbacks = {
|
||||
.start = SSH2_MSG_KEX_ECDH_INIT,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = dh_server_callbacks,
|
||||
.user = NULL
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
/** @internal
|
||||
* @brief sets up the curve25519-sha256@libssh.org kex callbacks
|
||||
*/
|
||||
void ssh_server_curve25519_init(ssh_session session){
|
||||
void ssh_server_curve25519_init(ssh_session session)
|
||||
{
|
||||
/* register the packet callbacks */
|
||||
ssh_packet_set_callbacks(session, &ssh_curve25519_server_callbacks);
|
||||
}
|
||||
@@ -374,7 +225,8 @@ void ssh_server_curve25519_init(ssh_session session){
|
||||
/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a
|
||||
* SSH_MSG_KEXDH_REPLY
|
||||
*/
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init)
|
||||
{
|
||||
/* ECDH keys */
|
||||
ssh_string q_c_string = NULL;
|
||||
ssh_string q_s_string = NULL;
|
||||
@@ -393,10 +245,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
|
||||
/* 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;
|
||||
}
|
||||
if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){
|
||||
if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Incorrect size for server Curve25519 public key: %zu",
|
||||
@@ -405,7 +257,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_data(q_c_string),
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
SSH_STRING_FREE(q_c_string);
|
||||
|
||||
/* Build server's key pair */
|
||||
@@ -447,8 +300,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
|
||||
}
|
||||
|
||||
/* add host's public key */
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer,
|
||||
server_pubkey_blob);
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_blob);
|
||||
SSH_STRING_FREE(server_pubkey_blob);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
@@ -509,7 +361,7 @@ error:
|
||||
SSH_STRING_FREE(q_c_string);
|
||||
SSH_STRING_FREE(q_s_string);
|
||||
ssh_buffer_reinit(session->out_buffer);
|
||||
session->session_state=SSH_SESSION_STATE_ERROR;
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
|
||||
164
src/curve25519_crypto.c
Normal file
164
src/curve25519_crypto.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* curve25519_crypto.c - Curve25519 ECDH functions for key exchange (OpenSSL)
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
|
||||
*
|
||||
* 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, version 2.1 of the License.
|
||||
*
|
||||
* 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/curve25519.h"
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
int ssh_curve25519_init(ssh_session session)
|
||||
{
|
||||
ssh_curve25519_pubkey *pubkey_loc = NULL;
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
|
||||
int rc;
|
||||
|
||||
if (session->server) {
|
||||
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
} else {
|
||||
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
}
|
||||
|
||||
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
|
||||
if (pctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_keygen_init(pctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 keygen: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_keygen(pctx, &pkey);
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to generate X25519 keys: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_get_raw_public_key(pkey, *pubkey_loc, &pubkey_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to get X25519 raw public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
EVP_PKEY_free(pkey);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Free any previously allocated privkey */
|
||||
if (session->next_crypto->curve25519_privkey != NULL) {
|
||||
EVP_PKEY_free(session->next_crypto->curve25519_privkey);
|
||||
session->next_crypto->curve25519_privkey = NULL;
|
||||
}
|
||||
|
||||
session->next_crypto->curve25519_privkey = pkey;
|
||||
pkey = NULL;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
|
||||
{
|
||||
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL, *pubkey = NULL;
|
||||
size_t shared_key_len = CURVE25519_PUBKEY_SIZE;
|
||||
|
||||
if (session->server) {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
} else {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
}
|
||||
|
||||
pkey = session->next_crypto->curve25519_privkey;
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create X25519 EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pctx = EVP_PKEY_CTX_new(pkey, NULL);
|
||||
if (pctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_derive_init(pctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to initialize X25519 key derivation: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519,
|
||||
NULL,
|
||||
*peer_pubkey_loc,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
if (pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create X25519 public key EVP_PKEY: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_derive_set_peer(pctx, pubkey);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to set peer X25519 public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_derive(pctx, k, &shared_key_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to derive X25519 shared secret: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto out;
|
||||
}
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
EVP_PKEY_free(pubkey);
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return ret;
|
||||
}
|
||||
73
src/curve25519_fallback.c
Normal file
73
src/curve25519_fallback.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* curve25519_fallback.c - Curve25519 ECDH functions for key exchange
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
|
||||
*
|
||||
* 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, version 2.1 of the License.
|
||||
*
|
||||
* 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/curve25519.h"
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#ifdef WITH_NACL
|
||||
#include "nacl/crypto_scalarmult_curve25519.h"
|
||||
#endif
|
||||
|
||||
int ssh_curve25519_init(ssh_session session)
|
||||
{
|
||||
ssh_curve25519_pubkey *pubkey_loc = NULL;
|
||||
int rc;
|
||||
|
||||
if (session->server) {
|
||||
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
} else {
|
||||
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
}
|
||||
|
||||
rc = ssh_get_random(session->next_crypto->curve25519_privkey,
|
||||
CURVE25519_PRIVKEY_SIZE,
|
||||
1);
|
||||
if (rc != 1) {
|
||||
ssh_set_error(session, SSH_FATAL, "PRNG error");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
crypto_scalarmult_base(*pubkey_loc,
|
||||
session->next_crypto->curve25519_privkey);
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
|
||||
{
|
||||
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
|
||||
|
||||
if (session->server) {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
} else {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
}
|
||||
|
||||
crypto_scalarmult(k,
|
||||
session->next_crypto->curve25519_privkey,
|
||||
*peer_pubkey_loc);
|
||||
return SSH_OK;
|
||||
}
|
||||
205
src/curve25519_gcrypt.c
Normal file
205
src/curve25519_gcrypt.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* curve25519_gcrypt.c - Curve25519 ECDH functions for key exchange (Gcrypt)
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
|
||||
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.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, version 2.1 of the License.
|
||||
*
|
||||
* 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/curve25519.h"
|
||||
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
int ssh_curve25519_init(ssh_session session)
|
||||
{
|
||||
ssh_curve25519_pubkey *pubkey_loc = NULL;
|
||||
gcry_error_t gcry_err;
|
||||
gcry_sexp_t param = NULL, keypair_sexp = NULL;
|
||||
ssh_string pubkey = NULL;
|
||||
const char *pubkey_data = NULL;
|
||||
int ret = SSH_ERROR;
|
||||
|
||||
if (session->server) {
|
||||
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
} else {
|
||||
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
}
|
||||
|
||||
gcry_err =
|
||||
gcry_sexp_build(¶m, NULL, "(genkey (ecdh (curve Curve25519)))");
|
||||
if (gcry_err != GPG_ERR_NO_ERROR) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create keypair sexp: %s",
|
||||
gcry_strerror(gcry_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
gcry_err = gcry_pk_genkey(&keypair_sexp, param);
|
||||
if (gcry_err != GPG_ERR_NO_ERROR) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to generate keypair: %s",
|
||||
gcry_strerror(gcry_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Extract the public key */
|
||||
pubkey = ssh_sexp_extract_mpi(keypair_sexp,
|
||||
"q",
|
||||
GCRYMPI_FMT_USG,
|
||||
GCRYMPI_FMT_STD);
|
||||
if (pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to extract public key: %s",
|
||||
gcry_strerror(gcry_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Store the public key in the session */
|
||||
/* The first byte should be 0x40 indicating that the point is compressed, so
|
||||
* we skip storing it */
|
||||
pubkey_data = (char *)ssh_string_data(pubkey);
|
||||
if (ssh_string_len(pubkey) != CURVE25519_PUBKEY_SIZE + 1 ||
|
||||
pubkey_data[0] != 0x40) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Invalid public key with length: %zu",
|
||||
ssh_string_len(pubkey));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE);
|
||||
|
||||
/* Free any previously allocated privkey */
|
||||
if (session->next_crypto->curve25519_privkey != NULL) {
|
||||
gcry_sexp_release(session->next_crypto->curve25519_privkey);
|
||||
session->next_crypto->curve25519_privkey = NULL;
|
||||
}
|
||||
|
||||
/* Store the private key */
|
||||
session->next_crypto->curve25519_privkey = keypair_sexp;
|
||||
keypair_sexp = NULL;
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
ssh_string_burn(pubkey);
|
||||
SSH_STRING_FREE(pubkey);
|
||||
gcry_sexp_release(param);
|
||||
gcry_sexp_release(keypair_sexp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
|
||||
{
|
||||
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
|
||||
gcry_error_t gcry_err;
|
||||
gcry_sexp_t pubkey_sexp = NULL, privkey_data_sexp = NULL,
|
||||
result_sexp = NULL;
|
||||
ssh_string shared_secret = NULL, privkey = NULL;
|
||||
char *shared_secret_data = NULL;
|
||||
int ret = SSH_ERROR;
|
||||
|
||||
if (session->server) {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
} else {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
}
|
||||
|
||||
gcry_err = gcry_sexp_build(
|
||||
&pubkey_sexp,
|
||||
NULL,
|
||||
"(key-data(public-key (ecdh (curve Curve25519) (q %b))))",
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
*peer_pubkey_loc);
|
||||
if (gcry_err != GPG_ERR_NO_ERROR) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create peer public key sexp: %s",
|
||||
gcry_strerror(gcry_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
privkey = ssh_sexp_extract_mpi(session->next_crypto->curve25519_privkey,
|
||||
"d",
|
||||
GCRYMPI_FMT_USG,
|
||||
GCRYMPI_FMT_STD);
|
||||
if (privkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to extract private key");
|
||||
goto out;
|
||||
}
|
||||
|
||||
gcry_err = gcry_sexp_build(&privkey_data_sexp,
|
||||
NULL,
|
||||
"(data(flags raw)(value %b))",
|
||||
ssh_string_len(privkey),
|
||||
ssh_string_data(privkey));
|
||||
if (gcry_err != GPG_ERR_NO_ERROR) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to create private key sexp: %s",
|
||||
gcry_strerror(gcry_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
gcry_err = gcry_pk_encrypt(&result_sexp, privkey_data_sexp, pubkey_sexp);
|
||||
if (gcry_err != GPG_ERR_NO_ERROR) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to compute shared secret: %s",
|
||||
gcry_strerror(gcry_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
shared_secret = ssh_sexp_extract_mpi(result_sexp,
|
||||
"s",
|
||||
GCRYMPI_FMT_USG,
|
||||
GCRYMPI_FMT_USG);
|
||||
if (shared_secret == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to extract shared secret");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Copy the shared secret to the output buffer */
|
||||
/* The first byte should be 0x40 indicating that it is a compressed point,
|
||||
* so we skip it */
|
||||
shared_secret_data = (char *)ssh_string_data(shared_secret);
|
||||
if (ssh_string_len(shared_secret) != CURVE25519_PUBKEY_SIZE + 1 ||
|
||||
shared_secret_data[0] != 0x40) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Invalid shared secret with length: %zu",
|
||||
ssh_string_len(shared_secret));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(k, shared_secret_data + 1, CURVE25519_PUBKEY_SIZE);
|
||||
|
||||
ret = SSH_OK;
|
||||
gcry_sexp_release(session->next_crypto->curve25519_privkey);
|
||||
session->next_crypto->curve25519_privkey = NULL;
|
||||
|
||||
out:
|
||||
ssh_string_burn(shared_secret);
|
||||
SSH_STRING_FREE(shared_secret);
|
||||
ssh_string_burn(privkey);
|
||||
SSH_STRING_FREE(privkey);
|
||||
gcry_sexp_release(privkey_data_sexp);
|
||||
gcry_sexp_release(pubkey_sexp);
|
||||
gcry_sexp_release(result_sexp);
|
||||
return ret;
|
||||
}
|
||||
189
src/curve25519_mbedcrypto.c
Normal file
189
src/curve25519_mbedcrypto.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* curve25519_mbedcrypto.c - Curve25519 ECDH functions for key exchange
|
||||
* (MbedTLS)
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2013-2023 by Aris Adamantiadis <aris@badcode.be>
|
||||
* Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.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, version 2.1 of the License.
|
||||
*
|
||||
* 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/curve25519.h"
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "mbedcrypto-compat.h"
|
||||
|
||||
#include <mbedtls/ecdh.h>
|
||||
#include <mbedtls/error.h>
|
||||
|
||||
int ssh_curve25519_init(ssh_session session)
|
||||
{
|
||||
ssh_curve25519_pubkey *pubkey_loc = NULL;
|
||||
mbedtls_ecdh_context ecdh_ctx;
|
||||
mbedtls_ecdh_params *ecdh_params = NULL;
|
||||
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
char error_buf[128];
|
||||
|
||||
if (session->server) {
|
||||
pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
} else {
|
||||
pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
}
|
||||
|
||||
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
|
||||
|
||||
mbedtls_ecdh_init(&ecdh_ctx);
|
||||
rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx);
|
||||
|
||||
rc = mbedtls_ecdh_gen_public(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp),
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Q),
|
||||
mbedtls_ctr_drbg_random,
|
||||
ctr_drbg);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to generate X25519 keypair: %s",
|
||||
error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
|
||||
session->next_crypto->curve25519_privkey,
|
||||
CURVE25519_PRIVKEY_SIZE);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to write X25519 private key: %s",
|
||||
error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mbedtls_mpi_write_binary_le(
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Q).MBEDTLS_ECDH_PRIVATE(X),
|
||||
*pubkey_loc,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to write X25519 public key: %s",
|
||||
error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
mbedtls_ecdh_free(&ecdh_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k)
|
||||
{
|
||||
ssh_curve25519_pubkey *peer_pubkey_loc = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
mbedtls_ecdh_context ecdh_ctx;
|
||||
mbedtls_ecdh_params *ecdh_params = NULL;
|
||||
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
|
||||
char error_buf[128];
|
||||
|
||||
if (session->server) {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey;
|
||||
} else {
|
||||
peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey;
|
||||
}
|
||||
|
||||
ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
|
||||
|
||||
mbedtls_ecdh_init(&ecdh_ctx);
|
||||
rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx);
|
||||
|
||||
rc = mbedtls_mpi_read_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
|
||||
session->next_crypto->curve25519_privkey,
|
||||
CURVE25519_PRIVKEY_SIZE);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to read private key: %s", error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mbedtls_mpi_read_binary_le(
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(X),
|
||||
*peer_pubkey_loc,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to read peer public key: %s", error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mbedtls_mpi_lset(
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(Z),
|
||||
1);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to set Z coordinate: %s", error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mbedtls_ecdh_compute_shared(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp),
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(z),
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp),
|
||||
&ecdh_params->MBEDTLS_ECDH_PRIVATE(d),
|
||||
mbedtls_ctr_drbg_random,
|
||||
ctr_drbg);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to compute shared secret: %s",
|
||||
error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(z),
|
||||
k,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
if (rc != 0) {
|
||||
mbedtls_strerror(rc, error_buf, sizeof(error_buf));
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to write shared secret: %s", error_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
mbedtls_ecdh_free(&ecdh_ctx);
|
||||
return ret;
|
||||
}
|
||||
@@ -416,6 +416,9 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
|
||||
do {
|
||||
firstbyte = getc(moduli);
|
||||
} while(firstbyte != '\n' && firstbyte != EOF);
|
||||
if (firstbyte == EOF) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (firstbyte == EOF) {
|
||||
@@ -439,6 +442,9 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
|
||||
do {
|
||||
firstbyte = getc(moduli);
|
||||
} while(firstbyte != '\n' && firstbyte != EOF);
|
||||
if (firstbyte == EOF) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -407,6 +407,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
|
||||
struct dh_ctx *ctx = NULL;
|
||||
int rc;
|
||||
|
||||
/* Cleanup any previously allocated dh_ctx */
|
||||
if (crypto->dh_ctx != NULL) {
|
||||
ssh_dh_cleanup(crypto);
|
||||
}
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return SSH_ERROR;
|
||||
@@ -592,7 +597,7 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
|
||||
}
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
|
||||
*dest = BN_bin2bn(kstring, klen, NULL);
|
||||
*dest = BN_bin2bn(kstring, (int)klen, NULL);
|
||||
if (*dest == NULL) {
|
||||
rc = SSH_ERROR;
|
||||
goto done;
|
||||
|
||||
@@ -237,6 +237,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
|
||||
struct dh_ctx *ctx = NULL;
|
||||
int rc;
|
||||
|
||||
/* Cleanup any previously allocated dh_ctx */
|
||||
if (crypto->dh_ctx != NULL) {
|
||||
ssh_dh_cleanup(crypto);
|
||||
}
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return SSH_ERROR;
|
||||
|
||||
@@ -191,6 +191,17 @@ static ssh_string ssh_ecdh_generate(ssh_session session)
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free any previously allocated privkey */
|
||||
if (session->next_crypto->ecdh_privkey != NULL) {
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
EC_KEY_free(session->next_crypto->ecdh_privkey);
|
||||
#else
|
||||
EVP_PKEY_free(session->next_crypto->ecdh_privkey);
|
||||
#endif
|
||||
session->next_crypto->ecdh_privkey = NULL;
|
||||
}
|
||||
|
||||
session->next_crypto->ecdh_privkey = key;
|
||||
return pubkey_string;
|
||||
}
|
||||
@@ -219,6 +230,7 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ssh_string_free(session->next_crypto->ecdh_client_pubkey);
|
||||
session->next_crypto->ecdh_client_pubkey = client_pubkey;
|
||||
|
||||
/* register the packet callbacks */
|
||||
|
||||
@@ -101,8 +101,15 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Free any previously allocated privkey */
|
||||
if (session->next_crypto->ecdh_privkey != NULL) {
|
||||
gcry_sexp_release(session->next_crypto->ecdh_privkey);
|
||||
session->next_crypto->ecdh_privkey = NULL;
|
||||
}
|
||||
session->next_crypto->ecdh_privkey = key;
|
||||
key = NULL;
|
||||
|
||||
SSH_STRING_FREE(session->next_crypto->ecdh_client_pubkey);
|
||||
session->next_crypto->ecdh_client_pubkey = client_pubkey;
|
||||
client_pubkey = NULL;
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Free any previously allocated privkey */
|
||||
if (session->next_crypto->ecdh_privkey != NULL) {
|
||||
mbedtls_ecp_keypair_free(session->next_crypto->ecdh_privkey);
|
||||
SAFE_FREE(session->next_crypto->ecdh_privkey);
|
||||
}
|
||||
|
||||
session->next_crypto->ecdh_privkey = malloc(sizeof(mbedtls_ecp_keypair));
|
||||
if (session->next_crypto->ecdh_privkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
@@ -110,6 +116,7 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
goto out;
|
||||
}
|
||||
|
||||
SSH_STRING_FREE(session->next_crypto->ecdh_client_pubkey);
|
||||
session->next_crypto->ecdh_client_pubkey = client_pubkey;
|
||||
client_pubkey = NULL;
|
||||
|
||||
|
||||
1060
src/external/sntrup761.c
vendored
Normal file
1060
src/external/sntrup761.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,7 @@ static int ssh_gets(const char *prompt, char *buf, size_t len, int verify)
|
||||
fprintf(stdout, "%s", prompt);
|
||||
}
|
||||
fflush(stdout);
|
||||
if (fgets(tmp, len, stdin) == NULL) {
|
||||
if (fgets(tmp, (int)len, stdin) == NULL) {
|
||||
free(tmp);
|
||||
return 0;
|
||||
}
|
||||
@@ -87,7 +87,7 @@ static int ssh_gets(const char *prompt, char *buf, size_t len, int verify)
|
||||
|
||||
fprintf(stdout, "\nVerifying, please re-enter. %s", prompt);
|
||||
fflush(stdout);
|
||||
if (! fgets(key_string, len, stdin)) {
|
||||
if (!fgets(key_string, (int)len, stdin)) {
|
||||
explicit_bzero(key_string, len);
|
||||
SAFE_FREE(key_string);
|
||||
clearerr(stdin);
|
||||
|
||||
208
src/kex.c
208
src/kex.c
@@ -40,6 +40,10 @@
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/string.h"
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/sntrup761.h"
|
||||
#ifdef HAVE_MLKEM
|
||||
#include "libssh/mlkem768.h"
|
||||
#endif
|
||||
#include "libssh/knownhosts.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
@@ -95,6 +99,18 @@
|
||||
#define CURVE25519 ""
|
||||
#endif /* HAVE_CURVE25519 */
|
||||
|
||||
#ifdef HAVE_SNTRUP761
|
||||
#define SNTRUP761X25519 "sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,"
|
||||
#else
|
||||
#define SNTRUP761X25519 ""
|
||||
#endif /* HAVE_SNTRUP761 */
|
||||
|
||||
#ifdef HAVE_MLKEM
|
||||
#define MLKEM768X25519 "mlkem768x25519-sha256,"
|
||||
#else
|
||||
#define MLKEM768X25519 ""
|
||||
#endif /* HAVE_MLKEM */
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
|
||||
#define EC_HOSTKEYS "ecdsa-sha2-nistp521," \
|
||||
@@ -158,6 +174,8 @@
|
||||
#define CHACHA20 "chacha20-poly1305@openssh.com,"
|
||||
|
||||
#define DEFAULT_KEY_EXCHANGE \
|
||||
MLKEM768X25519 \
|
||||
SNTRUP761X25519 \
|
||||
CURVE25519 \
|
||||
ECDH \
|
||||
"diffie-hellman-group18-sha512,diffie-hellman-group16-sha512," \
|
||||
@@ -267,38 +285,58 @@ static const char *ssh_kex_descriptions[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *ssh_kex_get_default_methods(uint32_t algo)
|
||||
const char *ssh_kex_get_default_methods(enum ssh_kex_types_e type)
|
||||
{
|
||||
if (algo >= SSH_KEX_METHODS) {
|
||||
if (type >= SSH_KEX_METHODS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return default_methods[algo];
|
||||
return default_methods[type];
|
||||
}
|
||||
|
||||
const char *ssh_kex_get_supported_method(uint32_t algo)
|
||||
const char *ssh_kex_get_supported_method(enum ssh_kex_types_e type)
|
||||
{
|
||||
if (algo >= SSH_KEX_METHODS) {
|
||||
if (type >= SSH_KEX_METHODS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return supported_methods[algo];
|
||||
return supported_methods[type];
|
||||
}
|
||||
|
||||
const char *ssh_kex_get_description(uint32_t algo) {
|
||||
if (algo >= SSH_KEX_METHODS) {
|
||||
return NULL;
|
||||
}
|
||||
const char *ssh_kex_get_description(enum ssh_kex_types_e type)
|
||||
{
|
||||
if (type >= SSH_KEX_METHODS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ssh_kex_descriptions[algo];
|
||||
return ssh_kex_descriptions[type];
|
||||
}
|
||||
|
||||
const char *ssh_kex_get_fips_methods(uint32_t algo) {
|
||||
if (algo >= SSH_KEX_METHODS) {
|
||||
return NULL;
|
||||
}
|
||||
const char *ssh_kex_get_fips_methods(enum ssh_kex_types_e type)
|
||||
{
|
||||
if (type >= SSH_KEX_METHODS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fips_methods[algo];
|
||||
return fips_methods[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a list of supported algorithms of a given type. This respects the
|
||||
* FIPS mode status.
|
||||
*
|
||||
* @param[in] type The type of the algorithm to query (SSH_KEX, SSH_MAC_C_S,
|
||||
* ...).
|
||||
*
|
||||
* @return The list of supported methods as comma-separated string, or NULL for
|
||||
* unknown type.
|
||||
*/
|
||||
const char *ssh_get_supported_methods(enum ssh_kex_types_e type)
|
||||
{
|
||||
if (ssh_fips_mode()) {
|
||||
return ssh_kex_get_fips_methods(type);
|
||||
} else {
|
||||
return ssh_kex_get_supported_method(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -894,6 +932,14 @@ kex_select_kex_type(const char *kex)
|
||||
return SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
|
||||
} else if (strcmp(kex, "curve25519-sha256") == 0) {
|
||||
return SSH_KEX_CURVE25519_SHA256;
|
||||
} else if (strcmp(kex, "sntrup761x25519-sha512@openssh.com") == 0) {
|
||||
return SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM;
|
||||
} else if (strcmp(kex, "sntrup761x25519-sha512") == 0) {
|
||||
return SSH_KEX_SNTRUP761X25519_SHA512;
|
||||
#ifdef HAVE_MLKEM
|
||||
} else if (strcmp(kex, "mlkem768x25519-sha256") == 0) {
|
||||
return SSH_KEX_MLKEM768X25519_SHA256;
|
||||
#endif
|
||||
}
|
||||
/* should not happen. We should be getting only valid names at this stage */
|
||||
return 0;
|
||||
@@ -933,6 +979,17 @@ static void revert_kex_callbacks(ssh_session session)
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
ssh_client_curve25519_remove_callbacks(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_SNTRUP761
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
ssh_client_sntrup761x25519_remove_callbacks(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
ssh_client_mlkem768x25519_remove_callbacks(session);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1340,6 +1397,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
|
||||
buf = ssh_buffer_new();
|
||||
if (buf == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1348,6 +1406,9 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->clientbanner,
|
||||
session->serverbanner);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to pack client and server banner");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -1361,6 +1422,9 @@ int ssh_make_sessionid(ssh_session session)
|
||||
|
||||
rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to get next server pubkey blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -1375,6 +1439,9 @@ int ssh_make_sessionid(ssh_session session)
|
||||
server_pubkey_blob);
|
||||
SSH_STRING_FREE(server_pubkey_blob);
|
||||
if (rc != SSH_OK){
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to pack hashes and pubkey blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -1399,6 +1466,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
client_pubkey,
|
||||
server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to pack DH pubkeys");
|
||||
goto error;
|
||||
}
|
||||
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
@@ -1434,6 +1502,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
client_pubkey,
|
||||
server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to pack DH GEX params");
|
||||
goto error;
|
||||
}
|
||||
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
@@ -1456,6 +1525,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->ecdh_client_pubkey,
|
||||
session->next_crypto->ecdh_server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to pack ECDH pubkeys");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
@@ -1473,13 +1543,87 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->curve25519_server_pubkey);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to pack Curve25519 pubkeys");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_CURVE25519 */
|
||||
#ifdef HAVE_SNTRUP761
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPPdPP",
|
||||
SNTRUP761_PUBLICKEY_SIZE + CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)SNTRUP761_PUBLICKEY_SIZE,
|
||||
session->next_crypto->sntrup761_client_pubkey,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE,
|
||||
session->next_crypto->curve25519_client_pubkey,
|
||||
SNTRUP761_CIPHERTEXT_SIZE + CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)SNTRUP761_CIPHERTEXT_SIZE,
|
||||
session->next_crypto->sntrup761_ciphertext,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE,
|
||||
session->next_crypto->curve25519_server_pubkey);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to pack SNTRU Prime params");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_SNTRUP761 */
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPPdPP",
|
||||
MLKEM768X25519_CLIENT_PUBKEY_SIZE,
|
||||
(size_t)MLKEM768_PUBLICKEY_SIZE,
|
||||
session->next_crypto->mlkem768_client_pubkey,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE,
|
||||
session->next_crypto->curve25519_client_pubkey,
|
||||
MLKEM768X25519_SERVER_RESPONSE_SIZE,
|
||||
(size_t)MLKEM768_CIPHERTEXT_SIZE,
|
||||
session->next_crypto->mlkem768_ciphertext,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE,
|
||||
session->next_crypto->curve25519_server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to pack ML-KEM768 individual components");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_MLKEM */
|
||||
default:
|
||||
/* Handle unsupported kex types - this should not happen in normal operation */
|
||||
rc = SSH_ERROR;
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported KEX algorithm");
|
||||
goto error;
|
||||
}
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"F",
|
||||
session->next_crypto->shared_secret,
|
||||
SHA512_DIGEST_LEN);
|
||||
break;
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"F",
|
||||
session->next_crypto->shared_secret,
|
||||
SHA256_DIGEST_LEN);
|
||||
break;
|
||||
#endif /* HAVE_MLKEM */
|
||||
default:
|
||||
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
|
||||
break;
|
||||
}
|
||||
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to pack shared secret");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -1487,6 +1631,8 @@ int ssh_make_sessionid(ssh_session session)
|
||||
ssh_log_hexdump("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf));
|
||||
#endif
|
||||
|
||||
/* Set rc for the following switch statement in case we goto error. */
|
||||
rc = SSH_ERROR;
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
@@ -1507,6 +1653,9 @@ int ssh_make_sessionid(ssh_session session)
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
#endif
|
||||
#ifdef WITH_GEX
|
||||
case SSH_KEX_DH_GEX_SHA256:
|
||||
#endif /* WITH_GEX */
|
||||
@@ -1534,6 +1683,8 @@ int ssh_make_sessionid(ssh_session session)
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
session->next_crypto->digest_len = SHA512_DIGEST_LENGTH;
|
||||
session->next_crypto->digest_type = SSH_KDF_SHA512;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
@@ -1545,7 +1696,13 @@ int ssh_make_sessionid(ssh_session session)
|
||||
ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
default:
|
||||
/* Handle unsupported kex types - this should not happen in normal operation */
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported KEX algorithm for hash computation");
|
||||
rc = SSH_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* During the first kex, secret hash and session ID are equal. However, after
|
||||
* a key re-exchange, a new secret hash is calculated. This hash will not replace
|
||||
* but complement existing session id.
|
||||
@@ -1554,6 +1711,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->session_id = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->session_id == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
rc = SSH_ERROR;
|
||||
goto error;
|
||||
}
|
||||
memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash,
|
||||
@@ -1670,7 +1828,19 @@ int ssh_generate_session_keys(ssh_session session)
|
||||
size_t intkey_srv_to_cli_len = 0;
|
||||
int rc = -1;
|
||||
|
||||
k_string = ssh_make_bignum_string(crypto->shared_secret);
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
#endif /* HAVE_MLKEM */
|
||||
k_string = ssh_make_padded_bignum_string(crypto->shared_secret,
|
||||
crypto->digest_len);
|
||||
break;
|
||||
default:
|
||||
k_string = ssh_make_bignum_string(crypto->shared_secret);
|
||||
break;
|
||||
}
|
||||
if (k_string == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
|
||||
@@ -171,7 +171,7 @@ static int known_hosts_read_line(FILE *fp,
|
||||
size_t *buf_len,
|
||||
size_t *lineno)
|
||||
{
|
||||
while (fgets(buf, buf_size, fp) != NULL) {
|
||||
while (fgets(buf, (int)buf_size, fp) != NULL) {
|
||||
size_t len;
|
||||
if (buf[0] == '\0') {
|
||||
continue;
|
||||
|
||||
@@ -49,8 +49,9 @@
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/hmac.h>
|
||||
#else
|
||||
#include <openssl/param_build.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/param_build.h>
|
||||
#include <openssl/provider.h>
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
#include <openssl/rand.h>
|
||||
#if defined(WITH_PKCS11_URI) && !defined(WITH_PKCS11_PROVIDER)
|
||||
@@ -96,7 +97,37 @@ void ssh_reseed(void){
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(WITH_PKCS11_URI) && !defined(WITH_PKCS11_PROVIDER)
|
||||
#if defined(WITH_PKCS11_URI)
|
||||
#if defined(WITH_PKCS11_PROVIDER)
|
||||
static OSSL_PROVIDER *provider = NULL;
|
||||
static bool pkcs11_provider_failed = false;
|
||||
|
||||
int pki_load_pkcs11_provider(void)
|
||||
{
|
||||
if (OSSL_PROVIDER_available(NULL, "pkcs11") == 1) {
|
||||
/* the provider is already available.
|
||||
* Loaded through a configuration file? */
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (pkcs11_provider_failed) {
|
||||
/* the loading failed previously -- do not retry */
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
provider = OSSL_PROVIDER_try_load(NULL, "pkcs11", 1);
|
||||
if (provider != NULL) {
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to load the pkcs11 provider: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
/* Do not attempt to load it again */
|
||||
pkcs11_provider_failed = true;
|
||||
return SSH_ERROR;
|
||||
}
|
||||
#else
|
||||
static ENGINE *engine = NULL;
|
||||
|
||||
ENGINE *pki_get_engine(void)
|
||||
@@ -128,7 +159,8 @@ ENGINE *pki_get_engine(void)
|
||||
}
|
||||
return engine;
|
||||
}
|
||||
#endif /* defined(WITH_PKCS11_URI) && !defined(WITH_PKCS11_PROVIDER) */
|
||||
#endif /* defined(WITH_PKCS11_PROVIDER) */
|
||||
#endif /* defined(WITH_PKCS11_URI) */
|
||||
|
||||
#ifdef HAVE_OPENSSL_EVP_KDF_CTX
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
@@ -317,7 +349,7 @@ HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, len);
|
||||
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, (int)len);
|
||||
if (pkey == NULL) {
|
||||
goto error;
|
||||
}
|
||||
@@ -374,7 +406,7 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher)
|
||||
if (cipher->ctx == NULL) {
|
||||
cipher->ctx = EVP_CIPHER_CTX_new();
|
||||
} else {
|
||||
EVP_CIPHER_CTX_init(cipher->ctx);
|
||||
EVP_CIPHER_CTX_reset(cipher->ctx);
|
||||
}
|
||||
|
||||
switch(cipher->ciphertype){
|
||||
@@ -603,7 +635,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
|
||||
(unsigned char *)out + aadlen,
|
||||
&tmplen,
|
||||
(unsigned char *)in + aadlen,
|
||||
(int)len - aadlen);
|
||||
(int)(len - aadlen));
|
||||
outlen = tmplen;
|
||||
if (rc != 1 || outlen != (int)len - aadlen) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
|
||||
@@ -621,7 +653,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
|
||||
|
||||
rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
|
||||
EVP_CTRL_GCM_GET_TAG,
|
||||
authlen,
|
||||
(int)authlen,
|
||||
(unsigned char *)tag);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_GET_TAG failed");
|
||||
@@ -659,7 +691,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
|
||||
/* set tag for authentication */
|
||||
rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
|
||||
EVP_CTRL_GCM_SET_TAG,
|
||||
authlen,
|
||||
(int)authlen,
|
||||
(unsigned char *)complete_packet + aadlen + encrypted_size);
|
||||
if (rc == 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_TAG failed");
|
||||
@@ -684,7 +716,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
|
||||
(unsigned char *)out,
|
||||
&outlen,
|
||||
(unsigned char *)complete_packet + aadlen,
|
||||
encrypted_size /* already subtracted aadlen */);
|
||||
(int)encrypted_size /* already subtracted aadlen */);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed");
|
||||
return SSH_ERROR;
|
||||
@@ -966,7 +998,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rv = EVP_CipherUpdate(ctx->header_evp, out, &outlen, in, len);
|
||||
rv = EVP_CipherUpdate(ctx->header_evp, out, &outlen, in, (int)len);
|
||||
if (rv != 1 || outlen != sizeof(uint32_t)) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
|
||||
return SSH_ERROR;
|
||||
@@ -1053,9 +1085,11 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
|
||||
}
|
||||
|
||||
/* Decrypt the message */
|
||||
rv = EVP_CipherUpdate(ctx->main_evp, out, &len,
|
||||
rv = EVP_CipherUpdate(ctx->main_evp,
|
||||
out,
|
||||
&len,
|
||||
(uint8_t *)complete_packet + sizeof(uint32_t),
|
||||
encrypted_size);
|
||||
(int)encrypted_size);
|
||||
if (rv != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
|
||||
goto out;
|
||||
@@ -1122,7 +1156,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
|
||||
out_packet->payload,
|
||||
&outlen,
|
||||
in_packet->payload,
|
||||
len - sizeof(uint32_t));
|
||||
(int)(len - sizeof(uint32_t)));
|
||||
if (ret != 1) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
|
||||
return;
|
||||
@@ -1402,6 +1436,14 @@ void ssh_crypto_finalize(void)
|
||||
engine = NULL;
|
||||
}
|
||||
#endif
|
||||
#if defined(WITH_PKCS11_URI)
|
||||
#if defined(WITH_PKCS11_PROVIDER)
|
||||
if (provider != NULL) {
|
||||
OSSL_PROVIDER_unload(provider);
|
||||
provider = NULL;
|
||||
}
|
||||
#endif /* WITH_PKCS11_PROVIDER */
|
||||
#endif /* WITH_PKCS11_URI */
|
||||
|
||||
libcrypto_initialized = 0;
|
||||
}
|
||||
@@ -1546,6 +1588,12 @@ int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote)
|
||||
{
|
||||
return evp_dup_pkey("EC", key, demote, new_key);
|
||||
}
|
||||
|
||||
int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote)
|
||||
{
|
||||
return evp_dup_pkey("ED25519", key, demote, new_key);
|
||||
}
|
||||
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
|
||||
ssh_string
|
||||
|
||||
@@ -966,7 +966,8 @@ int ssh_crypto_init(void)
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
|
||||
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P, 0)) {
|
||||
gcry_control(GCRYCTL_INIT_SECMEM, 4096);
|
||||
gcry_control(GCRYCTL_USE_SECURE_RNDPOOL);
|
||||
gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0);
|
||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -481,3 +481,17 @@ LIBSSH_4_10_0 # Released
|
||||
ssh_pki_export_privkey_file_format;
|
||||
ssh_request_no_more_sessions;
|
||||
} LIBSSH_4_9_0;
|
||||
|
||||
LIBSSH_AFTER_4_10_0
|
||||
{
|
||||
global:
|
||||
sftp_get_users_groups_by_id;
|
||||
sftp_name_id_map_free;
|
||||
sftp_name_id_map_new;
|
||||
ssh_get_supported_methods;
|
||||
sshsig_sign;
|
||||
sshsig_verify;
|
||||
ssh_string_cmp;
|
||||
ssh_string_from_data;
|
||||
} LIBSSH_4_10_0;
|
||||
|
||||
|
||||
10
src/log.c
10
src/log.c
@@ -59,22 +59,22 @@ static int current_timestring(int hires, char *buf, size_t len)
|
||||
{
|
||||
char tbuf[64];
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
struct tm tm, *tm_ptr = NULL;
|
||||
time_t t;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
t = (time_t) tv.tv_sec;
|
||||
|
||||
tm = localtime(&t);
|
||||
if (tm == NULL) {
|
||||
tm_ptr = localtime_r(&t, &tm);
|
||||
if (tm_ptr == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hires) {
|
||||
strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm);
|
||||
strftime(tbuf, sizeof(tbuf), "%Y/%m/%d %H:%M:%S", &tm);
|
||||
snprintf(buf, len, "%s.%06ld", tbuf, (long)tv.tv_usec);
|
||||
} else {
|
||||
strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm);
|
||||
strftime(tbuf, sizeof(tbuf), "%Y/%m/%d %H:%M:%S", &tm);
|
||||
snprintf(buf, len, "%s", tbuf);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,8 +205,10 @@ int match_pattern_list(const char *string, const char *pattern,
|
||||
* Returns -1 if negation matches, 1 if there is a positive match, 0 if there
|
||||
* is no match at all.
|
||||
*/
|
||||
int match_hostname(const char *host, const char *pattern, unsigned int len) {
|
||||
return match_pattern_list(host, pattern, len, 1);
|
||||
int
|
||||
match_hostname(const char *host, const char *pattern, size_t len)
|
||||
{
|
||||
return match_pattern_list(host, pattern, len, 1);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
@@ -5,13 +5,17 @@
|
||||
* 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
|
||||
#include <mbedtls/version.h>
|
||||
|
||||
#ifndef MBEDTLS_VERSION_MAJOR
|
||||
#include <mbedtls/build_info.h>
|
||||
#endif /* MBEDTLS_VERSION_MAJOR */
|
||||
|
||||
#if MBEDTLS_VERSION_MAJOR < 3
|
||||
|
||||
static inline size_t mbedtls_cipher_info_get_key_bitlen(
|
||||
const mbedtls_cipher_info_t *info)
|
||||
static inline size_t
|
||||
mbedtls_cipher_info_get_key_bitlen(const mbedtls_cipher_info_t *info)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return 0;
|
||||
@@ -19,8 +23,8 @@ static inline size_t mbedtls_cipher_info_get_key_bitlen(
|
||||
return info->key_bitlen;
|
||||
}
|
||||
|
||||
static inline size_t mbedtls_cipher_info_get_iv_size(
|
||||
const mbedtls_cipher_info_t *info)
|
||||
static inline size_t
|
||||
mbedtls_cipher_info_get_iv_size(const mbedtls_cipher_info_t *info)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return 0;
|
||||
@@ -29,11 +33,24 @@ static inline size_t mbedtls_cipher_info_get_iv_size(
|
||||
}
|
||||
|
||||
#define MBEDTLS_PRIVATE(X) X
|
||||
|
||||
#ifdef HAVE_MBEDTLS_CURVE25519
|
||||
#include <mbedtls/ecdh.h>
|
||||
|
||||
#define MBEDTLS_ECDH_PRIVATE(X) X
|
||||
#define MBEDTLS_ECDH_PARAMS(X) X
|
||||
typedef mbedtls_ecdh_context mbedtls_ecdh_params;
|
||||
#endif /* HAVE_MBEDTLS_CURVE25519 */
|
||||
|
||||
#else /* MBEDTLS_VERSION_MAJOR < 3 */
|
||||
|
||||
#ifdef HAVE_MBEDTLS_CURVE25519
|
||||
#include <mbedtls/ecdh.h>
|
||||
|
||||
#define MBEDTLS_ECDH_PRIVATE(X) MBEDTLS_PRIVATE(X)
|
||||
#define MBEDTLS_ECDH_PARAMS(X) X.MBEDTLS_PRIVATE(ctx).MBEDTLS_PRIVATE(mbed_ecdh)
|
||||
typedef mbedtls_ecdh_context_mbed mbedtls_ecdh_params;
|
||||
#endif /* HAVE_MBEDTLS_CURVE25519 */
|
||||
|
||||
#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 */
|
||||
|
||||
653
src/messages.c
653
src/messages.c
@@ -204,8 +204,35 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
|
||||
ssh_message_reply_default(msg);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
#define CB channel_open_request_direct_tcpip_function
|
||||
} else if (msg->channel_request_open.type == SSH_CHANNEL_DIRECT_TCPIP &&
|
||||
ssh_callbacks_exists(session->server_callbacks, CB)) {
|
||||
struct ssh_channel_request_open *rq = &msg->channel_request_open;
|
||||
channel = session->server_callbacks->CB(session,
|
||||
rq->destination,
|
||||
rq->destination_port,
|
||||
rq->originator,
|
||||
rq->originator_port,
|
||||
session->server_callbacks->userdata);
|
||||
#undef CB
|
||||
if (channel != NULL) {
|
||||
rc = ssh_message_channel_request_open_reply_accept_channel(
|
||||
msg,
|
||||
channel);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to send reply for accepting a channel "
|
||||
"open");
|
||||
}
|
||||
return SSH_OK;
|
||||
} else {
|
||||
ssh_message_reply_default(msg);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
break;
|
||||
case SSH_REQUEST_CHANNEL:
|
||||
channel = msg->channel_request.channel;
|
||||
@@ -519,30 +546,36 @@ static void ssh_message_queue(ssh_session session, ssh_message message)
|
||||
*
|
||||
* @returns The head message or NULL if it doesn't exist.
|
||||
*/
|
||||
ssh_message ssh_message_pop_head(ssh_session session){
|
||||
ssh_message msg=NULL;
|
||||
struct ssh_iterator *i = NULL;
|
||||
if(session->ssh_message_list == NULL)
|
||||
return NULL;
|
||||
i=ssh_list_get_iterator(session->ssh_message_list);
|
||||
if(i != NULL){
|
||||
msg=ssh_iterator_value(ssh_message,i);
|
||||
ssh_list_remove(session->ssh_message_list,i);
|
||||
}
|
||||
return msg;
|
||||
ssh_message ssh_message_pop_head(ssh_session session)
|
||||
{
|
||||
ssh_message msg = NULL;
|
||||
struct ssh_iterator *i = NULL;
|
||||
|
||||
if (session->ssh_message_list == NULL)
|
||||
return NULL;
|
||||
|
||||
i = ssh_list_get_iterator(session->ssh_message_list);
|
||||
if (i != NULL) {
|
||||
msg = ssh_iterator_value(ssh_message, i);
|
||||
ssh_list_remove(session->ssh_message_list, i);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* Returns 1 if there is a message available */
|
||||
static int ssh_message_termination(void *s){
|
||||
ssh_session session = s;
|
||||
struct ssh_iterator *it = NULL;
|
||||
if(session->session_state == SSH_SESSION_STATE_ERROR)
|
||||
return 1;
|
||||
it = ssh_list_get_iterator(session->ssh_message_list);
|
||||
if(!it)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
static int ssh_message_termination(void *s)
|
||||
{
|
||||
ssh_session session = s;
|
||||
struct ssh_iterator *it = NULL;
|
||||
|
||||
if (session->session_state == SSH_SESSION_STATE_ERROR)
|
||||
return 1;
|
||||
|
||||
it = ssh_list_get_iterator(session->ssh_message_list);
|
||||
if (!it)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* @brief Retrieve a SSH message from a SSH session.
|
||||
@@ -642,6 +675,7 @@ void ssh_message_free(ssh_message msg){
|
||||
SAFE_FREE(msg->auth_request.password);
|
||||
}
|
||||
ssh_key_free(msg->auth_request.pubkey);
|
||||
ssh_key_free(msg->auth_request.server_pubkey);
|
||||
break;
|
||||
case SSH_REQUEST_CHANNEL_OPEN:
|
||||
SAFE_FREE(msg->channel_request_open.originator);
|
||||
@@ -733,11 +767,12 @@ error:
|
||||
static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
ssh_message msg,
|
||||
const char *service,
|
||||
ssh_string algo)
|
||||
ssh_string algo,
|
||||
const char *method)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = NULL;
|
||||
ssh_buffer buffer = NULL;
|
||||
ssh_string str=NULL;
|
||||
ssh_string str = NULL;
|
||||
int rc;
|
||||
|
||||
crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
|
||||
@@ -763,10 +798,10 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
SSH2_MSG_USERAUTH_REQUEST, /* type */
|
||||
msg->auth_request.username,
|
||||
service,
|
||||
"publickey", /* method */
|
||||
method,
|
||||
1, /* has to be signed (true) */
|
||||
ssh_string_get_char(algo), /* pubkey algorithm */
|
||||
str); /* public key as a blob */
|
||||
str); /* public key as a blob */
|
||||
|
||||
SSH_STRING_FREE(str);
|
||||
if (rc != SSH_OK) {
|
||||
@@ -775,6 +810,25 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add server public key for hostbound extension */
|
||||
if (strcmp(method, "publickey-hostbound-v00@openssh.com") == 0 &&
|
||||
msg->auth_request.server_pubkey != NULL) {
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(msg->auth_request.server_pubkey, &str);
|
||||
if (rc < 0) {
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(buffer, str);
|
||||
SSH_STRING_FREE(str);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -784,263 +838,334 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
* @brief Handle a SSH_MSG_MSG_USERAUTH_REQUEST packet and queue a
|
||||
* SSH Message
|
||||
*/
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
|
||||
ssh_message msg = NULL;
|
||||
ssh_signature sig = NULL;
|
||||
char *service = NULL;
|
||||
char *method = NULL;
|
||||
int cmp;
|
||||
int rc;
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_request)
|
||||
{
|
||||
ssh_message msg = NULL;
|
||||
ssh_signature sig = NULL;
|
||||
char *service = NULL;
|
||||
char *method = NULL;
|
||||
int cmp;
|
||||
int rc;
|
||||
|
||||
(void)user;
|
||||
(void)type;
|
||||
(void)user;
|
||||
(void)type;
|
||||
|
||||
msg = ssh_message_new(session);
|
||||
if (msg == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
msg->type = SSH_REQUEST_AUTH;
|
||||
rc = ssh_buffer_unpack(packet,
|
||||
"sss",
|
||||
&msg->auth_request.username,
|
||||
&service,
|
||||
&method);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Auth request for service %s, method %s for user '%s'",
|
||||
service, method,
|
||||
msg->auth_request.username);
|
||||
|
||||
cmp = strcmp(service, "ssh-connection");
|
||||
if (cmp != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Invalid service request: %s",
|
||||
service);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "none") == 0) {
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_NONE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "password") == 0) {
|
||||
uint8_t tmp;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_PASSWORD;
|
||||
rc = ssh_buffer_unpack(packet, "bs", &tmp, &msg->auth_request.password);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "keyboard-interactive") == 0) {
|
||||
ssh_string lang = NULL;
|
||||
ssh_string submethods = NULL;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE;
|
||||
lang = ssh_buffer_get_ssh_string(packet);
|
||||
if (lang == NULL) {
|
||||
goto error;
|
||||
}
|
||||
/* from the RFC 4256
|
||||
* 3.1. Initial Exchange
|
||||
* "The language tag is deprecated and SHOULD be the empty string."
|
||||
*/
|
||||
SSH_STRING_FREE(lang);
|
||||
|
||||
submethods = ssh_buffer_get_ssh_string(packet);
|
||||
if (submethods == NULL) {
|
||||
goto error;
|
||||
}
|
||||
/* from the RFC 4256
|
||||
* 3.1. Initial Exchange
|
||||
* "One possible implementation strategy of the submethods field on the
|
||||
* server is that, unless the user may use multiple different
|
||||
* submethods, the server ignores this field."
|
||||
*/
|
||||
SSH_STRING_FREE(submethods);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "publickey") == 0) {
|
||||
ssh_string algo = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
uint8_t has_sign;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_PUBLICKEY;
|
||||
SAFE_FREE(method);
|
||||
rc = ssh_buffer_unpack(packet, "bSS",
|
||||
&has_sign,
|
||||
&algo,
|
||||
&pubkey_blob
|
||||
);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey);
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
pubkey_blob = NULL;
|
||||
if (rc < 0) {
|
||||
SSH_STRING_FREE(algo);
|
||||
algo = NULL;
|
||||
msg = ssh_message_new(session);
|
||||
if (msg == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
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;
|
||||
msg->type = SSH_REQUEST_AUTH;
|
||||
rc = ssh_buffer_unpack(packet,
|
||||
"sss",
|
||||
&msg->auth_request.username,
|
||||
&service,
|
||||
&method);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// has a valid signature ?
|
||||
if(has_sign) {
|
||||
ssh_string sig_blob = NULL;
|
||||
ssh_buffer digest = NULL;
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Auth request for service %s, method %s for user '%s'",
|
||||
service,
|
||||
method,
|
||||
msg->auth_request.username);
|
||||
|
||||
sig_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if(sig_blob == NULL) {
|
||||
SSH_LOG(SSH_LOG_PACKET, "Invalid signature packet from peer");
|
||||
cmp = strcmp(service, "ssh-connection");
|
||||
if (cmp != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid service request: %s", service);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "none") == 0) {
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_NONE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "password") == 0) {
|
||||
uint8_t tmp;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_PASSWORD;
|
||||
rc = ssh_buffer_unpack(packet, "bs", &tmp, &msg->auth_request.password);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "keyboard-interactive") == 0) {
|
||||
ssh_string lang = NULL;
|
||||
ssh_string submethods = NULL;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE;
|
||||
lang = ssh_buffer_get_ssh_string(packet);
|
||||
if (lang == NULL) {
|
||||
goto error;
|
||||
}
|
||||
/* from the RFC 4256
|
||||
* 3.1. Initial Exchange
|
||||
* "The language tag is deprecated and SHOULD be the empty string."
|
||||
*/
|
||||
SSH_STRING_FREE(lang);
|
||||
|
||||
submethods = ssh_buffer_get_ssh_string(packet);
|
||||
if (submethods == NULL) {
|
||||
goto error;
|
||||
}
|
||||
/* from the RFC 4256
|
||||
* 3.1. Initial Exchange
|
||||
* "One possible implementation strategy of the submethods field on the
|
||||
* server is that, unless the user may use multiple different
|
||||
* submethods, the server ignores this field."
|
||||
*/
|
||||
SSH_STRING_FREE(submethods);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "publickey") == 0 ||
|
||||
strcmp(method, "publickey-hostbound-v00@openssh.com") == 0) {
|
||||
ssh_string algo = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
ssh_string server_pubkey_blob = NULL;
|
||||
uint8_t has_sign;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_PUBLICKEY;
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "bSS", &has_sign, &algo, &pubkey_blob);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
cmp = strcmp(method, "publickey-hostbound-v00@openssh.com");
|
||||
if (cmp == 0) {
|
||||
server_pubkey_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (server_pubkey_blob == NULL) {
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
SSH_STRING_FREE(algo);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_pubkey_blob(server_pubkey_blob,
|
||||
&msg->auth_request.server_pubkey);
|
||||
SSH_STRING_FREE(server_pubkey_blob);
|
||||
|
||||
if (rc < 0) {
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
SSH_STRING_FREE(algo);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey);
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
pubkey_blob = NULL;
|
||||
if (rc < 0) {
|
||||
SSH_STRING_FREE(algo);
|
||||
algo = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
digest = ssh_msg_userauth_build_digest(session, msg, service, algo);
|
||||
SSH_STRING_FREE(algo);
|
||||
algo = NULL;
|
||||
if (digest == NULL) {
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
SSH_LOG(SSH_LOG_PACKET, "Failed to get digest");
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG;
|
||||
goto error;
|
||||
}
|
||||
// has a valid signature ?
|
||||
if (has_sign) {
|
||||
ssh_string sig_blob = NULL;
|
||||
ssh_buffer digest = NULL;
|
||||
|
||||
rc = ssh_pki_import_signature_blob(sig_blob,
|
||||
msg->auth_request.pubkey,
|
||||
&sig);
|
||||
if (rc == SSH_OK) {
|
||||
/* Check if the signature from client matches server preferences */
|
||||
if (session->opts.pubkey_accepted_types) {
|
||||
cmp = match_group(session->opts.pubkey_accepted_types,
|
||||
sig->type_c);
|
||||
if (cmp != 1) {
|
||||
ssh_set_error(session,
|
||||
sig_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (sig_blob == NULL) {
|
||||
SSH_LOG(SSH_LOG_PACKET, "Invalid signature packet from peer");
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR;
|
||||
SSH_STRING_FREE(algo);
|
||||
algo = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
digest = ssh_msg_userauth_build_digest(session,
|
||||
msg,
|
||||
service,
|
||||
algo,
|
||||
method);
|
||||
SSH_STRING_FREE(algo);
|
||||
algo = NULL;
|
||||
if (digest == NULL) {
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
SSH_LOG(SSH_LOG_PACKET, "Failed to get digest");
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_signature_blob(sig_blob,
|
||||
msg->auth_request.pubkey,
|
||||
&sig);
|
||||
if (rc == SSH_OK) {
|
||||
/* Check if the signature from client matches server preferences
|
||||
*/
|
||||
if (session->opts.pubkey_accepted_types) {
|
||||
cmp = match_group(session->opts.pubkey_accepted_types,
|
||||
sig->type_c);
|
||||
if (cmp != 1) {
|
||||
ssh_set_error(
|
||||
session,
|
||||
SSH_FATAL,
|
||||
"Public key from client (%s) doesn't match server "
|
||||
"preference (%s)",
|
||||
sig->type_c,
|
||||
session->opts.pubkey_accepted_types);
|
||||
rc = SSH_ERROR;
|
||||
rc = SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == SSH_OK) {
|
||||
rc = ssh_pki_signature_verify(session,
|
||||
sig,
|
||||
msg->auth_request.pubkey,
|
||||
ssh_buffer_get(digest),
|
||||
ssh_buffer_get_len(digest));
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == SSH_OK) {
|
||||
rc = ssh_pki_signature_verify(session,
|
||||
sig,
|
||||
msg->auth_request.pubkey,
|
||||
ssh_buffer_get(digest),
|
||||
ssh_buffer_get_len(digest));
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
SSH_BUFFER_FREE(digest);
|
||||
ssh_signature_free(sig);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Received an invalid signature from peer");
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Valid signature received");
|
||||
|
||||
cmp = strcmp(method, "publickey-hostbound-v00@openssh.com");
|
||||
if (cmp == 0) {
|
||||
ssh_key server_key = NULL;
|
||||
|
||||
if (msg->auth_request.server_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Server public key not provided by client");
|
||||
msg->auth_request.signature_state =
|
||||
SSH_PUBLICKEY_STATE_WRONG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_get_server_publickey(session, &server_key);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Failed to get server public key for hostbound "
|
||||
"verification");
|
||||
msg->auth_request.signature_state =
|
||||
SSH_PUBLICKEY_STATE_ERROR;
|
||||
ssh_key_free(server_key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ssh_key_cmp(server_key,
|
||||
msg->auth_request.server_pubkey,
|
||||
SSH_KEY_CMP_PUBLIC) != 0) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Server public key doesn't match the one provided "
|
||||
"by client");
|
||||
msg->auth_request.signature_state =
|
||||
SSH_PUBLICKEY_STATE_WRONG;
|
||||
ssh_key_free(server_key);
|
||||
goto error;
|
||||
}
|
||||
ssh_key_free(server_key);
|
||||
}
|
||||
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID;
|
||||
}
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
SSH_BUFFER_FREE(digest);
|
||||
ssh_signature_free(sig);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(
|
||||
SSH_LOG_PACKET,
|
||||
"Received an invalid signature from peer");
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG;
|
||||
|
||||
SAFE_FREE(method);
|
||||
SSH_STRING_FREE(algo);
|
||||
goto end;
|
||||
}
|
||||
#ifdef WITH_GSSAPI
|
||||
if (strcmp(method, "gssapi-with-mic") == 0) {
|
||||
uint32_t n_oid;
|
||||
ssh_string *oids = NULL;
|
||||
ssh_string oid = NULL;
|
||||
char *hexa = NULL;
|
||||
int i;
|
||||
ssh_buffer_get_u32(packet, &n_oid);
|
||||
n_oid = ntohl(n_oid);
|
||||
if (n_oid > 100) {
|
||||
ssh_set_error(
|
||||
session,
|
||||
SSH_FATAL,
|
||||
"USERAUTH_REQUEST: gssapi-with-mic OID count too big (%d)",
|
||||
n_oid);
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_PACKET, "gssapi: %d OIDs", n_oid);
|
||||
oids = calloc(n_oid, sizeof(ssh_string));
|
||||
if (oids == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; i < (int)n_oid; ++i) {
|
||||
oid = ssh_buffer_get_ssh_string(packet);
|
||||
if (oid == NULL) {
|
||||
for (i = i - 1; i >= 0; --i) {
|
||||
SAFE_FREE(oids[i]);
|
||||
}
|
||||
SAFE_FREE(oids);
|
||||
ssh_set_error(session,
|
||||
SSH_LOG_PACKET,
|
||||
"USERAUTH_REQUEST: gssapi-with-mic missing OID");
|
||||
goto error;
|
||||
}
|
||||
oids[i] = oid;
|
||||
if (session->common.log_verbosity >= SSH_LOG_PACKET) {
|
||||
hexa = ssh_get_hexa(ssh_string_data(oid), ssh_string_len(oid));
|
||||
SSH_LOG(SSH_LOG_PACKET, "gssapi: OID %d: %s", i, hexa);
|
||||
SAFE_FREE(hexa);
|
||||
}
|
||||
}
|
||||
ssh_gssapi_handle_userauth(session,
|
||||
msg->auth_request.username,
|
||||
n_oid,
|
||||
oids);
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Valid signature received");
|
||||
for (i = 0; i < (int)n_oid; ++i) {
|
||||
SAFE_FREE(oids[i]);
|
||||
}
|
||||
SAFE_FREE(oids);
|
||||
/* bypass the message queue thing */
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
SSH_MESSAGE_FREE(msg);
|
||||
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID;
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
SSH_STRING_FREE(algo);
|
||||
goto end;
|
||||
}
|
||||
#ifdef WITH_GSSAPI
|
||||
if (strcmp(method, "gssapi-with-mic") == 0) {
|
||||
uint32_t n_oid;
|
||||
ssh_string *oids = NULL;
|
||||
ssh_string oid = NULL;
|
||||
char *hexa = NULL;
|
||||
int i;
|
||||
ssh_buffer_get_u32(packet, &n_oid);
|
||||
n_oid=ntohl(n_oid);
|
||||
if(n_oid > 100){
|
||||
ssh_set_error(session, SSH_FATAL, "USERAUTH_REQUEST: gssapi-with-mic OID count too big (%d)",n_oid);
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_PACKET, "gssapi: %d OIDs", n_oid);
|
||||
oids = calloc(n_oid, sizeof(ssh_string));
|
||||
if (oids == NULL){
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
for (i=0;i<(int) n_oid;++i){
|
||||
oid=ssh_buffer_get_ssh_string(packet);
|
||||
if(oid == NULL){
|
||||
for(i=i-1;i>=0;--i){
|
||||
SAFE_FREE(oids[i]);
|
||||
}
|
||||
SAFE_FREE(oids);
|
||||
ssh_set_error(session, SSH_LOG_PACKET, "USERAUTH_REQUEST: gssapi-with-mic missing OID");
|
||||
goto error;
|
||||
}
|
||||
oids[i] = oid;
|
||||
if(session->common.log_verbosity >= SSH_LOG_PACKET){
|
||||
hexa = ssh_get_hexa(ssh_string_data(oid), ssh_string_len(oid));
|
||||
SSH_LOG(SSH_LOG_PACKET,"gssapi: OID %d: %s",i, hexa);
|
||||
SAFE_FREE(hexa);
|
||||
}
|
||||
}
|
||||
ssh_gssapi_handle_userauth(session, msg->auth_request.username, n_oid, oids);
|
||||
|
||||
for(i=0;i<(int)n_oid;++i){
|
||||
SAFE_FREE(oids[i]);
|
||||
}
|
||||
SAFE_FREE(oids);
|
||||
/* bypass the message queue thing */
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
SSH_MESSAGE_FREE(msg);
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
#endif
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_UNKNOWN;
|
||||
SAFE_FREE(method);
|
||||
goto end;
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_UNKNOWN;
|
||||
SAFE_FREE(method);
|
||||
goto end;
|
||||
error:
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
|
||||
SSH_MESSAGE_FREE(msg);
|
||||
SSH_MESSAGE_FREE(msg);
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
return SSH_PACKET_USED;
|
||||
end:
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
|
||||
ssh_message_queue(session,msg);
|
||||
ssh_message_queue(session, msg);
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
@@ -1301,7 +1426,9 @@ end:
|
||||
*
|
||||
* @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) {
|
||||
int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg,
|
||||
ssh_channel chan)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
int rc;
|
||||
|
||||
@@ -1352,25 +1479,25 @@ int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_c
|
||||
*
|
||||
* @returns NULL in case of error
|
||||
*/
|
||||
ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) {
|
||||
ssh_channel chan = NULL;
|
||||
int rc;
|
||||
ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg)
|
||||
{
|
||||
ssh_channel chan = NULL;
|
||||
int rc;
|
||||
|
||||
if (msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chan = ssh_channel_new(msg->session);
|
||||
if (chan == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
rc = ssh_message_channel_request_open_reply_accept_channel(msg, chan);
|
||||
if (rc < 0) {
|
||||
ssh_channel_free(chan);
|
||||
chan = NULL;
|
||||
}
|
||||
return chan;
|
||||
if (msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chan = ssh_channel_new(msg->session);
|
||||
if (chan == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
rc = ssh_message_channel_request_open_reply_accept_channel(msg, chan);
|
||||
if (rc < 0) {
|
||||
ssh_channel_free(chan);
|
||||
chan = NULL;
|
||||
}
|
||||
return chan;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
132
src/misc.c
132
src/misc.c
@@ -178,6 +178,35 @@ int ssh_gettimeofday(struct timeval *__p, void *__t)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Convert time in seconds since the Epoch to broken-down local time
|
||||
*
|
||||
* This is a helper used to provide localtime_r() like function interface
|
||||
* on Windows.
|
||||
*
|
||||
* @param timer Pointer to a location storing the time_t which
|
||||
* represents the time in seconds since the Epoch.
|
||||
*
|
||||
* @param result Pointer to a location where the broken-down time
|
||||
* (expressed as local time) should be stored.
|
||||
*
|
||||
* @returns A pointer to the structure pointed to by the parameter
|
||||
* <tt>result</tt> on success, NULL on error with the errno
|
||||
* set to indicate the error.
|
||||
*/
|
||||
struct tm *ssh_localtime(const time_t *timer, struct tm *result)
|
||||
{
|
||||
errno_t rc;
|
||||
rc = localtime_s(result, timer);
|
||||
if (rc != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char *ssh_get_local_username(void)
|
||||
{
|
||||
DWORD size = 0;
|
||||
@@ -401,22 +430,22 @@ int ssh_is_ipaddr(const char *str)
|
||||
|
||||
char *ssh_lowercase(const char* str)
|
||||
{
|
||||
char *new = NULL, *p = NULL;
|
||||
char *new = NULL, *p = NULL;
|
||||
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new = strdup(str);
|
||||
if (new == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
new = strdup(str);
|
||||
if (new == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (p = new; *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
for (p = new; *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
|
||||
return new;
|
||||
return new;
|
||||
}
|
||||
|
||||
char *ssh_hostport(const char *host, int port)
|
||||
@@ -836,32 +865,32 @@ int ssh_list_prepend(struct ssh_list *list, const void *data)
|
||||
|
||||
void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator)
|
||||
{
|
||||
struct ssh_iterator *ptr = NULL, *prev = NULL;
|
||||
struct ssh_iterator *ptr = NULL, *prev = NULL;
|
||||
|
||||
if (list == NULL) {
|
||||
return;
|
||||
}
|
||||
if (list == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
prev=NULL;
|
||||
ptr=list->root;
|
||||
while(ptr && ptr != iterator){
|
||||
prev=ptr;
|
||||
ptr=ptr->next;
|
||||
}
|
||||
if(!ptr){
|
||||
/* we did not find the element */
|
||||
return;
|
||||
}
|
||||
/* unlink it */
|
||||
if(prev)
|
||||
prev->next=ptr->next;
|
||||
/* if iterator was the head */
|
||||
if(list->root == iterator)
|
||||
list->root=iterator->next;
|
||||
/* if iterator was the tail */
|
||||
if(list->end == iterator)
|
||||
list->end = prev;
|
||||
SAFE_FREE(iterator);
|
||||
prev = NULL;
|
||||
ptr = list->root;
|
||||
while (ptr && ptr != iterator) {
|
||||
prev = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (!ptr) {
|
||||
/* we did not find the element */
|
||||
return;
|
||||
}
|
||||
/* unlink it */
|
||||
if (prev)
|
||||
prev->next = ptr->next;
|
||||
/* if iterator was the head */
|
||||
if (list->root == iterator)
|
||||
list->root = iterator->next;
|
||||
/* if iterator was the tail */
|
||||
if (list->end == iterator)
|
||||
list->end = prev;
|
||||
SAFE_FREE(iterator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1347,6 +1376,7 @@ int ssh_analyze_banner(ssh_session session, int server)
|
||||
{
|
||||
const char *banner = NULL;
|
||||
const char *openssh = NULL;
|
||||
const char *ios = NULL;
|
||||
|
||||
if (server) {
|
||||
banner = session->clientbanner;
|
||||
@@ -1436,6 +1466,11 @@ int ssh_analyze_banner(ssh_session session, int server)
|
||||
major, minor, session->openssh);
|
||||
}
|
||||
}
|
||||
/* Cisco devices have odd scp implementation which breaks */
|
||||
ios = strstr(banner, "Cisco");
|
||||
if (ios != NULL) {
|
||||
session->flags |= SSH_SESSION_FLAG_SCP_QUOTING_BROKEN;
|
||||
}
|
||||
|
||||
done:
|
||||
return 0;
|
||||
@@ -1590,6 +1625,27 @@ void explicit_bzero(void *s, size_t n)
|
||||
}
|
||||
#endif /* !HAVE_EXPLICIT_BZERO */
|
||||
|
||||
/**
|
||||
* @brief Securely free memory by overwriting it before deallocation
|
||||
*
|
||||
* Overwrites the memory region with zeros before calling free() to prevent
|
||||
* sensitive data from remaining in memory after deallocation.
|
||||
*
|
||||
* @param[in] ptr Pointer to the memory region to securely free.
|
||||
* Can be NULL (no operation performed).
|
||||
* @param[in] len Length of the memory region in bytes.
|
||||
*
|
||||
*/
|
||||
void burn_free(void *ptr, size_t len)
|
||||
{
|
||||
if (ptr == NULL || len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
explicit_bzero(ptr, len);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_STRNDUP)
|
||||
char *strndup(const char *s, size_t n)
|
||||
{
|
||||
@@ -1802,7 +1858,7 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len)
|
||||
/* Put the string terminator */
|
||||
*dst = '\0';
|
||||
|
||||
return dst - buf;
|
||||
return (int)(dst - buf);
|
||||
|
||||
error:
|
||||
return SSH_ERROR;
|
||||
@@ -1848,7 +1904,7 @@ int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
|
||||
}
|
||||
*out = '\0';
|
||||
|
||||
return out - buf;
|
||||
return (int)(out - buf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
679
src/mlkem768.c
Normal file
679
src/mlkem768.c
Normal file
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
* mlkem768x25519.c - ML-KEM768x25519 hybrid key exchange
|
||||
* mlkem768x25519-sha256
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2025 by Red Hat, Inc.
|
||||
*
|
||||
* Author: Sahana Prasad <sahana@redhat.com>
|
||||
* Author: Claude (Anthropic)
|
||||
*
|
||||
* 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, version 2.1 of the License.
|
||||
*
|
||||
* 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/bignum.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/mlkem768.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_mlkem768x25519_reply);
|
||||
|
||||
static ssh_packet_callback dh_client_callbacks[] = {
|
||||
ssh_packet_client_mlkem768x25519_reply,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_mlkem768x25519_client_callbacks =
|
||||
{
|
||||
.start = SSH2_MSG_KEX_HYBRID_REPLY,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = dh_client_callbacks,
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
/* Generate ML-KEM768 keypair using OpenSSL */
|
||||
static int mlkem768_keypair_gen(ssh_mlkem768_pubkey pubkey,
|
||||
ssh_mlkem768_privkey privkey)
|
||||
{
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
size_t pubkey_len = MLKEM768_PUBLICKEY_SIZE;
|
||||
size_t privkey_len = MLKEM768_SECRETKEY_SIZE;
|
||||
|
||||
ctx = EVP_PKEY_CTX_new_from_name(NULL, "ML-KEM-768", NULL);
|
||||
if (ctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to create ML-KEM-768 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_keygen_init(ctx);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to initialize ML-KEM-768 keygen: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_keygen(ctx, &pkey);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to perform ML-KEM-768 keygen: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_get_raw_public_key(pkey, pubkey, &pubkey_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to extract ML-KEM-768 public key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_get_raw_private_key(pkey, privkey, &privkey_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to extract ML-KEM-768 private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Encapsulate shared secret using ML-KEM768 - used by server side */
|
||||
static int mlkem768_encapsulate(const ssh_mlkem768_pubkey pubkey,
|
||||
ssh_mlkem768_ciphertext ciphertext,
|
||||
unsigned char *shared_secret)
|
||||
{
|
||||
EVP_PKEY *pkey = NULL;
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
size_t ct_len = MLKEM768_CIPHERTEXT_SIZE;
|
||||
size_t ss_len = MLKEM768_SHARED_SECRET_SIZE;
|
||||
|
||||
pkey = EVP_PKEY_new_raw_public_key_ex(NULL,
|
||||
"ML-KEM-768",
|
||||
NULL,
|
||||
pubkey,
|
||||
MLKEM768_PUBLICKEY_SIZE);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to create ML-KEM-768 public key from raw data: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
|
||||
if (ctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to create ML-KEM-768 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_encapsulate_init(ctx, NULL);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to initialize ML-KEM-768 encapsulation: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_encapsulate(ctx, ciphertext, &ct_len, shared_secret, &ss_len);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to perform ML-KEM-768 encapsulation: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Decapsulate shared secret using ML-KEM768 - used by client side */
|
||||
static int mlkem768_decapsulate(const ssh_mlkem768_privkey privkey,
|
||||
const ssh_mlkem768_ciphertext ciphertext,
|
||||
unsigned char *shared_secret)
|
||||
{
|
||||
EVP_PKEY *pkey = NULL;
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
size_t ss_len = MLKEM768_SHARED_SECRET_SIZE;
|
||||
|
||||
pkey = EVP_PKEY_new_raw_private_key_ex(NULL,
|
||||
"ML-KEM-768",
|
||||
NULL,
|
||||
privkey,
|
||||
MLKEM768_SECRETKEY_SIZE);
|
||||
if (pkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to create ML-KEM-768 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
|
||||
if (ctx == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to create ML-KEM-768 context: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_decapsulate_init(ctx, NULL);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to initialize ML-KEM-768 decapsulation: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = EVP_PKEY_decapsulate(ctx,
|
||||
shared_secret,
|
||||
&ss_len,
|
||||
ciphertext,
|
||||
MLKEM768_CIPHERTEXT_SIZE);
|
||||
if (rc != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to perform ML-KEM-768 decapsulation: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssh_client_mlkem768x25519_init(ssh_session session)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_buffer client_pubkey = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
int rc;
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Initializing ML-KEM768x25519 key exchange");
|
||||
|
||||
/* Initialize Curve25519 component first */
|
||||
rc = ssh_curve25519_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Generate ML-KEM768 keypair */
|
||||
rc = mlkem768_keypair_gen(crypto->mlkem768_client_pubkey,
|
||||
crypto->mlkem768_client_privkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to generate ML-KEM768 keypair");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Create hybrid client public key: ML-KEM768 + Curve25519 */
|
||||
client_pubkey = ssh_buffer_new();
|
||||
if (client_pubkey == NULL) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
rc = SSH_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(client_pubkey,
|
||||
"PP",
|
||||
MLKEM768_PUBLICKEY_SIZE,
|
||||
crypto->mlkem768_client_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
crypto->curve25519_client_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
rc = SSH_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Convert to string for sending */
|
||||
pubkey_blob = ssh_string_new(ssh_buffer_get_len(client_pubkey));
|
||||
if (pubkey_blob == NULL) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
rc = SSH_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
ssh_string_fill(pubkey_blob,
|
||||
ssh_buffer_get(client_pubkey),
|
||||
ssh_buffer_get_len(client_pubkey));
|
||||
|
||||
/* Send the hybrid public key to server */
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bS",
|
||||
SSH2_MSG_KEX_HYBRID_INIT,
|
||||
pubkey_blob);
|
||||
if (rc != SSH_OK) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
rc = SSH_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
session->dh_handshake_state = DH_STATE_INIT_SENT;
|
||||
|
||||
ssh_packet_set_callbacks(session, &ssh_mlkem768x25519_client_callbacks);
|
||||
rc = ssh_packet_send(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to send SSH_MSG_KEX_ECDH_INIT");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ssh_buffer_free(client_pubkey);
|
||||
ssh_string_free(pubkey_blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_mlkem768x25519_reply)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_string s_server_blob = NULL;
|
||||
ssh_string s_pubkey_blob = NULL;
|
||||
ssh_string s_signature = NULL;
|
||||
const unsigned char *server_data = NULL;
|
||||
unsigned char mlkem_shared_secret[MLKEM768_SHARED_SECRET_SIZE];
|
||||
unsigned char curve25519_shared_secret[CURVE25519_PUBKEY_SIZE];
|
||||
unsigned char combined_secret[MLKEM768X25519_SHARED_SECRET_SIZE];
|
||||
unsigned char hashed_secret[SHA256_DIGEST_LEN];
|
||||
size_t server_blob_len;
|
||||
int rc;
|
||||
(void)type;
|
||||
(void)user;
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Received ML-KEM768x25519 server reply");
|
||||
|
||||
ssh_client_mlkem768x25519_remove_callbacks(session);
|
||||
|
||||
s_pubkey_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (s_pubkey_blob == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No public key in packet");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_dh_import_next_pubkey_blob(session, s_pubkey_blob);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to import next public key");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get server blob containing ML-KEM768 ciphertext + Curve25519 pubkey */
|
||||
s_server_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (s_server_blob == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No server blob in packet");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
server_data = ssh_string_data(s_server_blob);
|
||||
server_blob_len = ssh_string_len(s_server_blob);
|
||||
|
||||
/* Expect ML-KEM768 ciphertext + Curve25519 pubkey */
|
||||
if (server_blob_len != MLKEM768X25519_SERVER_RESPONSE_SIZE) {
|
||||
ssh_set_error(session, SSH_FATAL, "Invalid server blob size");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Store ML-KEM768 ciphertext for sessionid calculation */
|
||||
memcpy(crypto->mlkem768_ciphertext, server_data, MLKEM768_CIPHERTEXT_SIZE);
|
||||
|
||||
/* Decapsulate ML-KEM768 shared secret */
|
||||
rc = mlkem768_decapsulate(crypto->mlkem768_client_privkey,
|
||||
crypto->mlkem768_ciphertext,
|
||||
mlkem_shared_secret);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "ML-KEM768 decapsulation failed");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Store server Curve25519 public key for shared secret computation */
|
||||
memcpy(crypto->curve25519_server_pubkey,
|
||||
server_data + MLKEM768_CIPHERTEXT_SIZE,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
|
||||
/* Derive Curve25519 shared secret using existing libssh function */
|
||||
rc = ssh_curve25519_create_k(session, curve25519_shared_secret);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Curve25519 ECDH failed");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Combine secrets: ML-KEM768 + Curve25519 for hybrid approach */
|
||||
memcpy(combined_secret, mlkem_shared_secret, MLKEM768_SHARED_SECRET_SIZE);
|
||||
memcpy(combined_secret + MLKEM768_SHARED_SECRET_SIZE,
|
||||
curve25519_shared_secret,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
|
||||
sha256(combined_secret, MLKEM768X25519_SHARED_SECRET_SIZE, hashed_secret);
|
||||
|
||||
bignum_bin2bn(hashed_secret, SHA256_DIGEST_LEN, &crypto->shared_secret);
|
||||
if (crypto->shared_secret == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to create shared secret bignum");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get signature for verification */
|
||||
s_signature = ssh_buffer_get_ssh_string(packet);
|
||||
if (s_signature == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No signature in packet");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
crypto->dh_server_signature = s_signature;
|
||||
s_signature = NULL;
|
||||
|
||||
/* Send the MSG_NEWKEYS */
|
||||
rc = ssh_packet_send_newkeys(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to send SSH_MSG_NEWKEYS");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
|
||||
|
||||
cleanup:
|
||||
/* Clear sensitive data */
|
||||
explicit_bzero(mlkem_shared_secret, sizeof(mlkem_shared_secret));
|
||||
explicit_bzero(curve25519_shared_secret, sizeof(curve25519_shared_secret));
|
||||
explicit_bzero(combined_secret, sizeof(combined_secret));
|
||||
explicit_bzero(hashed_secret, sizeof(hashed_secret));
|
||||
ssh_string_free(s_pubkey_blob);
|
||||
ssh_string_free(s_server_blob);
|
||||
ssh_string_free(s_signature);
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
void ssh_client_mlkem768x25519_remove_callbacks(ssh_session session)
|
||||
{
|
||||
ssh_packet_remove_callbacks(session, &ssh_mlkem768x25519_client_callbacks);
|
||||
}
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_mlkem768x25519_init);
|
||||
|
||||
static ssh_packet_callback dh_server_callbacks[] = {
|
||||
ssh_packet_server_mlkem768x25519_init,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_mlkem768x25519_server_callbacks =
|
||||
{
|
||||
.start = SSH2_MSG_KEX_HYBRID_INIT,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = dh_server_callbacks,
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_mlkem768x25519_init)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_string client_pubkey_blob = NULL;
|
||||
ssh_string server_pubkey_blob = NULL;
|
||||
ssh_buffer server_response = NULL;
|
||||
const unsigned char *client_data = NULL;
|
||||
unsigned char mlkem_shared_secret[MLKEM768_SHARED_SECRET_SIZE];
|
||||
unsigned char curve25519_shared_secret[CURVE25519_PUBKEY_SIZE];
|
||||
unsigned char combined_secret[MLKEM768X25519_SHARED_SECRET_SIZE];
|
||||
unsigned char mlkem_ciphertext[MLKEM768_CIPHERTEXT_SIZE];
|
||||
unsigned char hashed_secret[SHA256_DIGEST_LEN];
|
||||
size_t client_blob_len;
|
||||
ssh_key privkey = NULL;
|
||||
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
|
||||
ssh_string sig_blob = NULL;
|
||||
ssh_string server_hostkey_blob = NULL;
|
||||
int rc = SSH_ERROR;
|
||||
(void)type;
|
||||
(void)user;
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Received ML-KEM768x25519 client init");
|
||||
|
||||
ssh_packet_remove_callbacks(session, &ssh_mlkem768x25519_server_callbacks);
|
||||
|
||||
/* Get client hybrid public key: ML-KEM768 + Curve25519 */
|
||||
client_pubkey_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (client_pubkey_blob == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "No client public key in packet");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
client_data = ssh_string_data(client_pubkey_blob);
|
||||
client_blob_len = ssh_string_len(client_pubkey_blob);
|
||||
|
||||
/* Expect ML-KEM768 pubkey + Curve25519 pubkey */
|
||||
if (client_blob_len != MLKEM768X25519_CLIENT_PUBKEY_SIZE) {
|
||||
ssh_set_error(session, SSH_FATAL, "Invalid client public key size");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Extract client ML-KEM768 public key */
|
||||
memcpy(crypto->mlkem768_client_pubkey,
|
||||
client_data,
|
||||
MLKEM768_PUBLICKEY_SIZE);
|
||||
|
||||
/* Extract client Curve25519 public key */
|
||||
memcpy(crypto->curve25519_client_pubkey,
|
||||
client_data + MLKEM768_PUBLICKEY_SIZE,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
|
||||
/* Generate server Curve25519 keypair */
|
||||
rc = ssh_curve25519_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to generate server Curve25519 key");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Derive Curve25519 shared secret */
|
||||
rc = ssh_curve25519_create_k(session, curve25519_shared_secret);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Curve25519 ECDH failed");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Encapsulate ML-KEM768 shared secret using client's public key */
|
||||
rc = mlkem768_encapsulate(client_data, mlkem_ciphertext, mlkem_shared_secret);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "ML-KEM768 encapsulation failed");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Store ML-KEM768 ciphertext for sessionid calculation */
|
||||
memcpy(crypto->mlkem768_ciphertext, mlkem_ciphertext, MLKEM768_CIPHERTEXT_SIZE);
|
||||
|
||||
/* Combine secrets: ML-KEM768 + Curve25519 for hybrid approach */
|
||||
memcpy(combined_secret, mlkem_shared_secret, MLKEM768_SHARED_SECRET_SIZE);
|
||||
memcpy(combined_secret + MLKEM768_SHARED_SECRET_SIZE,
|
||||
curve25519_shared_secret,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
|
||||
sha256(combined_secret, MLKEM768X25519_SHARED_SECRET_SIZE, hashed_secret);
|
||||
|
||||
/* Store the combined secret */
|
||||
bignum_bin2bn(hashed_secret, SHA256_DIGEST_LEN, &crypto->shared_secret);
|
||||
if (crypto->shared_secret == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to create shared secret bignum");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Create server response: ML-KEM768 ciphertext + Curve25519 pubkey */
|
||||
server_response = ssh_buffer_new();
|
||||
if (server_response == NULL) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(server_response,
|
||||
"PP",
|
||||
MLKEM768_CIPHERTEXT_SIZE,
|
||||
mlkem_ciphertext,
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
crypto->curve25519_server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Convert to string for sending */
|
||||
server_pubkey_blob = ssh_string_new(ssh_buffer_get_len(server_response));
|
||||
if (server_pubkey_blob == NULL) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
ssh_string_fill(server_pubkey_blob,
|
||||
ssh_buffer_get(server_response),
|
||||
ssh_buffer_get_len(server_response));
|
||||
|
||||
/* Add MSG_KEX_ECDH_REPLY header */
|
||||
rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_HYBRID_REPLY);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get server host key */
|
||||
rc = ssh_get_key_params(session, &privkey, &digest);
|
||||
if (rc == SSH_ERROR) {
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Build session ID */
|
||||
rc = ssh_make_sessionid(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not create a session id");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_dh_get_next_server_publickey_blob(session, &server_hostkey_blob);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not export server public key");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Add server host key to output */
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, server_hostkey_blob);
|
||||
SSH_STRING_FREE(server_hostkey_blob);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Add server response (ciphertext + pubkey) */
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_blob);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Sign the exchange hash */
|
||||
sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey, digest);
|
||||
if (sig_blob == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not sign the session id");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Add signature */
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, sig_blob);
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_packet_send(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to send SSH_MSG_KEX_ECDH_REPLY");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Send the MSG_NEWKEYS */
|
||||
rc = ssh_packet_send_newkeys(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to send SSH_MSG_NEWKEYS");
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
|
||||
|
||||
cleanup:
|
||||
/* Clear sensitive data */
|
||||
explicit_bzero(mlkem_shared_secret, sizeof(mlkem_shared_secret));
|
||||
explicit_bzero(curve25519_shared_secret, sizeof(curve25519_shared_secret));
|
||||
explicit_bzero(combined_secret, sizeof(combined_secret));
|
||||
explicit_bzero(hashed_secret, sizeof(hashed_secret));
|
||||
ssh_string_free(client_pubkey_blob);
|
||||
ssh_string_free(server_pubkey_blob);
|
||||
ssh_buffer_free(server_response);
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
void ssh_server_mlkem768x25519_init(ssh_session session)
|
||||
{
|
||||
SSH_LOG(SSH_LOG_TRACE, "Setting up ML-KEM768x25519 server callbacks");
|
||||
ssh_packet_set_callbacks(session, &ssh_mlkem768x25519_server_callbacks);
|
||||
}
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
145
src/options.c
145
src/options.c
@@ -31,13 +31,14 @@
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include "libssh/config_parser.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/pki.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"
|
||||
#include <sys/types.h>
|
||||
#ifdef WITH_SERVER
|
||||
#include "libssh/server.h"
|
||||
#include "libssh/bind.h"
|
||||
@@ -592,10 +593,10 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* - 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
|
||||
* The values under 1024 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.
|
||||
* Default is 3072 bits or 2048 bits in FIPS mode.
|
||||
* (int)
|
||||
|
||||
* - SSH_OPTIONS_IDENTITY_AGENT
|
||||
@@ -1288,11 +1289,13 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
|
||||
/* (*x == 0) is allowed as it is used to revert to default */
|
||||
|
||||
if (*x > 0 && *x < 768) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
if (*x > 0 && *x < RSA_MIN_KEY_SIZE) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"The provided value (%d) for minimal RSA key "
|
||||
"size is too small. Use at least 768 bits.",
|
||||
*x);
|
||||
"size is too small. Use at least %d bits.",
|
||||
*x,
|
||||
RSA_MIN_KEY_SIZE);
|
||||
return -1;
|
||||
}
|
||||
session->opts.rsa_min_size = *x;
|
||||
@@ -1814,6 +1817,8 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
|
||||
*
|
||||
* @param filename The options file to use, if NULL the default
|
||||
* ~/.ssh/config and /etc/ssh/ssh_config will be used.
|
||||
* If complied with support for hermetic-usr,
|
||||
* /usr/etc/ssh/ssh_config will be used last.
|
||||
*
|
||||
* @return 0 on success, < 0 on error.
|
||||
*
|
||||
@@ -1821,48 +1826,63 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
|
||||
*/
|
||||
int ssh_options_parse_config(ssh_session session, const char *filename)
|
||||
{
|
||||
char *expanded_filename = NULL;
|
||||
int r;
|
||||
char *expanded_filename = NULL;
|
||||
int r;
|
||||
FILE *fp = NULL;
|
||||
|
||||
if (session == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (session->opts.host == NULL) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
if (session == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (session->opts.host == NULL) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->opts.sshdir == NULL) {
|
||||
r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL);
|
||||
if (r < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (session->opts.sshdir == NULL) {
|
||||
r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL);
|
||||
if (r < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* set default filename */
|
||||
if (filename == NULL) {
|
||||
expanded_filename = ssh_path_expand_escape(session, "%d/config");
|
||||
} else {
|
||||
expanded_filename = ssh_path_expand_escape(session, filename);
|
||||
}
|
||||
if (expanded_filename == NULL) {
|
||||
return -1;
|
||||
}
|
||||
/* set default filename */
|
||||
if (filename == NULL) {
|
||||
expanded_filename = ssh_path_expand_escape(session, "%d/config");
|
||||
} else {
|
||||
expanded_filename = ssh_path_expand_escape(session, filename);
|
||||
}
|
||||
if (expanded_filename == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = ssh_config_parse_file(session, expanded_filename);
|
||||
if (r < 0) {
|
||||
goto out;
|
||||
}
|
||||
if (filename == NULL) {
|
||||
r = ssh_config_parse_file(session, GLOBAL_CLIENT_CONFIG);
|
||||
}
|
||||
r = ssh_config_parse_file(session, expanded_filename);
|
||||
if (r < 0) {
|
||||
goto out;
|
||||
}
|
||||
if (filename == NULL) {
|
||||
if ((fp = fopen(GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
|
||||
filename = GLOBAL_CLIENT_CONFIG;
|
||||
#ifdef USR_GLOBAL_CLIENT_CONFIG
|
||||
} else if ((fp = fopen(USR_GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
|
||||
filename = USR_GLOBAL_CLIENT_CONFIG;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Do not process the default configuration as part of connection again */
|
||||
session->opts.config_processed = true;
|
||||
if (fp) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Reading configuration data from %s",
|
||||
filename);
|
||||
r = ssh_config_parse(session, fp, true);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Do not process the default configuration as part of connection again */
|
||||
session->opts.config_processed = true;
|
||||
out:
|
||||
free(expanded_filename);
|
||||
return r;
|
||||
free(expanded_filename);
|
||||
return r;
|
||||
}
|
||||
|
||||
int ssh_options_apply(ssh_session session)
|
||||
@@ -1968,10 +1988,10 @@ int ssh_options_apply(ssh_session session)
|
||||
* it with ssh expansion of ssh escape characters.
|
||||
*/
|
||||
tmp = ssh_path_expand_escape(session, id);
|
||||
free(id);
|
||||
if (tmp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
free(id);
|
||||
}
|
||||
|
||||
/* use append to keep the order at first call and use prepend
|
||||
@@ -1982,6 +2002,7 @@ int ssh_options_apply(ssh_session session)
|
||||
rc = ssh_list_append(session->opts.identity, tmp);
|
||||
}
|
||||
if (rc != SSH_OK) {
|
||||
free(tmp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1993,13 +2014,14 @@ int ssh_options_apply(ssh_session session)
|
||||
char *id = tmp;
|
||||
|
||||
tmp = ssh_path_expand_escape(session, id);
|
||||
free(id);
|
||||
if (tmp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
free(id);
|
||||
|
||||
rc = ssh_list_append(session->opts.certificate, tmp);
|
||||
if (rc != SSH_OK) {
|
||||
free(tmp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -2179,11 +2201,11 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
|
||||
* - 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
|
||||
* operations. The values under 1024 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.
|
||||
* Default is 3072 bits or 2048 bits in FIPS mode.
|
||||
* (int)
|
||||
*
|
||||
*
|
||||
@@ -2268,6 +2290,9 @@ ssh_bind_options_set(ssh_bind sshbind,
|
||||
SSH_FATAL,
|
||||
"The host key size %d is too small.",
|
||||
ssh_key_size(key));
|
||||
if (type != SSH_BIND_OPTIONS_IMPORT_KEY) {
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
key_type = ssh_key_type(key);
|
||||
@@ -2313,6 +2338,11 @@ ssh_bind_options_set(ssh_bind sshbind,
|
||||
ssh_key_free(key);
|
||||
return -1;
|
||||
}
|
||||
} else if (type == SSH_BIND_OPTIONS_IMPORT_KEY_STR) {
|
||||
if (bind_key_loc == NULL) {
|
||||
ssh_key_free(key);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (bind_key_loc == NULL) {
|
||||
return -1;
|
||||
@@ -2563,12 +2593,13 @@ ssh_bind_options_set(ssh_bind sshbind,
|
||||
|
||||
/* (*x == 0) is allowed as it is used to revert to default */
|
||||
|
||||
if (*x > 0 && *x < 768) {
|
||||
if (*x > 0 && *x < RSA_MIN_KEY_SIZE) {
|
||||
ssh_set_error(sshbind,
|
||||
SSH_REQUEST_DENIED,
|
||||
"The provided value (%d) for minimal RSA key "
|
||||
"size is too small. Use at least 768 bits.",
|
||||
*x);
|
||||
"size is too small. Use at least %d bits.",
|
||||
*x,
|
||||
RSA_MIN_KEY_SIZE);
|
||||
return -1;
|
||||
}
|
||||
sshbind->rsa_min_size = *x;
|
||||
@@ -2706,7 +2737,13 @@ int ssh_bind_options_parse_config(ssh_bind sshbind, const char *filename)
|
||||
/* If the global default configuration hasn't been processed yet, process it
|
||||
* before the provided configuration. */
|
||||
if (!(sshbind->config_processed)) {
|
||||
rc = ssh_bind_config_parse_file(sshbind, GLOBAL_BIND_CONFIG);
|
||||
if (ssh_file_readaccess_ok(GLOBAL_BIND_CONFIG)) {
|
||||
rc = ssh_bind_config_parse_file(sshbind, GLOBAL_BIND_CONFIG);
|
||||
#ifdef USR_GLOBAL_BIND_CONFIG
|
||||
} else {
|
||||
rc = ssh_bind_config_parse_file(sshbind, USR_GLOBAL_BIND_CONFIG);
|
||||
#endif
|
||||
}
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
24
src/packet.c
24
src/packet.c
@@ -294,6 +294,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
* or session_state == SSH_SESSION_STATE_INITIAL_KEX
|
||||
* - dh_handshake_state == DH_STATE_INIT
|
||||
* or dh_handshake_state == DH_STATE_INIT_SENT (re-exchange)
|
||||
* or dh_handshake_state == DH_STATE_REQUEST_SENT (dh-gex)
|
||||
* or dh_handshake_state == DH_STATE_FINISHED (re-exchange)
|
||||
*
|
||||
* Transitions:
|
||||
@@ -313,6 +314,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
|
||||
if ((session->dh_handshake_state != DH_STATE_INIT) &&
|
||||
(session->dh_handshake_state != DH_STATE_INIT_SENT) &&
|
||||
(session->dh_handshake_state != DH_STATE_REQUEST_SENT) &&
|
||||
(session->dh_handshake_state != DH_STATE_FINISHED))
|
||||
{
|
||||
rc = SSH_PACKET_DENIED;
|
||||
@@ -350,7 +352,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
break;
|
||||
case SSH2_MSG_KEXDH_INIT: // 30
|
||||
// SSH2_MSG_KEX_ECDH_INIT: // 30
|
||||
// SSH2_MSG_ECMQV_INIT: // 30
|
||||
// SSH2_MSG_KEX_HYBRID_INIT: // 30
|
||||
// SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: // 30
|
||||
|
||||
/* Server only */
|
||||
@@ -386,7 +388,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
break;
|
||||
case SSH2_MSG_KEXDH_REPLY: // 31
|
||||
// SSH2_MSG_KEX_ECDH_REPLY: // 31
|
||||
// SSH2_MSG_ECMQV_REPLY: // 31
|
||||
// SSH2_MSG_KEX_HYBRID_REPLY: // 31
|
||||
// SSH2_MSG_KEX_DH_GEX_GROUP: // 31
|
||||
|
||||
/* Client only */
|
||||
@@ -1158,7 +1160,7 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
|
||||
uint32_t lenfield_blocksize = 8;
|
||||
size_t current_macsize = 0;
|
||||
uint8_t *ptr = NULL;
|
||||
long to_be_read;
|
||||
ssize_t to_be_read;
|
||||
int rc;
|
||||
uint8_t *cleartext_packet = NULL;
|
||||
uint8_t *packet_second_block = NULL;
|
||||
@@ -1268,7 +1270,7 @@ 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 (%ld)!",
|
||||
"Given numbers of bytes left to be read < 0 (%zd)!",
|
||||
to_be_read);
|
||||
goto error;
|
||||
}
|
||||
@@ -1286,7 +1288,7 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
|
||||
/* give up, not enough data in buffer */
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"packet: partial packet (read len) "
|
||||
"[len=%" PRIu32 ", receivedlen=%zu, to_be_read=%ld]",
|
||||
"[len=%" PRIu32 ", receivedlen=%zu, to_be_read=%zd]",
|
||||
packet_len,
|
||||
receivedlen,
|
||||
to_be_read);
|
||||
@@ -1300,7 +1302,7 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
|
||||
/* remaining encrypted bytes from the packet, MAC not included */
|
||||
packet_remaining = packet_len - (packet_offset - sizeof(uint32_t));
|
||||
cleartext_packet = ssh_buffer_allocate(session->in_buffer,
|
||||
packet_remaining);
|
||||
(uint32_t)packet_remaining);
|
||||
if (cleartext_packet == NULL) {
|
||||
goto error;
|
||||
}
|
||||
@@ -1476,6 +1478,7 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
|
||||
|
||||
session->packet_state = PACKET_STATE_INIT;
|
||||
if (processed < receivedlen) {
|
||||
size_t num;
|
||||
/* Handle a potential packet left in socket buffer */
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Processing %zu bytes left in socket buffer",
|
||||
@@ -1483,9 +1486,10 @@ 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);
|
||||
processed += rc;
|
||||
num = ssh_packet_socket_callback(ptr,
|
||||
receivedlen - processed,
|
||||
user);
|
||||
processed += num;
|
||||
}
|
||||
|
||||
ok = ssh_packet_need_rekey(session, 0);
|
||||
@@ -1854,7 +1858,7 @@ static int packet_send2(ssh_session session)
|
||||
if (hmac != NULL) {
|
||||
rc = ssh_buffer_add_data(session->out_buffer,
|
||||
hmac,
|
||||
hmac_digest_len(hmac_type));
|
||||
(uint32_t)hmac_digest_len(hmac_type));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -291,7 +291,6 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
|
||||
for (i = 0; i < nr_extensions; i++) {
|
||||
char *name = NULL;
|
||||
char *value = NULL;
|
||||
int cmp;
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "ss", &name, &value);
|
||||
if (rc != SSH_OK) {
|
||||
@@ -299,8 +298,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
cmp = strcmp(name, "server-sig-algs");
|
||||
if (cmp == 0) {
|
||||
if (strcmp(name, "server-sig-algs") == 0) {
|
||||
/* TODO check for NULL bytes */
|
||||
SSH_LOG(SSH_LOG_PACKET, "Extension: %s=<%s>", name, value);
|
||||
|
||||
@@ -313,6 +311,9 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
|
||||
if (rc == 1) {
|
||||
session->extensions |= SSH_EXT_SIG_RSA_SHA256;
|
||||
}
|
||||
} else if (strcmp(name, "publickey-hostbound@openssh.com") == 0) {
|
||||
SSH_LOG(SSH_LOG_PACKET, "Extension: %s=<%s>", name, value);
|
||||
session->extensions |= SSH_EXT_PUBLICKEY_HOSTBOUND;
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_PACKET, "Unknown extension: %s", name);
|
||||
}
|
||||
|
||||
@@ -126,11 +126,10 @@ ssh_pcap_file ssh_pcap_file_new(void)
|
||||
{
|
||||
struct ssh_pcap_file_struct *pcap = NULL;
|
||||
|
||||
pcap = malloc(sizeof(struct ssh_pcap_file_struct));
|
||||
pcap = calloc(1, sizeof(struct ssh_pcap_file_struct));
|
||||
if (pcap == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ZERO_STRUCTP(pcap);
|
||||
|
||||
return pcap;
|
||||
}
|
||||
@@ -296,12 +295,13 @@ void ssh_pcap_file_free(ssh_pcap_file pcap)
|
||||
*/
|
||||
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));
|
||||
ssh_pcap_context ctx = NULL;
|
||||
|
||||
ctx = calloc(1, sizeof(struct ssh_pcap_context_struct));
|
||||
if (ctx == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return NULL;
|
||||
}
|
||||
ZERO_STRUCTP(ctx);
|
||||
ctx->session = session;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user