Compare commits
221 Commits
09155adb19
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b3c061aae | ||
|
|
1525ea3dda | ||
|
|
a189c2ef4d | ||
|
|
b2abcf8534 | ||
|
|
809f9b7729 | ||
|
|
d297621c33 | ||
|
|
d936b7e81d | ||
|
|
971d44107e | ||
|
|
a1e49728ba | ||
|
|
6c5459e7fc | ||
|
|
f47d1c797a | ||
|
|
da27d23125 | ||
|
|
34db488e4d | ||
|
|
9780fa2f01 | ||
|
|
5a795ce47c | ||
|
|
b33a90d20b | ||
|
|
ef45b8ae8c | ||
|
|
3c2b254206 | ||
|
|
7dea005729 | ||
|
|
ad8d0c1e03 | ||
|
|
cb0f7d963e | ||
|
|
c90b239230 | ||
|
|
18ec01c980 | ||
|
|
a983142a07 | ||
|
|
89d51ced0d | ||
|
|
16771cc574 | ||
|
|
247ebb4d7f | ||
|
|
acd5dace66 | ||
|
|
5545b8808b | ||
|
|
be5a900ed0 | ||
|
|
40ba3c6c80 | ||
|
|
57225a7168 | ||
|
|
02ae2ace35 | ||
|
|
76c6ee9ccf | ||
|
|
9a3351934b | ||
|
|
1f1309c915 | ||
|
|
a8ca282033 | ||
|
|
b61bb3f8ac | ||
|
|
c9abf5ebbb | ||
|
|
48fdf4b80a | ||
|
|
f5eb3e532b | ||
|
|
3f0007895c | ||
|
|
06186279a8 | ||
|
|
c36bd2304a | ||
|
|
82db6a7ab3 | ||
|
|
deffea5ad2 | ||
|
|
320844669a | ||
|
|
d0d45c8915 | ||
|
|
65abae059e | ||
|
|
7c2574682c | ||
|
|
d2bb1ba889 | ||
|
|
9b4ee9c6d4 | ||
|
|
d3e80d9a19 | ||
|
|
4d3da7819c | ||
|
|
b79a681ebb | ||
|
|
f7cad4245a | ||
|
|
11c4b29e20 | ||
|
|
e04d753ace | ||
|
|
06eea93ded | ||
|
|
06edb2db5e | ||
|
|
ced98d41cf | ||
|
|
88c2ea6752 | ||
|
|
5fed1bc8be | ||
|
|
a30ba0091f | ||
|
|
ad23fe8c27 | ||
|
|
3710b31d24 | ||
|
|
2c5bb17211 | ||
|
|
83ae6b3f0a | ||
|
|
06cefe1d67 | ||
|
|
043b1fb133 | ||
|
|
f1490170f3 | ||
|
|
4ba0746135 | ||
|
|
e94fd6ccd1 | ||
|
|
83114b636f | ||
|
|
5a99cf9c7f | ||
|
|
213556ce01 | ||
|
|
5d06ee459b | ||
|
|
96807b9313 | ||
|
|
6d81ecddbe | ||
|
|
d0e5cf78d0 | ||
|
|
a0707afc3e | ||
|
|
06b61f75fa | ||
|
|
f9d7cadf4b | ||
|
|
c1aab9903f | ||
|
|
fd1c3e8878 | ||
|
|
d730b40b91 | ||
|
|
9044fcdb52 | ||
|
|
bc5211d055 | ||
|
|
701a2155a7 | ||
|
|
38f3d158f6 | ||
|
|
0d5a2652b4 | ||
|
|
5c496acef7 | ||
|
|
3e074a3fba | ||
|
|
98a844ceb2 | ||
|
|
ce45ba8c61 | ||
|
|
62c85a59a9 | ||
|
|
c4f1a70a89 | ||
|
|
f52be27114 | ||
|
|
228208af5e | ||
|
|
163373c9d9 | ||
|
|
e82677a923 | ||
|
|
79966eb924 | ||
|
|
4feb0dd79d | ||
|
|
f8d943afda | ||
|
|
4bad7cc08f | ||
|
|
3526e02dee | ||
|
|
ecea5b6052 | ||
|
|
1833ce86f9 | ||
|
|
3938e5e850 | ||
|
|
dd80a56029 | ||
|
|
9d6df9d0fa | ||
|
|
ee180c660e | ||
|
|
541cd39f14 | ||
|
|
64f72ed55f | ||
|
|
0ef79018b3 | ||
|
|
7911580304 | ||
|
|
e5108f2ffc | ||
|
|
5ce4b65abb | ||
|
|
b62675b435 | ||
|
|
f333d95013 | ||
|
|
92d0f8aba6 | ||
|
|
66460578b1 | ||
|
|
b93db6c3d1 | ||
|
|
1c3143ff00 | ||
|
|
47305a2f72 | ||
|
|
5bbaecfaa7 | ||
|
|
6e5d0a935f | ||
|
|
5d4d9f8208 | ||
|
|
c128cf8807 | ||
|
|
5937b5ba4e | ||
|
|
1241a3a8c9 | ||
|
|
21d338737a | ||
|
|
d91630308d | ||
|
|
37f0e91814 | ||
|
|
32a256e157 | ||
|
|
14bd26e71c | ||
|
|
97e71606e0 | ||
|
|
d4b0de702b | ||
|
|
acc080ac03 | ||
|
|
e56af9fa79 | ||
|
|
c4b2bd34a8 | ||
|
|
50ee6411f2 | ||
|
|
c1dd30b47b | ||
|
|
8ba9e931e8 | ||
|
|
eda5c6576b | ||
|
|
302d868875 | ||
|
|
7db75e8fd0 | ||
|
|
ebe632cf8f | ||
|
|
150d606db7 | ||
|
|
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 |
1
.clang-format-ignore
Normal file
@@ -0,0 +1 @@
|
||||
src/external/*
|
||||
6
.cmake-format.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
line_width: 80
|
||||
tab_size: 4
|
||||
use_tabchars: false
|
||||
separate_ctrl_name_with_space: true
|
||||
separate_fn_name_with_space: false
|
||||
@@ -12,7 +12,12 @@ indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
[{CMakeLists.txt,*.cmake}]
|
||||
[CMakeLists.txt]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
tab_width = 4
|
||||
|
||||
[*.cmake]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
218
.gitlab-ci.yml
@@ -17,7 +17,7 @@ stages:
|
||||
- analysis
|
||||
|
||||
# This is some black magic to select between branch pipelines and
|
||||
# merge request pipelines to avoid running same pipelines in twice
|
||||
# merge request pipelines to avoid running same pipelines twice
|
||||
workflow:
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
|
||||
@@ -31,7 +31,7 @@ workflow:
|
||||
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_BUILD_OPTIONS: "-DWITH_BLOWFISH_CIPHER=ON -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DWITH_FIDO2=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
|
||||
|
||||
@@ -83,6 +83,42 @@ workflow:
|
||||
.tumbleweed:
|
||||
extends: .tests
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD
|
||||
script:
|
||||
# torture_gssapi_key_exchange_null is excluded because of a bug
|
||||
# https://bugzilla.opensuse.org/show_bug.cgi?id=1254680
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure -E "^torture_gssapi_key_exchange_null$"
|
||||
|
||||
.centos:
|
||||
extends: .tests
|
||||
variables:
|
||||
OPENSSL_ENABLE_SHA1_SIGNATURES: 1
|
||||
|
||||
.centos10:
|
||||
extends: .centos
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
|
||||
|
||||
.centos9:
|
||||
extends: .centos
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
|
||||
|
||||
# Unit testing only, no client and pkd testing, because cwrap is not available
|
||||
# for MinGW
|
||||
.mingw:
|
||||
extends: .tests
|
||||
variables:
|
||||
WINEDEBUG: -all
|
||||
script:
|
||||
- $WINEBIN $CMAKE_DEFAULT_OPTIONS
|
||||
-DWITH_SFTP=ON
|
||||
-DWITH_SERVER=ON
|
||||
-DWITH_ZLIB=ON
|
||||
-DWITH_PCAP=ON
|
||||
-DWITH_FIDO2=ON
|
||||
-DUNIT_TESTING=ON .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure -E torture_rand
|
||||
|
||||
.fips:
|
||||
extends: .tests
|
||||
@@ -126,12 +162,10 @@ review:
|
||||
# CentOS builds #
|
||||
###############################################################################
|
||||
centos10s/openssl_3.5.x/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
|
||||
extends: .tests
|
||||
extends: .centos10
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
|
||||
script:
|
||||
- export OPENSSL_ENABLE_SHA1_SIGNATURES=1
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure
|
||||
@@ -141,19 +175,17 @@ centos10s/openssl_3.5.x/x86_64/fips:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS10_BUILD
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
|
||||
OPENSSL_ENABLE_SHA1_SIGNATURES: 1
|
||||
script:
|
||||
- export OPENSSL_ENABLE_SHA1_SIGNATURES=1
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
|
||||
|
||||
centos9s/openssl_3.5.x/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
|
||||
extends: .tests
|
||||
extends: .centos9
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
|
||||
script:
|
||||
- export OPENSSL_ENABLE_SHA1_SIGNATURES=1
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure
|
||||
@@ -167,11 +199,14 @@ centos9s/mbedtls_2.x/x86_64:
|
||||
centos9s/openssl_3.5.x/x86_64/fips:
|
||||
extends: .fips
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
|
||||
variables:
|
||||
OPENSSL_ENABLE_SHA1_SIGNATURES: 1
|
||||
script:
|
||||
- export OPENSSL_ENABLE_SHA1_SIGNATURES=1
|
||||
# torture_gssapi_key_exchange_* tests are excluded because gssapi-keyex is disabled
|
||||
# by OpenSSH in FIPS mode in RHEL 9
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
|
||||
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure -E "^torture_gssapi_key_exchange.*"
|
||||
|
||||
centos8s/openssl_1.1.1/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
|
||||
@@ -187,9 +222,11 @@ centos8s/openssl_1.1.1/x86_64/fips:
|
||||
extends: .fips
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
|
||||
script:
|
||||
# torture_gssapi_key_exchange_* and torture_gssapi_server_key_exchange_* tests are excluded
|
||||
# because gssapi-keyex is not allowed in FIPS mode in RHEL 8
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
|
||||
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure -E "^torture_gssapi.*key_exchange.*"
|
||||
|
||||
###############################################################################
|
||||
# Fedora builds #
|
||||
@@ -202,14 +239,16 @@ fedora/docs:
|
||||
extends: .build
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
|
||||
script:
|
||||
- cmake .. && make docs_coverage && make docs
|
||||
- cmake -DWITH_INTERNAL_DOC=ON .. && make docs_coverage && make docs
|
||||
coverage: '/^Documentation coverage is \d+.\d+%/'
|
||||
|
||||
fedora/ninja:
|
||||
extends: .fedora
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
|
||||
variables:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
script:
|
||||
- cmake -G Ninja $CMAKE_OPTIONS ../ && ninja && CTEST_OUTPUT_ON_FAILURE=1 ninja test
|
||||
- cmake -G Ninja $CMAKE_OPTIONS ../ && ninja && ninja test
|
||||
|
||||
fedora/coverage:
|
||||
extends: .fedora
|
||||
@@ -247,9 +286,10 @@ fedora/openssl_3.x/x86_64/minimal:
|
||||
-DWITH_SERVER=OFF
|
||||
-DWITH_ZLIB=OFF
|
||||
-DWITH_PCAP=OFF
|
||||
-DWITH_GSSAPI=OFF
|
||||
-DWITH_GEX=OFF
|
||||
-DUNIT_TESTING=ON
|
||||
-DCLIENT_TESTING=ON
|
||||
-DWITH_GEX=OFF .. &&
|
||||
-DCLIENT_TESTING=ON .. &&
|
||||
make -j$(nproc)
|
||||
|
||||
.valgrind:
|
||||
@@ -261,6 +301,37 @@ fedora/openssl_3.x/x86_64/minimal:
|
||||
make test_memcheck
|
||||
- cat Testing/Temporary/MemoryChecker.*.log | wc -l | grep "^0$"
|
||||
|
||||
fedora/libressl/x86_64:
|
||||
extends: .fedora
|
||||
stage: test
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
|
||||
variables:
|
||||
LIBRESSL_VERSION: "4.2.1"
|
||||
CMAKE_ADDITIONAL_OPTIONS: >
|
||||
-DCMAKE_C_FLAGS="-I/opt/libressl/include"
|
||||
-DOPENSSL_ROOT_DIR=/opt/libressl
|
||||
-DOPENSSL_INCLUDE_DIR=/opt/libressl/include
|
||||
-DOPENSSL_CRYPTO_LIBRARY=/opt/libressl/lib/libcrypto.so
|
||||
-DOPENSSL_SSL_LIBRARY=/opt/libressl/lib/libssl.so
|
||||
-DWITH_GSSAPI=OFF
|
||||
-DWITH_FIDO2=OFF
|
||||
before_script:
|
||||
- *build
|
||||
- dnf install -y perl-core autoconf automake libtool pkgconf-pkg-config
|
||||
- curl -LO https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz
|
||||
- tar xf libressl-${LIBRESSL_VERSION}.tar.gz
|
||||
- cd libressl-${LIBRESSL_VERSION}
|
||||
- ./configure --prefix=/opt/libressl
|
||||
- make -j$(nproc)
|
||||
- make install
|
||||
- cd ..
|
||||
script:
|
||||
- export PKG_CONFIG_PATH=/opt/libressl/lib/pkgconfig
|
||||
- export LD_LIBRARY_PATH=/opt/libressl/lib
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure
|
||||
|
||||
# The PKCS#11 support is turned off as it brings dozens of memory issues from
|
||||
# engine_pkcs11 or openssl itself
|
||||
fedora/valgrind/openssl:
|
||||
@@ -331,47 +402,26 @@ fedora/undefined-sanitizer:
|
||||
fedora/libgcrypt/x86_64:
|
||||
extends: .fedora
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DWITH_GCRYPT=ON -DWITH_DEBUG_CRYPTO=ON"
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DWITH_GCRYPT=ON"
|
||||
|
||||
fedora/mbedtls_2.x/x86_64:
|
||||
fedora/mbedtls_3.x/x86_64:
|
||||
extends: .fedora
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON -DWITH_DEBUG_CRYPTO=ON "
|
||||
CMAKE_ADDITIONAL_OPTIONS: "-DWITH_MBEDTLS=ON"
|
||||
|
||||
# Unit testing only, no client and pkd testing, because cwrap is not available
|
||||
# for MinGW
|
||||
fedora/mingw64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$MINGW_BUILD
|
||||
extends: .tests
|
||||
script:
|
||||
- export WINEPATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin
|
||||
- export WINEDEBUG=-all
|
||||
- mingw64-cmake $CMAKE_DEFAULT_OPTIONS
|
||||
-DWITH_SFTP=ON
|
||||
-DWITH_SERVER=ON
|
||||
-DWITH_ZLIB=ON
|
||||
-DWITH_PCAP=ON
|
||||
-DUNIT_TESTING=ON .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure -E torture_rand
|
||||
extends: .mingw
|
||||
variables:
|
||||
WINEPATH: /usr/x86_64-w64-mingw32/sys-root/mingw/bin
|
||||
WINEBIN: mingw64-cmake
|
||||
|
||||
# Unit testing only, no client and pkd testing, because cwrap is not available
|
||||
# for MinGW
|
||||
fedora/mingw32:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$MINGW_BUILD
|
||||
extends: .tests
|
||||
script:
|
||||
- export WINEPATH=/usr/i686-w64-mingw32/sys-root/mingw/bin
|
||||
- export WINEDEBUG=-all
|
||||
- mingw32-cmake $CMAKE_DEFAULT_OPTIONS
|
||||
-DWITH_SFTP=ON
|
||||
-DWITH_SERVER=ON
|
||||
-DWITH_ZLIB=ON
|
||||
-DWITH_PCAP=ON
|
||||
-DUNIT_TESTING=ON .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure -E torture_rand
|
||||
|
||||
extends: .mingw
|
||||
variables:
|
||||
WINEPATH: /usr/i686-w64-mingw32/sys-root/mingw/bin
|
||||
WINEBIN: mingw32-cmake
|
||||
|
||||
###############################################################################
|
||||
# Fedora csbuild #
|
||||
@@ -380,6 +430,8 @@ fedora/mingw32:
|
||||
stage: analysis
|
||||
variables:
|
||||
GIT_DEPTH: "100"
|
||||
CSCPPC_ADD_OPTS: "--library=./.gitlab-ci/cmocka.cfg"
|
||||
CMAKE_OPTIONS: "-DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON"
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
|
||||
before_script:
|
||||
- |
|
||||
@@ -397,6 +449,14 @@ fedora/mingw32:
|
||||
git branch --contains $CI_COMMIT_BEFORE_SHA 2>/dev/null || export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~20")
|
||||
|
||||
export CI_COMMIT_RANGE="$CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA"
|
||||
script:
|
||||
- csbuild
|
||||
--build-dir=obj-csbuild
|
||||
--build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS @SRCDIR@ && make clean && make -j$(nproc)"
|
||||
--git-commit-range $CI_COMMIT_RANGE
|
||||
--color
|
||||
--print-current --print-fixed
|
||||
|
||||
tags:
|
||||
- saas-linux-small-amd64
|
||||
except:
|
||||
@@ -411,33 +471,16 @@ fedora/mingw32:
|
||||
|
||||
fedora/csbuild/openssl_3.x:
|
||||
extends: .csbuild
|
||||
script:
|
||||
- csbuild
|
||||
--build-dir=obj-csbuild
|
||||
--build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON @SRCDIR@ && make clean && make -j$(nproc)"
|
||||
--git-commit-range $CI_COMMIT_RANGE
|
||||
--color
|
||||
--print-current --print-fixed
|
||||
|
||||
fedora/csbuild/libgcrypt:
|
||||
extends: .csbuild
|
||||
script:
|
||||
- csbuild
|
||||
--build-dir=obj-csbuild
|
||||
--build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON -DWITH_GCRYPT=ON @SRCDIR@ && make clean && make -j$(nproc)"
|
||||
--git-commit-range $CI_COMMIT_RANGE
|
||||
--color
|
||||
--print-current --print-fixed
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_GCRYPT=ON
|
||||
|
||||
fedora/csbuild/mbedtls:
|
||||
extends: .csbuild
|
||||
script:
|
||||
- csbuild
|
||||
--build-dir=obj-csbuild
|
||||
--build-cmd "rm -rf CMakeFiles CMakeCache.txt && cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON -DFUZZ_TESTING=ON -DWITH_MBEDTLS=ON @SRCDIR@ && make clean && make -j$(nproc)"
|
||||
--git-commit-range $CI_COMMIT_RANGE
|
||||
--color
|
||||
--print-current --print-fixed
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_MBEDTLS=ON
|
||||
|
||||
###############################################################################
|
||||
# Fedora abidiff #
|
||||
@@ -502,6 +545,12 @@ fedora/abidiff:
|
||||
ubuntu/openssl_3.0.x/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$UBUNTU_BUILD
|
||||
extends: .tests
|
||||
script:
|
||||
# torture_gssapi_key_exchange_null is excluded because of a bug
|
||||
# https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/2134527
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
ctest --output-on-failure -E "^torture_gssapi_key_exchange_null$"
|
||||
|
||||
|
||||
###############################################################################
|
||||
@@ -576,9 +625,10 @@ tumbleweed/static-analysis:
|
||||
extends: .tests
|
||||
stage: analysis
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD
|
||||
variables:
|
||||
CCC_CC: clang
|
||||
CCC_CXX: clang++
|
||||
script:
|
||||
- export CCC_CC=clang
|
||||
- export CCC_CXX=clang++
|
||||
- scan-build cmake
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
-DCMAKE_C_COMPILER=clang
|
||||
@@ -602,19 +652,12 @@ tumbleweed/static-analysis:
|
||||
freebsd/openssl_1.1.1/x86_64:
|
||||
image:
|
||||
extends: .tests
|
||||
variables:
|
||||
CMAKE_OPTIONS: $CMAKE_DEFAULT_OPTIONS -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DUNIT_TESTING=ON
|
||||
before_script:
|
||||
- mkdir -p obj && cd obj && cmake
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
-DPICKY_DEVELOPER=ON
|
||||
-DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
|
||||
-DUNIT_TESTING=ON ..
|
||||
- mkdir -p obj && cd obj && cmake $CMAKE_OPTIONS ..
|
||||
script:
|
||||
- cmake $CMAKE_DEFAULT_OPTIONS
|
||||
-DWITH_SFTP=ON
|
||||
-DWITH_SERVER=ON
|
||||
-DWITH_ZLIB=ON
|
||||
-DWITH_PCAP=ON
|
||||
-DUNIT_TESTING=ON .. &&
|
||||
- cmake $CMAKE_OPTIONS .. &&
|
||||
make &&
|
||||
ctest --output-on-failure
|
||||
tags:
|
||||
@@ -624,7 +667,6 @@ freebsd/openssl_1.1.1/x86_64:
|
||||
- branches@libssh/libssh-mirror
|
||||
- branches@cryptomilk/libssh-mirror
|
||||
- branches@jjelen/libssh-mirror
|
||||
- branches@marco.fortina/libssh-mirror
|
||||
|
||||
|
||||
###############################################################################
|
||||
@@ -663,13 +705,14 @@ freebsd/openssl_1.1.1/x86_64:
|
||||
- vcpkg install cmocka
|
||||
- vcpkg install openssl
|
||||
- vcpkg install zlib
|
||||
- vcpkg install libfido2
|
||||
- vcpkg integrate install
|
||||
- mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
|
||||
- cmake
|
||||
-A $PLATFORM
|
||||
-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
-DPICKY_DEVELOPER=ON
|
||||
-DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
|
||||
-DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DWITH_FIDO2=ON
|
||||
-DUNIT_TESTING=ON ..
|
||||
|
||||
visualstudio/x86_64:
|
||||
@@ -730,6 +773,7 @@ coverity:
|
||||
# MacOS #
|
||||
###############################################################################
|
||||
.macos:
|
||||
extends: .build_options
|
||||
tags:
|
||||
- saas-macos-medium-m1
|
||||
image: macos-14-xcode-15
|
||||
@@ -742,17 +786,13 @@ coverity:
|
||||
- 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 .. &&
|
||||
|
||||
475
.gitlab-ci/cmocka.cfg
Normal file
@@ -0,0 +1,475 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
cppcheck library configuration for cmocka
|
||||
|
||||
This file provides cppcheck with information about cmocka's assertion functions
|
||||
and their behavior, particularly that assertion failures do not return.
|
||||
|
||||
This helps cppcheck understand that after assert_non_null(ptr), the pointer
|
||||
is guaranteed to be non-NULL, eliminating false positives like:
|
||||
- nullPointerArithmeticOutOfMemory
|
||||
- nullPointer
|
||||
- etc.
|
||||
|
||||
Usage:
|
||||
cppcheck --library=cmocka.cfg [other options] <source files>
|
||||
|
||||
For more information on cppcheck library format, see:
|
||||
https://cppcheck.sourceforge.io/manual.html#library-configuration
|
||||
-->
|
||||
<def format="2">
|
||||
<!-- Core functions -->
|
||||
<function name="_fail">
|
||||
<noreturn>true</noreturn>
|
||||
<arg nr="1" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Boolean assertions -->
|
||||
<function name="_assert_true">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 != 0</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_false">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 == 0</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Pointer assertions -->
|
||||
<function name="_assert_ptr_equal_msg">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 == arg2</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_ptr_not_equal_msg">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 != arg2</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Integer assertions -->
|
||||
<function name="_assert_int_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 == arg2</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_int_not_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 != arg2</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_uint_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 == arg2</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_uint_not_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 != arg2</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Float/double assertions -->
|
||||
<function name="_assert_float_equal">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_float_not_equal">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_double_equal">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_double_not_equal">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- String assertions -->
|
||||
<function name="_assert_string_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_string_not_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Memory assertions -->
|
||||
<function name="_assert_memory_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_memory_not_equal">
|
||||
<arg nr="1" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Range assertions -->
|
||||
<function name="_assert_int_in_range">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg2 <= arg1 && arg1 <= arg3</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_int_not_in_range">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 < arg2 || arg3 < arg1</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_uint_in_range">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg2 <= arg1 && arg1 <= arg3</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_uint_not_in_range">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>arg1 < arg2 || arg3 < arg1</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_float_in_range">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in"/>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_float_not_in_range">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in"/>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Set assertions -->
|
||||
<function name="_assert_int_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_int_not_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_uint_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_uint_not_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_not_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_float_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in"/>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="7" direction="in"/>
|
||||
</function>
|
||||
|
||||
<function name="_assert_float_not_in_set">
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in"/>
|
||||
<arg nr="4" direction="in"/>
|
||||
<arg nr="5" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="6" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="7" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Return code assertion -->
|
||||
<function name="_assert_return_code">
|
||||
<arg nr="1" direction="in">
|
||||
<valid>0 <= arg1</valid>
|
||||
</arg>
|
||||
<arg nr="2" direction="in"/>
|
||||
<arg nr="3" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="4" direction="in">
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="5" direction="in"/>
|
||||
</function>
|
||||
|
||||
<!-- Macro definitions -->
|
||||
<!-- Boolean macros -->
|
||||
<define name="assert_true(c)" value="_assert_true(cast_to_uintmax_type(c), #c, __FILE__, __LINE__)"/>
|
||||
<define name="assert_false(c)" value="_assert_false(cast_to_uintmax_type(c), #c, __FILE__, __LINE__)"/>
|
||||
|
||||
<!-- Pointer macros -->
|
||||
<define name="assert_non_null(c)" value="assert_ptr_not_equal((c), NULL)"/>
|
||||
<define name="assert_non_null_msg(c, msg)" value="assert_ptr_not_equal_msg((c), NULL, (msg))"/>
|
||||
<define name="assert_null(c)" value="assert_ptr_equal((c), NULL)"/>
|
||||
<define name="assert_null_msg(c, msg)" value="assert_ptr_equal_msg((c), NULL, (msg))"/>
|
||||
<define name="assert_ptr_equal(a, b)" value="assert_ptr_equal_msg((a), (b), NULL)"/>
|
||||
<define name="assert_ptr_equal_msg(a, b, msg)" value="_assert_ptr_equal_msg((const void*)(a), (const void*)(b), __FILE__, __LINE__, (msg))"/>
|
||||
<define name="assert_ptr_not_equal(a, b)" value="_assert_ptr_not_equal_msg((const void*)(a), (const void*)(b), __FILE__, __LINE__, NULL)"/>
|
||||
<define name="assert_ptr_not_equal_msg(a, b, msg)" value="_assert_ptr_not_equal_msg((const void*)(a), (const void*)(b), __FILE__, __LINE__, (msg))"/>
|
||||
|
||||
<!-- Integer macros -->
|
||||
<define name="assert_int_equal(a, b)" value="_assert_int_equal(cast_to_intmax_type(a), cast_to_intmax_type(b), __FILE__, __LINE__)"/>
|
||||
<define name="assert_int_not_equal(a, b)" value="_assert_int_not_equal(cast_to_intmax_type(a), cast_to_intmax_type(b), __FILE__, __LINE__)"/>
|
||||
<define name="assert_uint_equal(a, b)" value="_assert_uint_equal(cast_to_uintmax_type(a), cast_to_uintmax_type(b), __FILE__, __LINE__)"/>
|
||||
<define name="assert_uint_not_equal(a, b)" value="_assert_uint_not_equal(cast_to_uintmax_type(a), cast_to_uintmax_type(b), __FILE__, __LINE__)"/>
|
||||
|
||||
<!-- Float/double macros -->
|
||||
<define name="assert_float_equal(a, b, epsilon)" value="_assert_float_equal((float)(a), (float)(b), (float)(epsilon), __FILE__, __LINE__)"/>
|
||||
<define name="assert_float_not_equal(a, b, epsilon)" value="_assert_float_not_equal((float)(a), (float)(b), (float)(epsilon), __FILE__, __LINE__)"/>
|
||||
<define name="assert_double_equal(a, b, epsilon)" value="_assert_double_equal((double)(a), (double)(b), (double)(epsilon), __FILE__, __LINE__)"/>
|
||||
<define name="assert_double_not_equal(a, b, epsilon)" value="_assert_double_not_equal((double)(a), (double)(b), (double)(epsilon), __FILE__, __LINE__)"/>
|
||||
|
||||
<!-- String macros -->
|
||||
<define name="assert_string_equal(a, b)" value="_assert_string_equal((const char*)(a), (const char*)(b), __FILE__, __LINE__)"/>
|
||||
<define name="assert_string_not_equal(a, b)" value="_assert_string_not_equal((const char*)(a), (const char*)(b), __FILE__, __LINE__)"/>
|
||||
|
||||
<!-- Memory macros -->
|
||||
<define name="assert_memory_equal(a, b, size)" value="_assert_memory_equal((const void*)(a), (const void*)(b), size, __FILE__, __LINE__)"/>
|
||||
<define name="assert_memory_not_equal(a, b, size)" value="_assert_memory_not_equal((const void*)(a), (const void*)(b), size, __FILE__, __LINE__)"/>
|
||||
|
||||
<!-- Range macros -->
|
||||
<define name="assert_int_in_range(value, minimum, maximum)" value="_assert_int_in_range(cast_to_intmax_type(value), cast_to_intmax_type(minimum), cast_to_intmax_type(maximum), __FILE__, __LINE__)"/>
|
||||
<define name="assert_int_not_in_range(value, minimum, maximum)" value="_assert_int_not_in_range(cast_to_intmax_type(value), cast_to_intmax_type(minimum), cast_to_intmax_type(maximum), __FILE__, __LINE__)"/>
|
||||
<define name="assert_uint_in_range(value, minimum, maximum)" value="_assert_uint_in_range(cast_to_uintmax_type(value), cast_to_uintmax_type(minimum), cast_to_uintmax_type(maximum), __FILE__, __LINE__)"/>
|
||||
<define name="assert_uint_not_in_range(value, minimum, maximum)" value="_assert_uint_not_in_range(cast_to_uintmax_type(value), cast_to_uintmax_type(minimum), cast_to_uintmax_type(maximum), __FILE__, __LINE__)"/>
|
||||
<define name="assert_in_range(value, minimum, maximum)" value="assert_uint_in_range(value, minimum, maximum)"/>
|
||||
<define name="assert_not_in_range(value, minimum, maximum)" value="assert_uint_not_in_range(value, minimum, maximum)"/>
|
||||
<define name="assert_float_in_range(value, minimum, maximum, epsilon)" value="_assert_float_in_range((double)(value), (double)(minimum), (double)(maximum), (double)(epsilon), __FILE__, __LINE__)"/>
|
||||
<define name="assert_float_not_in_range(value, minimum, maximum, epsilon)" value="_assert_float_not_in_range((double)(value), (double)(minimum), (double)(maximum), (double)(epsilon), __FILE__, __LINE__)"/>
|
||||
|
||||
<!-- Set macros -->
|
||||
<define name="assert_in_set(value, values, number_of_values)" value="_assert_not_in_set(cast_to_uintmax_type(value), (uintmax_t*)(values), number_of_values, __FILE__, __LINE__, 0)"/>
|
||||
<define name="assert_not_in_set(value, values, number_of_values)" value="_assert_not_in_set(cast_to_uintmax_type(value), (uintmax_t*)(values), number_of_values, __FILE__, __LINE__, 1)"/>
|
||||
<define name="assert_int_in_set(value, values, number_of_values)" value="_assert_int_in_set(cast_to_intmax_type(value), (intmax_t*)(values), number_of_values, __FILE__, __LINE__, 0)"/>
|
||||
<define name="assert_int_not_in_set(value, values, number_of_values)" value="_assert_int_not_in_set(cast_to_intmax_type(value), (intmax_t*)(values), number_of_values, __FILE__, __LINE__, 1)"/>
|
||||
<define name="assert_uint_in_set(value, values, number_of_values)" value="_assert_uint_in_set(cast_to_uintmax_type(value), (uintmax_t*)(values), number_of_values, __FILE__, __LINE__, 0)"/>
|
||||
<define name="assert_uint_not_in_set(value, values, number_of_values)" value="_assert_uint_not_in_set(cast_to_uintmax_type(value), (uintmax_t*)(values), number_of_values, __FILE__, __LINE__, 1)"/>
|
||||
<define name="assert_float_in_set(value, values, number_of_values, epsilon)" value="_assert_float_in_set((double)(value), (double*)(values), number_of_values, (double)(epsilon), __FILE__, __LINE__, 0)"/>
|
||||
<define name="assert_float_not_in_set(value, values, number_of_values, epsilon)" value="_assert_float_not_in_set((double)(value), (double*)(values), number_of_values, (double)(epsilon), __FILE__, __LINE__, 1)"/>
|
||||
|
||||
<!-- Return code macro -->
|
||||
<define name="assert_return_code(rc, error)" value="_assert_return_code(cast_to_intmax_type(rc), (int32_t)(error), #rc, __FILE__, __LINE__)"/>
|
||||
</def>
|
||||
116
.gitlab-ci/local-ci.sh
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
RED="\033[1;31m"
|
||||
GREEN="\033[1;32m"
|
||||
YELLOW="\033[1;33m"
|
||||
BLUE="\033[1;34m"
|
||||
RESET="\033[0m"
|
||||
|
||||
export GCL_IGNORE_PREDEFINED_VARS=CI_REGISTRY
|
||||
|
||||
BASE_SHA=$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD~1)
|
||||
|
||||
COMMON_ARGS=(
|
||||
--variable "CI_MERGE_REQUEST_DIFF_BASE_SHA=$BASE_SHA"
|
||||
--variable "CI_REGISTRY=registry.gitlab.com"
|
||||
--json-schema-validation=false
|
||||
)
|
||||
|
||||
check_requirements() {
|
||||
for cmd in docker git gitlab-ci-local; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo -e "${RED}Missing dependency: $cmd${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}Found: $cmd${RESET}"
|
||||
done
|
||||
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo -e "${RED}Docker daemon is not running or permission denied${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
list_jobs() {
|
||||
gitlab-ci-local --list --json-schema-validation=false | awk 'NR>1 {print $1}'
|
||||
}
|
||||
|
||||
run_job() {
|
||||
JOB="$1"
|
||||
echo -e "${YELLOW}Running CI job: $JOB${RESET}"
|
||||
gitlab-ci-local "$JOB" "${COMMON_ARGS[@]}"
|
||||
}
|
||||
|
||||
cleanup_images() {
|
||||
echo -e "${BLUE}Removing libssh CI images only...${RESET}"
|
||||
docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" \
|
||||
| grep "$CI_REGISTRY/$BUILD_IMAGES_PROJECT" \
|
||||
| awk '{print $2}' \
|
||||
| xargs -r docker rmi -f
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo
|
||||
echo -e "${BLUE}Usage:${RESET}"
|
||||
echo " $0 --list"
|
||||
echo " $0 --run <job-name>"
|
||||
echo " $0 --all"
|
||||
echo " $0 --run <job-name> --clean"
|
||||
echo " $0 --all --clean"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_requirements
|
||||
|
||||
CLEAN=0
|
||||
MODE=""
|
||||
JOB=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--list)
|
||||
MODE="list"
|
||||
shift
|
||||
;;
|
||||
--run)
|
||||
MODE="run"
|
||||
JOB="$2"
|
||||
shift 2
|
||||
;;
|
||||
--all)
|
||||
MODE="all"
|
||||
shift
|
||||
;;
|
||||
--clean)
|
||||
CLEAN=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$MODE" in
|
||||
list)
|
||||
list_jobs
|
||||
;;
|
||||
run)
|
||||
[[ -z "$JOB" ]] && usage
|
||||
run_job "$JOB"
|
||||
[[ "$CLEAN" -eq 1 ]] && cleanup_images
|
||||
;;
|
||||
all)
|
||||
for job in $(list_jobs); do
|
||||
run_job "$job"
|
||||
[[ "$CLEAN" -eq 1 ]] && cleanup_images
|
||||
done
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "${GREEN}Done.${RESET}"
|
||||
@@ -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()
|
||||
@@ -78,9 +78,19 @@ if (WITH_NACL)
|
||||
endif (NOT NACL_FOUND)
|
||||
endif (WITH_NACL)
|
||||
|
||||
if (WITH_FIDO2)
|
||||
find_package(libfido2)
|
||||
if (LIBFIDO2_FOUND)
|
||||
set(HAVE_LIBFIDO2 ON)
|
||||
else (LIBFIDO2_FOUND)
|
||||
set(HAVE_LIBFIDO2 OFF)
|
||||
message(WARNING "libfido2 was not found. Internal support for interacting with FIDO2/U2F devices using the USB HID protocol will not be available.")
|
||||
endif (LIBFIDO2_FOUND)
|
||||
endif (WITH_FIDO2)
|
||||
|
||||
# Disable symbol versioning in non UNIX platforms
|
||||
if (UNIX)
|
||||
find_package(ABIMap 0.3.1)
|
||||
find_package(ABIMap 0.4.0)
|
||||
else (UNIX)
|
||||
set(WITH_SYMBOL_VERSIONING OFF)
|
||||
endif (UNIX)
|
||||
@@ -171,6 +181,10 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
|
||||
set(ALLOW_ABI_BREAK "BREAK_ABI")
|
||||
endif()
|
||||
|
||||
if (WITH_FINAL)
|
||||
set(FINAL "FINAL")
|
||||
endif()
|
||||
|
||||
# Target we can depend on in 'make dist'
|
||||
set(_SYMBOL_TARGET "${PROJECT_NAME}.map")
|
||||
|
||||
@@ -183,7 +197,7 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
|
||||
RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION}
|
||||
CURRENT_MAP ${MAP_PATH}
|
||||
COPY_TO ${MAP_PATH}
|
||||
FINAL
|
||||
${FINAL}
|
||||
${ALLOW_ABI_BREAK})
|
||||
|
||||
# Write the current version to the source
|
||||
@@ -199,7 +213,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
|
||||
@@ -238,6 +252,10 @@ message(STATUS "Client code testing: ${CLIENT_TESTING}")
|
||||
message(STATUS "Blowfish cipher support: ${HAVE_BLOWFISH}")
|
||||
message(STATUS "PKCS #11 URI support: ${WITH_PKCS11_URI}")
|
||||
message(STATUS "With PKCS #11 provider support: ${WITH_PKCS11_PROVIDER}")
|
||||
message(STATUS "With FIDO2/U2F support: ${WITH_FIDO2}")
|
||||
if (WITH_FIDO2)
|
||||
message(STATUS "With libfido2 (internal usb-hid support): ${HAVE_LIBFIDO2}")
|
||||
endif (WITH_FIDO2)
|
||||
set(_SERVER_TESTING OFF)
|
||||
if (WITH_SERVER)
|
||||
set(_SERVER_TESTING ${SERVER_TESTING})
|
||||
|
||||
@@ -137,6 +137,33 @@ 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.
|
||||
|
||||
## Running GitLab CI locally (optional helper)
|
||||
|
||||
For contributors working on CI, build system changes, or adding new CI jobs, it can be useful to run GitLab CI pipelines locally before pushing.
|
||||
|
||||
libssh provides a small helper script based on `gitlab-ci-local` that can:
|
||||
|
||||
- List all jobs defined in `.gitlab-ci.yml`
|
||||
- Run a specific job or the full pipeline locally
|
||||
- Automatically pick up new jobs when they are added to the CI configuration
|
||||
- Optionally clean up CI Docker images after execution
|
||||
|
||||
### Requirements
|
||||
|
||||
- Docker (daemon running)
|
||||
- git
|
||||
- gitlab-ci-local
|
||||
https://github.com/firecow/gitlab-ci-local
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
./.gitlab-ci/local-ci.sh --list
|
||||
./.gitlab-ci/local-ci.sh --run fedora/libressl/x86_64
|
||||
./.gitlab-ci/local-ci.sh --all
|
||||
./.gitlab-ci/local-ci.sh --run fedora/libressl/x86_64 --clean
|
||||
```
|
||||
|
||||
# Coding conventions in the libssh tree
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -104,6 +104,12 @@ 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-KEM availability (OpenSSL 3.5+)
|
||||
if (OPENSSL_VERSION VERSION_GREATER_EQUAL "3.5.0")
|
||||
set(HAVE_OPENSSL_MLKEM 1)
|
||||
set(HAVE_MLKEM1024 1)
|
||||
endif ()
|
||||
|
||||
unset(CMAKE_REQUIRED_INCLUDES)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
endif()
|
||||
@@ -134,6 +140,7 @@ check_function_exists(strncpy HAVE_STRNCPY)
|
||||
check_function_exists(strndup HAVE_STRNDUP)
|
||||
check_function_exists(strtoull HAVE_STRTOULL)
|
||||
check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
|
||||
check_function_exists(memset_explicit HAVE_MEMSET_EXPLICIT)
|
||||
check_function_exists(memset_s HAVE_MEMSET_S)
|
||||
|
||||
if (HAVE_GLOB_H)
|
||||
@@ -228,6 +235,10 @@ if (GCRYPT_FOUND)
|
||||
set(HAVE_GCRYPT_CHACHA_POLY 1)
|
||||
set(HAVE_GCRYPT_CURVE25519 1)
|
||||
endif (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
|
||||
if (GCRYPT_VERSION VERSION_GREATER_EQUAL "1.10.1")
|
||||
set(HAVE_GCRYPT_MLKEM 1)
|
||||
set(HAVE_MLKEM1024 1)
|
||||
endif ()
|
||||
endif (GCRYPT_FOUND)
|
||||
|
||||
if (MBEDTLS_FOUND)
|
||||
|
||||
@@ -13,6 +13,7 @@ option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
option(WITH_PKCS11_URI "Build with PKCS#11 URI support" OFF)
|
||||
option(WITH_PKCS11_PROVIDER "Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
|
||||
option(WITH_FIDO2 "Build with FIDO2/U2F support" OFF)
|
||||
option(UNIT_TESTING "Build with unit tests" OFF)
|
||||
option(CLIENT_TESTING "Build with client tests; requires openssh" OFF)
|
||||
option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" OFF)
|
||||
|
||||
63
cmake/Modules/Findlibfido2.cmake
Normal file
@@ -0,0 +1,63 @@
|
||||
# - Try to find libfido2
|
||||
# Once done this will define
|
||||
#
|
||||
# LIBFIDO2_ROOT_DIR - Set this variable to the root installation of libfido2
|
||||
#
|
||||
# Read-Only variables:
|
||||
# LIBFIDO2_FOUND - system has libfido2
|
||||
# LIBFIDO2_INCLUDE_DIR - the libfido2 include directory
|
||||
# LIBFIDO2_LIBRARIES - Link these to use libfido2
|
||||
#
|
||||
# The libfido2 library provides support for communicating
|
||||
# with FIDO2/U2F devices over USB/NFC.
|
||||
#
|
||||
# Copyright (c) 2025 Praneeth Sarode <praneethsarode@gmail.com>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
|
||||
|
||||
set(_LIBFIDO2_ROOT_HINTS
|
||||
$ENV{LIBFIDO2_ROOT_DIR}
|
||||
${LIBFIDO2_ROOT_DIR}
|
||||
)
|
||||
|
||||
set(_LIBFIDO2_ROOT_PATHS
|
||||
"$ENV{PROGRAMFILES}/libfido2"
|
||||
)
|
||||
|
||||
set(_LIBFIDO2_ROOT_HINTS_AND_PATHS
|
||||
HINTS ${_LIBFIDO2_ROOT_HINTS}
|
||||
PATHS ${_LIBFIDO2_ROOT_PATHS}
|
||||
)
|
||||
|
||||
find_path(LIBFIDO2_INCLUDE_DIR
|
||||
NAMES
|
||||
fido.h
|
||||
HINTS
|
||||
${_LIBFIDO2_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
include
|
||||
)
|
||||
|
||||
find_library(LIBFIDO2_LIBRARY
|
||||
NAMES
|
||||
fido2
|
||||
HINTS
|
||||
${_LIBFIDO2_ROOT_HINTS_AND_PATHS}
|
||||
PATH_SUFFIXES
|
||||
lib
|
||||
lib64
|
||||
)
|
||||
|
||||
set(LIBFIDO2_LIBRARIES
|
||||
${LIBFIDO2_LIBRARY}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(libfido2 DEFAULT_MSG LIBFIDO2_LIBRARIES LIBFIDO2_INCLUDE_DIR)
|
||||
|
||||
# show the LIBFIDO2_INCLUDE_DIR and LIBFIDO2_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(LIBFIDO2_INCLUDE_DIR LIBFIDO2_LIBRARIES)
|
||||
@@ -179,6 +179,9 @@
|
||||
/* Define to 1 if you have the `explicit_bzero' function. */
|
||||
#cmakedefine HAVE_EXPLICIT_BZERO 1
|
||||
|
||||
/* Define to 1 if you have the `memset_explicit' function. */
|
||||
#cmakedefine HAVE_MEMSET_EXPLICIT 1
|
||||
|
||||
/* Define to 1 if you have the `memset_s' function. */
|
||||
#cmakedefine HAVE_MEMSET_S 1
|
||||
|
||||
@@ -191,6 +194,15 @@
|
||||
/* Define to 1 if we have support for blowfish */
|
||||
#cmakedefine HAVE_BLOWFISH 1
|
||||
|
||||
/* Define to 1 if we have support for ML-KEM in libgcrypt */
|
||||
#cmakedefine HAVE_GCRYPT_MLKEM 1
|
||||
|
||||
/* Define to 1 if we have support for ML-KEM in OpenSSL */
|
||||
#cmakedefine HAVE_OPENSSL_MLKEM 1
|
||||
|
||||
/* Define to 1 if we have support for ML-KEM1024 in either backend */
|
||||
#cmakedefine HAVE_MLKEM1024 1
|
||||
|
||||
/*************************** LIBRARIES ***************************/
|
||||
|
||||
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||
@@ -208,6 +220,10 @@
|
||||
/* Define to 1 if you have the `cmocka' library (-lcmocka). */
|
||||
#cmakedefine HAVE_CMOCKA 1
|
||||
|
||||
/* Define to 1 if you have the `libfido2' library (-lfido2).
|
||||
* This is required for interacting with FIDO2/U2F devices over USB-HID. */
|
||||
#cmakedefine HAVE_LIBFIDO2 1
|
||||
|
||||
/**************************** OPTIONS ****************************/
|
||||
|
||||
#cmakedefine HAVE_GCC_THREAD_LOCAL_STORAGE 1
|
||||
@@ -274,6 +290,9 @@
|
||||
/* Define to 1 if we want to build a support for PKCS #11 provider. */
|
||||
#cmakedefine WITH_PKCS11_PROVIDER 1
|
||||
|
||||
/* Define to 1 if you want to enable FIDO2/U2F support */
|
||||
#cmakedefine WITH_FIDO2 1
|
||||
|
||||
/*************************** ENDIAN *****************************/
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
|
||||
@@ -1,91 +1,247 @@
|
||||
#
|
||||
# Build the documentation
|
||||
#
|
||||
if (${CMAKE_VERSION} VERSION_GREATER "3.8.99")
|
||||
|
||||
# To build the documentation with a local doxygen-awesome-css directory:
|
||||
#
|
||||
# cmake -S . -B obj \
|
||||
# -DDOXYGEN_AWESOME_CSS_DIR=/path/to/doxygen-awesome-css
|
||||
# cmake --build obj --target docs
|
||||
#
|
||||
# The tarball can be downloaded from:
|
||||
# https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz
|
||||
#
|
||||
find_package(Doxygen)
|
||||
|
||||
if (DOXYGEN_FOUND)
|
||||
set(DOXYGEN_AWESOME_CSS_PROJECT
|
||||
"https://github.com/jothepro/doxygen-awesome-css")
|
||||
set(DOXYGEN_AWESOME_CSS_VERSION "2.4.1")
|
||||
set(DOXYGEN_AWESOME_CSS_URL
|
||||
"${DOXYGEN_AWESOME_CSS_PROJECT}/archive/refs/tags/v${DOXYGEN_AWESOME_CSS_VERSION}.tar.gz"
|
||||
)
|
||||
|
||||
# Allow specifying a local doxygen-awesome-css directory (useful for
|
||||
# packaging)
|
||||
if (NOT DEFINED DOXYGEN_AWESOME_CSS_DIR)
|
||||
# Custom target to download doxygen-awesome-css at build time
|
||||
add_custom_target(
|
||||
doxygen-awesome-css
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -DURL=${DOXYGEN_AWESOME_CSS_URL}
|
||||
-DDEST_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
-DVERSION=${DOXYGEN_AWESOME_CSS_VERSION} -P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fetch_doxygen_awesome.cmake
|
||||
COMMENT "Fetching doxygen-awesome-css theme")
|
||||
|
||||
set(AWESOME_CSS_DIR
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/doxygen-awesome-css-${DOXYGEN_AWESOME_CSS_VERSION}"
|
||||
)
|
||||
else ()
|
||||
message(
|
||||
STATUS
|
||||
"Using doxygen-awesome-css from ${DOXYGEN_AWESOME_CSS_DIR}")
|
||||
set(AWESOME_CSS_DIR "${DOXYGEN_AWESOME_CSS_DIR}")
|
||||
endif ()
|
||||
|
||||
# Project title shown in documentation
|
||||
set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME})
|
||||
# Project version number shown in documentation
|
||||
set(DOXYGEN_PROJECT_NUMBER ${PROJECT_VERSION})
|
||||
# Brief description shown below project name
|
||||
set(DOXYGEN_PROJECT_BRIEF "The SSH library")
|
||||
# Project favicon (browser tab icon)
|
||||
set(DOXYGEN_PROJECT_ICON ${CMAKE_CURRENT_SOURCE_DIR}/favicon.png)
|
||||
|
||||
# Number of spaces used for indentation in code blocks
|
||||
set(DOXYGEN_TAB_SIZE 4)
|
||||
# Generate output optimized for C (vs C++)
|
||||
set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES)
|
||||
# Enable parsing of markdown in comments
|
||||
set(DOXYGEN_MARKDOWN_SUPPORT YES)
|
||||
set(DOXYGEN_FULL_PATH_NAMES NO)
|
||||
set(DOXYGEN_GENERATE_TAGFILE "tags.xml")
|
||||
# Warn about undocumented members to improve documentation quality
|
||||
set(DOXYGEN_WARN_IF_UNDOCUMENTED YES)
|
||||
# Do not extract private class members
|
||||
set(DOXYGEN_EXTRACT_PRIVATE NO)
|
||||
if (WITH_INTERNAL_DOC)
|
||||
# Include internal documentation
|
||||
set(DOXYGEN_INTERNAL_DOCS YES)
|
||||
else ()
|
||||
# Do not include internal documentation
|
||||
set(DOXYGEN_INTERNAL_DOCS NO)
|
||||
endif( WITH_INTERNAL_DOC)
|
||||
# Disable built-in clipboard (using doxygen-awesome extension instead)
|
||||
set(DOXYGEN_HTML_COPY_CLIPBOARD NO)
|
||||
# Disable page outline panel (using interactive TOC extension instead)
|
||||
set(DOXYGEN_PAGE_OUTLINE_PANEL NO)
|
||||
|
||||
set(DOXYGEN_PREDEFINED DOXYGEN
|
||||
WITH_SERVER
|
||||
WITH_SFTP
|
||||
# Required configuration for doxygen-awesome-css theme Generate treeview
|
||||
# sidebar for navigation
|
||||
set(DOXYGEN_GENERATE_TREEVIEW YES)
|
||||
# Enable default index pages
|
||||
set(DOXYGEN_DISABLE_INDEX NO)
|
||||
# Use top navigation bar instead of full sidebar (required for theme
|
||||
# compatibility)
|
||||
set(DOXYGEN_FULL_SIDEBAR NO)
|
||||
# Use light color style (required for Doxygen >= 1.9.5)
|
||||
set(DOXYGEN_HTML_COLORSTYLE LIGHT)
|
||||
|
||||
# Disable diagram generation (not relevant for C projects)
|
||||
set(DOXYGEN_HAVE_DOT NO)
|
||||
set(DOXYGEN_CLASS_DIAGRAMS NO)
|
||||
set(DOXYGEN_CALL_GRAPH NO)
|
||||
set(DOXYGEN_CALLER_GRAPH NO)
|
||||
|
||||
# Preprocessor defines to use when parsing code
|
||||
set(DOXYGEN_PREDEFINED DOXYGEN WITH_SERVER WITH_SFTP
|
||||
PRINTF_ATTRIBUTE\(x,y\))
|
||||
set(DOXYGEN_DOT_GRAPH_MAX_NODES 100)
|
||||
|
||||
set(DOXYGEN_EXCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/that_style)
|
||||
set(DOXYGEN_HTML_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/that_style/header.html)
|
||||
set(DOXYGEN_HTML_EXTRA_STYLESHEET ${CMAKE_CURRENT_SOURCE_DIR}/that_style/that_style.css)
|
||||
set(DOXYGEN_HTML_EXTRA_FILES ${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/nav_edge_left.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/nav_edge_right.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/nav_edge_inter.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/sync_off.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/sync_on.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/splitbar_handle.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/doc.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/mag_glass.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/folderclosed.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/img/folderopen.svg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/that_style/js/striped_bg.js)
|
||||
set(DOXYGEN_EXCLUDE_PATTERNS */src/external/* fe25519.h ge25519.h sc25519.h
|
||||
blf.h)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS chacha20_poly1305_keysched,dh_ctx,dh_ctx,dh_keypair,error_struct,
|
||||
packet_struct,pem_get_password_struct,ssh_tokens_st,
|
||||
sftp_attributes_struct,sftp_client_message_struct,
|
||||
sftp_dir_struct,sftp_ext_struct,sftp_file_struct,sftp_message_struct,
|
||||
sftp_packet_struct,sftp_request_queue_struct,sftp_session_struct,
|
||||
sftp_status_message_struct,ssh_agent_state_struct,
|
||||
ssh_agent_struct,ssh_auth_auto_state_struct,ssh_auth_request,
|
||||
ssh_bind_config_keyword_table_s,ssh_bind_config_match_keyword_table_s,
|
||||
ssh_bind_struct,ssh_buffer_struct,ssh_channel_callbacks_struct,
|
||||
ssh_channel_read_termination_struct,ssh_channel_request,
|
||||
ssh_channel_request_open,ssh_channel_struct,ssh_cipher_struct,
|
||||
ssh_common_struct,ssh_config_keyword_table_s,
|
||||
ssh_config_match_keyword_table_s,ssh_connector_struct,
|
||||
ssh_counter_struct,ssh_crypto_struct,ssh_event_fd_wrapper,
|
||||
ssh_event_struct,ssh_global_request,ssh_gssapi_struct,ssh_hmac_struct,
|
||||
ssh_iterator,ssh_kbdint_struct,ssh_kex_struct,ssh_key_struct,
|
||||
ssh_knownhosts_entry,ssh_list,ssh_mac_ctx_struct,ssh_message_struct,
|
||||
ssh_packet_callbacks_struct,ssh_packet_header,ssh_poll_ctx_struct,
|
||||
ssh_poll_handle_struct,ssh_pollfd_struct,ssh_private_key_struct,
|
||||
ssh_public_key_struct,ssh_scp_struct,ssh_service_request,
|
||||
ssh_session_struct,ssh_signature_struct,ssh_socket_struct,
|
||||
ssh_string_struct,ssh_threads_callbacks_struct,ssh_timestamp,)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS_MACRO SSH_FXP*,SSH_SOCKET*,SERVERBANNER,SOCKOPT_TYPE_ARG4,SSH_FILEXFER*,
|
||||
SSH_FXF*,SSH_S_*,SFTP_*,NSS_BUFLEN_PASSWD,CLOCK,MAX_LINE_SIZE,
|
||||
PKCS11_URI,KNOWNHOSTS_MAXTYPES,)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS sftp_attributes,sftp_client_message,sftp_dir,sftp_ext,sftp_file,
|
||||
sftp_message,sftp_packet,sftp_request_queue,sftp_session,
|
||||
sftp_status_message,sftp_statvfs_t,poll_fn,ssh_callback_int,
|
||||
ssh_callback_data,ssh_callback_int_int,ssh_message_callback,
|
||||
ssh_channel_callback_int,ssh_channel_callback_data,ssh_callbacks,
|
||||
ssh_gssapi_select_oid_callback,ssh_gssapi_accept_sec_ctx_callback,
|
||||
ssh_gssapi_verify_mic_callback,ssh_server_callbacks,ssh_socket_callbacks,
|
||||
ssh_packet_callbacks,ssh_channel_callbacks,ssh_bind,ssh_bind_callbacks,)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS ${DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS}
|
||||
${DOXYGEN_EXCLUDE_SYMBOLS_MACRO}
|
||||
${DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS})
|
||||
# Exclude patterns for files we don't want to document
|
||||
set(DOXYGEN_EXCLUDE_PATTERNS */src/external/* fe25519.h ge25519.h sc25519.h
|
||||
blf.h)
|
||||
# Exclude internal structures from documentation
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS
|
||||
chacha20_poly1305_keysched,
|
||||
dh_ctx,
|
||||
dh_ctx,
|
||||
dh_keypair,
|
||||
error_struct,
|
||||
packet_struct,
|
||||
pem_get_password_struct,
|
||||
ssh_tokens_st,
|
||||
sftp_attributes_struct,
|
||||
sftp_client_message_struct,
|
||||
sftp_dir_struct,
|
||||
sftp_ext_struct,
|
||||
sftp_file_struct,
|
||||
sftp_message_struct,
|
||||
sftp_packet_struct,
|
||||
sftp_request_queue_struct,
|
||||
sftp_session_struct,
|
||||
sftp_status_message_struct,
|
||||
ssh_agent_state_struct,
|
||||
ssh_agent_struct,
|
||||
ssh_auth_auto_state_struct,
|
||||
ssh_auth_request,
|
||||
ssh_bind_config_keyword_table_s,
|
||||
ssh_bind_config_match_keyword_table_s,
|
||||
ssh_bind_struct,
|
||||
ssh_buffer_struct,
|
||||
ssh_channel_callbacks_struct,
|
||||
ssh_channel_read_termination_struct,
|
||||
ssh_channel_request,
|
||||
ssh_channel_request_open,
|
||||
ssh_channel_struct,
|
||||
ssh_cipher_struct,
|
||||
ssh_common_struct,
|
||||
ssh_config_keyword_table_s,
|
||||
ssh_config_match_keyword_table_s,
|
||||
ssh_connector_struct,
|
||||
ssh_counter_struct,
|
||||
ssh_crypto_struct,
|
||||
ssh_event_fd_wrapper,
|
||||
ssh_event_struct,
|
||||
ssh_global_request,
|
||||
ssh_gssapi_struct,
|
||||
ssh_hmac_struct,
|
||||
ssh_iterator,
|
||||
ssh_kbdint_struct,
|
||||
ssh_kex_struct,
|
||||
ssh_key_struct,
|
||||
ssh_knownhosts_entry,
|
||||
ssh_list,
|
||||
ssh_mac_ctx_struct,
|
||||
ssh_message_struct,
|
||||
ssh_packet_callbacks_struct,
|
||||
ssh_packet_header,
|
||||
ssh_poll_ctx_struct,
|
||||
ssh_poll_handle_struct,
|
||||
ssh_pollfd_struct,
|
||||
ssh_private_key_struct,
|
||||
ssh_public_key_struct,
|
||||
ssh_scp_struct,
|
||||
ssh_service_request,
|
||||
ssh_session_struct,
|
||||
ssh_signature_struct,
|
||||
ssh_socket_struct,
|
||||
ssh_string_struct,
|
||||
ssh_threads_callbacks_struct,
|
||||
ssh_timestamp)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS_MACRO
|
||||
SSH_FXP*,
|
||||
SSH_SOCKET*,
|
||||
SERVERBANNER,
|
||||
SOCKOPT_TYPE_ARG4,
|
||||
SSH_FILEXFER*,
|
||||
SSH_FXF*,
|
||||
SSH_S_*,
|
||||
SFTP_*,
|
||||
NSS_BUFLEN_PASSWD,
|
||||
CLOCK,
|
||||
MAX_LINE_SIZE,
|
||||
PKCS11_URI,
|
||||
KNOWNHOSTS_MAXTYPES)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS
|
||||
sftp_attributes,
|
||||
sftp_client_message,
|
||||
sftp_dir,
|
||||
sftp_ext,
|
||||
sftp_file,
|
||||
sftp_message,
|
||||
sftp_packet,
|
||||
sftp_request_queue,
|
||||
sftp_status_message,
|
||||
sftp_statvfs_t,
|
||||
poll_fn,
|
||||
ssh_callback_int,
|
||||
ssh_callback_data,
|
||||
ssh_callback_int_int,
|
||||
ssh_message_callback,
|
||||
ssh_channel_callback_int,
|
||||
ssh_channel_callback_data,
|
||||
ssh_callbacks,
|
||||
ssh_gssapi_select_oid_callback,
|
||||
ssh_gssapi_accept_sec_ctx_callback,
|
||||
ssh_gssapi_verify_mic_callback,
|
||||
ssh_server_callbacks,
|
||||
ssh_socket_callbacks,
|
||||
ssh_packet_callbacks,
|
||||
ssh_channel_callbacks,
|
||||
ssh_bind,
|
||||
ssh_bind_callbacks)
|
||||
set(DOXYGEN_EXCLUDE_SYMBOLS
|
||||
${DOXYGEN_EXCLUDE_SYMBOLS_STRUCTS} ${DOXYGEN_EXCLUDE_SYMBOLS_MACRO}
|
||||
${DOXYGEN_EXCLUDE_SYMBOLS_TYPEDEFS})
|
||||
|
||||
# Custom layout file to rename "Topics" to "API Reference" and simplify
|
||||
# navigation
|
||||
set(DOXYGEN_LAYOUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/DoxygenLayout.xml)
|
||||
# Custom HTML header with doxygen-awesome extension initialization
|
||||
set(DOXYGEN_HTML_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/header.html)
|
||||
# Modern CSS theme for documentation with custom libssh.org color scheme
|
||||
set(DOXYGEN_HTML_EXTRA_STYLESHEET
|
||||
${AWESOME_CSS_DIR}/doxygen-awesome.css
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/doxygen-custom.css)
|
||||
# JavaScript extensions: dark mode toggle, copy button, paragraph links,
|
||||
# interactive TOC
|
||||
set(DOXYGEN_HTML_EXTRA_FILES
|
||||
${AWESOME_CSS_DIR}/doxygen-awesome-darkmode-toggle.js
|
||||
${AWESOME_CSS_DIR}/doxygen-awesome-fragment-copy-button.js
|
||||
${AWESOME_CSS_DIR}/doxygen-awesome-paragraph-link.js
|
||||
${AWESOME_CSS_DIR}/doxygen-awesome-interactive-toc.js)
|
||||
|
||||
# This updates the Doxyfile if we do changes here
|
||||
set(_doxyfile_template "${CMAKE_BINARY_DIR}/CMakeDoxyfile.in")
|
||||
set(_target_doxyfile "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.docs")
|
||||
configure_file("${_doxyfile_template}" "${_target_doxyfile}")
|
||||
|
||||
doxygen_add_docs(docs
|
||||
${CMAKE_SOURCE_DIR}/include/libssh
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
doxygen_add_docs(docs ${CMAKE_SOURCE_DIR}/include/libssh
|
||||
${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_custom_target(docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh ${CMAKE_BINARY_DIR})
|
||||
endif() # DOXYGEN_FOUND
|
||||
# Make docs depend on doxygen-awesome-css download (if not using local dir)
|
||||
if (TARGET doxygen-awesome-css)
|
||||
add_dependencies(docs doxygen-awesome-css)
|
||||
endif ()
|
||||
|
||||
endif() # CMAKE_VERSION
|
||||
add_custom_target(
|
||||
docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh
|
||||
${CMAKE_BINARY_DIR})
|
||||
endif (DOXYGEN_FOUND)
|
||||
|
||||
242
doc/DoxygenLayout.xml
Normal file
@@ -0,0 +1,242 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doxygenlayout version="2.0">
|
||||
<!-- Generated by doxygen 1.14.0 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="topics" visible="yes" title="API Reference" intro=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="structs" visible="yes" title="">
|
||||
<tab type="structlist" visible="yes" title="" intro=""/>
|
||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
</tab>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="yes"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes visible="yes" title=""/>
|
||||
<services visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicmethods visible="yes" title=""/>
|
||||
<publicstaticmethods visible="yes" title=""/>
|
||||
<publicattributes visible="yes" title=""/>
|
||||
<publicstaticattributes visible="yes" title=""/>
|
||||
<protectedtypes visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<protectedmethods visible="yes" title=""/>
|
||||
<protectedstaticmethods visible="yes" title=""/>
|
||||
<protectedattributes visible="yes" title=""/>
|
||||
<protectedstaticattributes visible="yes" title=""/>
|
||||
<packagetypes visible="yes" title=""/>
|
||||
<packagemethods visible="yes" title=""/>
|
||||
<packagestaticmethods visible="yes" title=""/>
|
||||
<packageattributes visible="yes" title=""/>
|
||||
<packagestaticattributes visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<privatetypes visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<privatemethods visible="yes" title=""/>
|
||||
<privatestaticmethods visible="yes" title=""/>
|
||||
<privateattributes visible="yes" title=""/>
|
||||
<privatestaticattributes visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
<related visible="yes" title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<services visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<constructors visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<related visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a concept page -->
|
||||
<concept>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<definition visible="yes" title=""/>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<authorsection visible="yes"/>
|
||||
</concept>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<enumvalues visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<enumvalues visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a C++20 module page -->
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<membergroups visible="yes" title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</module>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
127
doc/doxygen-custom.css
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Custom color scheme for libssh documentation
|
||||
* Based on libssh.org color palette
|
||||
*/
|
||||
|
||||
html {
|
||||
/* Primary colors - using libssh.org orange accent */
|
||||
--primary-color: #F78C40;
|
||||
--primary-dark-color: #f57900;
|
||||
--primary-light-color: #fab889;
|
||||
|
||||
/* Accent color - neutral gray */
|
||||
--primary-lighter-color: #5A5A5A;
|
||||
|
||||
/* Page colors - clean white background */
|
||||
--page-background-color: #ffffff;
|
||||
--page-foreground-color: #333333;
|
||||
--page-secondary-foreground-color: #666666;
|
||||
|
||||
/* Links - use the warm orange color */
|
||||
--link-color: #F78C40;
|
||||
--link-hover-color: #f0690a;
|
||||
|
||||
/* Code blocks and fragments - very light background */
|
||||
--code-background: #f9f9f9;
|
||||
--fragment-background: #f9f9f9;
|
||||
|
||||
/* Borders - subtle light grey */
|
||||
--separator-color: #e0e0e0;
|
||||
--border-light-color: #f0f0f0;
|
||||
|
||||
/* Side navigation - pure white */
|
||||
--side-nav-background: #ffffff;
|
||||
|
||||
/* Menu colors - warm orange accent */
|
||||
--menu-selected-background: #F78C40;
|
||||
|
||||
/* Tables and boxes - lighter */
|
||||
--tablehead-background: #fbc7a2;
|
||||
--tablehead-foreground: #333333;
|
||||
}
|
||||
|
||||
/* Header styling with libssh brand colors */
|
||||
#titlearea {
|
||||
background-color: #5A5A5A;
|
||||
background-image: linear-gradient(to right, #5A5A5A, #6a6a6a);
|
||||
border-bottom: 3px solid #F78C40;
|
||||
}
|
||||
|
||||
#projectname {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
#projectbrief {
|
||||
color: #fab889 !important;
|
||||
}
|
||||
|
||||
/* Top navigation tabs */
|
||||
#top {
|
||||
background: linear-gradient(to bottom, #5A5A5A 0%, #6a6a6a 100%);
|
||||
}
|
||||
|
||||
.tabs, .tabs2, .tabs3 {
|
||||
background-image: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.tablist li {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.tablist li:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.tablist li.current {
|
||||
background: #F78C40;
|
||||
border-bottom: 3px solid #f57900;
|
||||
}
|
||||
|
||||
/* Tab text colors - comprehensive selectors */
|
||||
#nav-path ul li a,
|
||||
.tabs a,
|
||||
.tabs2 a,
|
||||
.tabs3 a,
|
||||
.tablist a,
|
||||
.tablist a:link,
|
||||
.tablist a:visited,
|
||||
.tablist li a,
|
||||
#main-nav a,
|
||||
.sm > li > a,
|
||||
.sm > li > a .sub-arrow {
|
||||
color: #ffffff !important;
|
||||
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Active/current tab text */
|
||||
#nav-path ul li.current a,
|
||||
.tabs .current a,
|
||||
.tabs2 .current a,
|
||||
.tabs3 .current a,
|
||||
.tablist .current a,
|
||||
.tablist .current a:link,
|
||||
.tablist .current a:visited,
|
||||
.tablist li.current a,
|
||||
#main-nav .current a,
|
||||
.sm .current a {
|
||||
color: #333333 !important;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* Dropdown arrow - white color for top menu */
|
||||
.sm-dox a span.sub-arrow {
|
||||
border-right-color: #ffffff !important;
|
||||
border-bottom-color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Dropdown menu text - must be dark on white background */
|
||||
/* Make this as specific as possible to override white color */
|
||||
.sm-dox > li > ul > li > a,
|
||||
.sm-dox li ul li a,
|
||||
.sm-dox ul li a,
|
||||
#main-menu ul li a {
|
||||
color: #333333 !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
BIN
doc/favicon.png
Normal file
|
After Width: | Height: | Size: 858 B |
41
doc/fetch_doxygen_awesome.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
# Script to download doxygen-awesome-css at build time
|
||||
#
|
||||
# Usage:
|
||||
# cmake -P fetch_doxygen_awesome.cmake \
|
||||
# -DURL=<download_url> \
|
||||
# -DDEST_DIR=<destination_directory> \
|
||||
# -DVERSION=<version>
|
||||
|
||||
if(NOT DEFINED URL)
|
||||
message(FATAL_ERROR "URL not specified")
|
||||
endif()
|
||||
if(NOT DEFINED DEST_DIR)
|
||||
message(FATAL_ERROR "DEST_DIR not specified")
|
||||
endif()
|
||||
if(NOT DEFINED VERSION)
|
||||
message(FATAL_ERROR "VERSION not specified")
|
||||
endif()
|
||||
|
||||
set(EXTRACT_DIR "${DEST_DIR}/doxygen-awesome-css-${VERSION}")
|
||||
|
||||
if(NOT EXISTS "${EXTRACT_DIR}/doxygen-awesome.css")
|
||||
message(STATUS "Downloading doxygen-awesome-css ${VERSION}...")
|
||||
set(TARBALL "${DEST_DIR}/doxygen-awesome-css.tar.gz")
|
||||
file(DOWNLOAD
|
||||
"${URL}"
|
||||
"${TARBALL}"
|
||||
STATUS download_status
|
||||
SHOW_PROGRESS
|
||||
)
|
||||
list(GET download_status 0 status_code)
|
||||
if(NOT status_code EQUAL 0)
|
||||
list(GET download_status 1 error_msg)
|
||||
message(FATAL_ERROR "Download failed: ${error_msg}")
|
||||
endif()
|
||||
message(STATUS "Extracting doxygen-awesome-css...")
|
||||
file(ARCHIVE_EXTRACT
|
||||
INPUT "${TARBALL}"
|
||||
DESTINATION "${DEST_DIR}"
|
||||
)
|
||||
file(REMOVE "${TARBALL}")
|
||||
endif()
|
||||
601
doc/fido2.dox
Normal file
@@ -0,0 +1,601 @@
|
||||
/**
|
||||
|
||||
@page libssh_tutor_fido2 Chapter 11: FIDO2/U2F Keys Support
|
||||
|
||||
@section fido2_intro Introduction
|
||||
|
||||
The traditional SSH public key model stores the private key on disk
|
||||
and anyone who obtains that file (and possibly its passphrase) can impersonate
|
||||
the user. FIDO2 authenticators, such as USB security keys, are hardware tokens
|
||||
that generate or securely store private key material within a secure element
|
||||
and may require explicit user interaction such as a touch, PIN, or biometric
|
||||
verification for use. Hence, security keys are far safer from theft or
|
||||
exfiltration than traditional file-based SSH keys. libssh provides support
|
||||
for FIDO2/U2F security keys as hardware-backed SSH authentication credentials.
|
||||
|
||||
This chapter explains the concepts, build prerequisites, the API, and
|
||||
usage patterns for enrolling (creating) and using security key-backed SSH
|
||||
keys, including resident (discoverable) credentials.
|
||||
|
||||
@subsection fido2_resident_keys Resident Keys
|
||||
|
||||
Two credential storage modes exist for security keys:
|
||||
|
||||
- Non-resident (default): A credential ID (key handle) and metadata are
|
||||
stored on the client-side in a key file. This key handle must be
|
||||
presented to the FIDO2/U2F device while signing. This is somewhat
|
||||
similar to traditional SSH keys, except that the key handle is not the
|
||||
private key itself, but used in combination with the device's master key
|
||||
to derive the actual private key.
|
||||
|
||||
- Resident (discoverable): The credential (and metadata like user id) is
|
||||
stored on the device. No local file is needed; the device can enumerate or
|
||||
locate the credential internally when queried.
|
||||
|
||||
Advantages of resident keys include portability (using the same device
|
||||
across hosts) and resilience (no loss if the local machine is destroyed).
|
||||
Although, they may be limited by the storage of the authenticator.
|
||||
|
||||
@subsection fido2_presence_verification User Presence vs. User Verification
|
||||
|
||||
FIDO2 distinguishes between:
|
||||
|
||||
- User Presence (UP): A simple physical interaction (touch) to confirm a
|
||||
human is present.
|
||||
|
||||
- User Verification (UV): Verification of the user’s identity through
|
||||
biometric authentication or a PIN.
|
||||
|
||||
Requiring UV provides additional protection if the device is stolen
|
||||
and used without the PIN/biometric.
|
||||
|
||||
libssh exposes flags controlling these requirements (see below).
|
||||
|
||||
@subsection fido2_callbacks The Callback Abstraction
|
||||
|
||||
Different environments may need to access security keys through different
|
||||
transport layers (e.g., USB-HID, NFC, Bluetooth, etc.). To accommodate
|
||||
this variability, libssh does not hard-code a single implementation.
|
||||
|
||||
Instead, it defines a small callback interface (`ssh_sk_callbacks`) used for all
|
||||
security key operations. Any implementation of this callback interface can be used
|
||||
by higher-level PKI functions to perform enroll/sign/load_resident_keys
|
||||
operations without needing to know the transport specifics. Hence, users can
|
||||
define their own implementations for these callbacks to support different
|
||||
transport protocols or custom hardware. Refer @ref fido2_custom_callbacks
|
||||
for additional details.
|
||||
|
||||
The callback interface is defined in `libssh/callbacks.h` and the behaviour
|
||||
and return values are specified by `libssh/sk_api.h`, which is the same
|
||||
interface defined by OpenSSH for its security key support. This means that
|
||||
any callback implementations (also called "middleware" in OpenSSH terminology)
|
||||
developed for OpenSSH can be adapted to libssh with minimal changes.
|
||||
|
||||
The following operations are abstracted by the callback interface:
|
||||
|
||||
- api_version(): Report the version of the SK API that the callback implementation
|
||||
is based on, so that libssh can check whether this implementation would be
|
||||
compatible with the SK API version that it supports.
|
||||
Refer @ref fido2_custom_callbacks_version for additional details.
|
||||
- enroll(): Create (enroll) a new credential, returning public key, key
|
||||
handle, attestation data.
|
||||
- sign(): Produce a signature for supplied inputs using an existing key
|
||||
handle.
|
||||
- load_resident_keys(): Enumerate resident (discoverable) credentials stored
|
||||
on the authenticator.
|
||||
|
||||
libssh provides a default implementation of the `ssh_sk_callbacks` using
|
||||
the libfido2 library for the USB-HID transport protocol. Hence, by default,
|
||||
libssh can interact with any FIDO2/U2F device that supports USB-HID and is
|
||||
compatible with libfido2, without requiring any additional modifications.
|
||||
|
||||
@subsection fido2_build Building with FIDO2 Support
|
||||
|
||||
To enable FIDO2/U2F support, libssh must be built with the WITH_FIDO2
|
||||
build option as follows:
|
||||
|
||||
@verbatim
|
||||
cmake -DWITH_FIDO2=ON <other options> ..
|
||||
@endverbatim
|
||||
|
||||
libssh will also build the default USB-HID `ssh_sk_callbacks`, if the
|
||||
libfido2 library and headers are installed on your system.
|
||||
|
||||
@warning If built without libfido2, support for interacting with FIDO2/U2F
|
||||
devices over USB-HID will not be available.
|
||||
|
||||
@subsection fido2_api_overview API Overview
|
||||
|
||||
Security key operations are configured through the `ssh_pki_ctx`
|
||||
which allows to specify both general PKI options and FIDO2-specific
|
||||
options such as the sk_callbacks, challenge data, application string, flags, etc.
|
||||
|
||||
The following sections describe the options that can be configured and how
|
||||
the `ssh_pki_ctx` is used in conjunction with `ssh_key` to perform
|
||||
enrollment, signing, and resident key loading operations.
|
||||
|
||||
@subsection fido2_key_objects Security Key Objects & Metadata
|
||||
|
||||
Security keys are surfaced as `ssh_key` objects of type
|
||||
`SSH_KEYTYPE_SK_ECDSA` and `SSH_KEYTYPE_SK_ED25519` (corresponding to the
|
||||
OpenSSH public key algorithm names `sk-ecdsa-sha2-nistp256@openssh.com` and
|
||||
`sk-ssh-ed25519@openssh.com`). In addition to standard key handling, libssh
|
||||
exposes the following helper functions to retrieve embedded SK metadata:
|
||||
|
||||
- ssh_key_get_sk_application(): Returns the relying party / application
|
||||
(RP ID) string. The Relying Party ID (RP ID) is a string
|
||||
that identifies the application or service requesting key enrollment. It
|
||||
ensures that a credential is bound to a specific origin, preventing
|
||||
phishing across sites. During registration, the authenticator associates
|
||||
the credential with this RP ID so that it can later only be used for
|
||||
authentication requests from the same relying party. For SSH keys, the
|
||||
common format is "ssh:user@host".
|
||||
|
||||
- ssh_key_get_sk_user_id(): Returns a copy of the user ID associated with a key
|
||||
which represents a unique identifier for the user within the relying
|
||||
party (application) context. It is typically a string (such as an
|
||||
email, or a random identifier) that helps distinguish credentials
|
||||
belonging to different users for the same application.
|
||||
|
||||
Though the user ID can be binary data according to the FIDO2 spec, libssh only
|
||||
supports NUL-terminated strings for enrolling new keys in order to remain compatible
|
||||
with the OpenSSH's sk-api interface.
|
||||
|
||||
However, libssh does support loading existing resident keys with user IDs containing
|
||||
arbitrary binary data. It does so by using an `ssh_string` to store the loaded key's
|
||||
user_id, and an `ssh_string` can contain arbitrary binary data that can not be stored
|
||||
in a traditional NUL-terminated string (like null bytes).
|
||||
|
||||
@note The user_id is NOT stored in the key file for non-resident keys. It is only
|
||||
available for resident (discoverable) keys loaded from the authenticator via
|
||||
ssh_sk_resident_keys_load(). For keys imported from files, this function returns
|
||||
NULL.
|
||||
|
||||
- ssh_key_get_sk_flags(): Returns the flags associated with the key. The
|
||||
following are the supported flags and they can be combined using
|
||||
bitwise OR:
|
||||
- SSH_SK_USER_PRESENCE_REQD : Require user presence (touch).
|
||||
- SSH_SK_USER_VERIFICATION_REQD : Require user verification
|
||||
(PIN/biometric).
|
||||
- SSH_SK_RESIDENT_KEY : Request a resident discoverable credential.
|
||||
- SSH_SK_FORCE_OPERATION : Force resident (discoverable) credential
|
||||
creation even if one with same application and user_id already
|
||||
exists.
|
||||
|
||||
These functions perform no additional communication with the
|
||||
authenticator, this metadata is captured during enrollment/loading and
|
||||
cached in the `ssh_key`.
|
||||
|
||||
@subsection fido2_options Setting Security Key Context Options
|
||||
|
||||
Options are set via ssh_pki_ctx_options_set().
|
||||
|
||||
Representative security key options:
|
||||
- SSH_PKI_OPTION_SK_APPLICATION (const char *): Required relying party ID
|
||||
If not set, a default value of "ssh:" is used.
|
||||
- SSH_PKI_OPTION_SK_FLAGS (uint8_t *): Flags described above. If not set,
|
||||
defaults to SSH_SK_USER_PRESENCE_REQD. This is because OpenSSH `sshd`
|
||||
requires user presence for security key authentication by default.
|
||||
- SSH_PKI_OPTION_SK_USER_ID (const char *): Represents a unique identifier
|
||||
for the user within the relying party (application) context.
|
||||
It is typically a string (such as an email, or a random identifier) that
|
||||
helps distinguish credentials belonging to different users for the same
|
||||
application. If not set, defaults to 64 zeros.
|
||||
- SSH_PKI_OPTION_SK_CHALLENGE (ssh_buffer): Custom challenge; if omitted a
|
||||
random 32-byte challenge is generated.
|
||||
- SSH_PKI_OPTION_SK_CALLBACKS (ssh_sk_callbacks): Replace the default
|
||||
callbacks with custom callbacks.
|
||||
|
||||
PIN callback: Use ssh_pki_ctx_set_sk_pin_callback() to register a function
|
||||
matching `ssh_auth_callback` to prompt for and supply a PIN. The callback may
|
||||
be called multiple times to ask for the pin depending on the authenticator policy.
|
||||
|
||||
Callback options: Callback implementations may accept additional configuration
|
||||
name/value options such as the path to the fido device. These options can be provided via
|
||||
`ssh_pki_ctx_sk_callbacks_option_set()`. Refer @ref fido2_custom_callbacks_options
|
||||
for additional details.
|
||||
|
||||
The built-in callback implementation provided by libssh supports additional options,
|
||||
with their names defined in `libssh.h` prefixed with `SSH_SK_OPTION_NAME_*`, such as:
|
||||
|
||||
SSH_SK_OPTION_NAME_DEVICE_PATH: Used for specifying a device path.
|
||||
If the device path is not specified and multiple devices are connected, then
|
||||
depending upon the operation and the flags set, the callback implementation may
|
||||
automatically select a suitable device, or the user may be prompted to touch the
|
||||
device they want to use.
|
||||
|
||||
SSH_SK_OPTION_NAME_USER_ID: Used for setting the user ID.
|
||||
Note that the user ID can also be set using the ssh_pki_ctx_options_set() API.
|
||||
|
||||
@subsection fido2_enrollment Enrollment Example
|
||||
|
||||
An enrollment operation creates a new credential on the authenticator and
|
||||
returns an ssh_key object representing it. The application and user_id
|
||||
fields are required for creating the credential. The other options are
|
||||
optional. A successful enrollment returns the public key, key handle, and
|
||||
metadata which are stored in the ssh_key object, and may optionally return
|
||||
attestation data which is used for verifying the authenticator model and
|
||||
firmware version.
|
||||
|
||||
Below is a simple example enrolling an Ed25519 security key (non-resident)
|
||||
requiring user presence only:
|
||||
|
||||
@code
|
||||
#include <libssh/libssh.h>
|
||||
#include <string.h>
|
||||
|
||||
static int pin_cb(const char *prompt,
|
||||
char *buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void *userdata)
|
||||
{
|
||||
(void)prompt;
|
||||
(void)echo;
|
||||
(void)verify;
|
||||
(void)userdata;
|
||||
|
||||
/* In a real application, the user would be prompted to enter the PIN */
|
||||
const char *pin = "4242";
|
||||
size_t l = strlen(pin);
|
||||
if (l + 1 > len) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
memcpy(buf, pin, l + 1);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int enroll_sk_key()
|
||||
{
|
||||
const char *app = "ssh:user@host";
|
||||
const char *user_id = "alice";
|
||||
uint8_t flags = SSH_SK_USER_PRESENCE_REQD | SSH_SK_USER_VERIFICATION_REQD;
|
||||
const char *device_path = "/dev/hidraw6"; /* Optional device path */
|
||||
|
||||
ssh_pki_ctx pki_ctx = ssh_pki_ctx_new();
|
||||
ssh_pki_ctx_options_set(pki_ctx, SSH_PKI_OPTION_SK_APPLICATION, app);
|
||||
ssh_pki_ctx_options_set(pki_ctx, SSH_PKI_OPTION_SK_USER_ID, user_id);
|
||||
ssh_pki_ctx_options_set(pki_ctx, SSH_PKI_OPTION_SK_FLAGS, &flags);
|
||||
|
||||
ssh_pki_ctx_set_sk_pin_callback(pki_ctx, pin_cb, NULL);
|
||||
|
||||
ssh_pki_ctx_sk_callbacks_option_set(pki_ctx,
|
||||
SSH_SK_OPTION_NAME_DEVICE_PATH,
|
||||
device_path,
|
||||
true);
|
||||
|
||||
ssh_key enrolled = NULL;
|
||||
int rc = ssh_pki_generate_key(SSH_KEYTYPE_SK_ED25519,
|
||||
pki_ctx,
|
||||
&enrolled); /* produces sk-ed25519 key */
|
||||
|
||||
/* Save enrolled key using ssh_pki_export_privkey_file, retrieve attestation
|
||||
* buffer etc. */
|
||||
|
||||
/* Free context and key when done */
|
||||
}
|
||||
@endcode
|
||||
|
||||
After a successful enrollment, you can retrieve the attestation buffer
|
||||
(if provided by the authenticator) from the PKI context:
|
||||
|
||||
@code
|
||||
ssh_buffer att_buf = NULL;
|
||||
rc = ssh_pki_ctx_get_sk_attestation_buffer(pki_ctx, &att_buf);
|
||||
if (rc == SSH_OK && att_buf != NULL) {
|
||||
/* att_buf now contains the serialized attestation
|
||||
* ("ssh-sk-attest-v01"). You can inspect, save, or
|
||||
* parse the buffer as needed
|
||||
*/
|
||||
ssh_buffer_free(att_buf);
|
||||
}
|
||||
@endcode
|
||||
|
||||
Notes:
|
||||
- The attestation buffer is only populated if the enrollment operation
|
||||
succeeds and the authenticator provides attestation data.
|
||||
- `ssh_pki_ctx_get_sk_attestation_buffer()` returns a copy of the attestation
|
||||
buffer; the caller must free it with `ssh_buffer_free()`.
|
||||
|
||||
@subsection fido2_signing Authenticating with a Stored Security Key Public Key
|
||||
|
||||
To authenticate using a security key, the application typically loads the
|
||||
previously enrolled sk-* private key, establishes an SSH connection, and
|
||||
calls `ssh_userauth_publickey()`. libssh automatically recognizes security
|
||||
key types and transparently handles the required hardware-backed
|
||||
authentication steps such as prompting for a touch or PIN using the
|
||||
configured security key callbacks.
|
||||
|
||||
Example:
|
||||
@code
|
||||
#include <libssh/libssh.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int auth_with_sk_file(const char *host,
|
||||
const char *user,
|
||||
const char *privkey_path)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
ssh_key privkey = NULL;
|
||||
int rc = SSH_ERROR;
|
||||
|
||||
session = ssh_new();
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, host);
|
||||
ssh_options_set(session, SSH_OPTIONS_USER, user);
|
||||
ssh_connect(session);
|
||||
|
||||
ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &privkey);
|
||||
|
||||
ssh_pki_ctx pki_ctx = ssh_pki_ctx_new();
|
||||
/* Optionally set PIN callback, device path, etc. */
|
||||
/* ssh_pki_ctx_set_sk_pin_callback(pki_ctx, pin_cb, NULL); */
|
||||
|
||||
ssh_options_set(session, SSH_OPTIONS_PKI_CONTEXT, pki_ctx);
|
||||
|
||||
rc = ssh_userauth_publickey(session, user, privkey);
|
||||
if (rc == SSH_AUTH_SUCCESS) {
|
||||
printf("Authenticated with security key.\n");
|
||||
rc = SSH_OK;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Authentication failed rc=%d err=%s\n",
|
||||
rc,
|
||||
ssh_get_error(session));
|
||||
rc = SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Free resources */
|
||||
}
|
||||
@endcode
|
||||
|
||||
@subsection fido2_resident Resident Key Enumeration
|
||||
|
||||
Resident keys stored on the device can be discovered and loaded with
|
||||
ssh_sk_resident_keys_load() which takes a PKI context (configured with
|
||||
a PIN callback) and returns each key as an ssh_key and the number of keys loaded.
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
#include <libssh/libssh.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int pin_cb(const char *prompt,
|
||||
char *buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void *userdata)
|
||||
{
|
||||
(void)prompt;
|
||||
(void)echo;
|
||||
(void)verify;
|
||||
(void)userdata;
|
||||
const char *pin = "4242";
|
||||
size_t l = strlen(pin);
|
||||
|
||||
if (l + 1 > len) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
memcpy(buf, pin, l + 1);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int auth_with_resident(const char *host,
|
||||
const char *user,
|
||||
const char *application,
|
||||
const char *user_id)
|
||||
{
|
||||
ssh_pki_ctx pki_ctx = NULL;
|
||||
size_t num_found = 0;
|
||||
ssh_key *keys = NULL;
|
||||
ssh_key final_key = NULL;
|
||||
int rc = SSH_ERROR;
|
||||
|
||||
ssh_string cur_application = NULL;
|
||||
ssh_string cur_user_id = NULL;
|
||||
ssh_string expected_application = NULL;
|
||||
ssh_string expected_user_id = NULL;
|
||||
|
||||
pki_ctx = ssh_pki_ctx_new();
|
||||
ssh_pki_ctx_set_sk_pin_callback(pki_ctx, pin_cb, NULL);
|
||||
|
||||
expected_application = ssh_string_from_char(application);
|
||||
expected_user_id = ssh_string_from_char(user_id);
|
||||
|
||||
rc = ssh_sk_resident_keys_load(pki_ctx, &keys, &num_found);
|
||||
for (size_t i = 0; i < num_found; i++) {
|
||||
cur_application = ssh_key_get_sk_application(keys[i]);
|
||||
cur_user_id = ssh_key_get_sk_user_id(keys[i]);
|
||||
|
||||
if (ssh_string_cmp(cur_application, expected_application) == 0 &&
|
||||
ssh_string_cmp(cur_user_id, expected_user_id) == 0) {
|
||||
SSH_STRING_FREE(cur_application);
|
||||
SSH_STRING_FREE(cur_user_id);
|
||||
final_key = keys[i];
|
||||
break;
|
||||
}
|
||||
|
||||
SSH_STRING_FREE(cur_application);
|
||||
SSH_STRING_FREE(cur_user_id);
|
||||
}
|
||||
|
||||
SSH_STRING_FREE(expected_application);
|
||||
SSH_STRING_FREE(expected_user_id);
|
||||
|
||||
/* Continue with authentication using the ssh_key with
|
||||
* ssh_userauth_publickey as usual, and free resources when done. */
|
||||
}
|
||||
@endcode
|
||||
|
||||
@subsection fido2_sshsig Signing using the sshsig API
|
||||
|
||||
Security keys can also be used for general-purpose signing of arbitrary data
|
||||
(without SSH authentication) using the existing `sshsig_sign()` and `sshsig_verify()`
|
||||
functions. These functions work seamlessly with security key types
|
||||
(`SSH_KEYTYPE_SK_ECDSA` and `SSH_KEYTYPE_SK_ED25519`) and will automatically
|
||||
invoke the configured security key callbacks to perform hardware-backed signing
|
||||
operations.
|
||||
|
||||
@subsection fido2_custom_callbacks Implementing Custom Callback Implementations
|
||||
|
||||
Users may need to implement custom callback implementations to support
|
||||
different transport protocols (e.g., NFC, Bluetooth) beyond the default USB-HID
|
||||
support. This section describes how to implement and integrate custom callback
|
||||
implementations.
|
||||
|
||||
To implement custom callbacks, you must include the following headers:
|
||||
|
||||
@code
|
||||
#include <libssh/callbacks.h> /* For ssh_sk_callbacks_struct */
|
||||
#include <libssh/sk_api.h> /* For SK API constants and data structures */
|
||||
@endcode
|
||||
|
||||
The `libssh/sk_api.h` header provides the complete interface specification including
|
||||
request/response structures, flags, and version macros.
|
||||
|
||||
@subsubsection fido2_custom_callbacks_version API Version Compatibility
|
||||
|
||||
libssh validates callback implementations by checking the API version returned by
|
||||
the `api_version()` callback. To ensure compatibility, libssh compares the major
|
||||
version (upper 16 bits) of the returned value with `LIBSSH_SK_API_VERSION_MAJOR`.
|
||||
If they don't match, libssh will reject the callback implementation.
|
||||
This ensures that the callbacks' SK API matches the major version expected by libssh,
|
||||
while allowing minor version differences.
|
||||
|
||||
@subsubsection fido2_custom_callbacks_implementation Implementation Example
|
||||
|
||||
Here's a minimal example of defining and using custom callbacks:
|
||||
|
||||
@code
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/sk_api.h>
|
||||
|
||||
/* Your custom API version callback */
|
||||
static uint32_t my_sk_api_version(void)
|
||||
{
|
||||
/* Match the major version, set your own minor version */
|
||||
return SSH_SK_VERSION_MAJOR | 0x0001;
|
||||
}
|
||||
|
||||
/* Your custom enroll callback */
|
||||
static int my_sk_enroll(uint32_t alg,
|
||||
const uint8_t *challenge,
|
||||
size_t challenge_len,
|
||||
const char *application,
|
||||
uint8_t flags,
|
||||
const char *pin,
|
||||
struct sk_option **options,
|
||||
struct sk_enroll_response **enroll_response)
|
||||
{
|
||||
/* Parse options array to extract custom parameters */
|
||||
if (options != NULL) {
|
||||
for (size_t i = 0; options[i] != NULL; i++) {
|
||||
if (strcmp(options[i]->name, "my_custom_option") == 0) {
|
||||
/* Use options[i]->value */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Implement your enroll logic here */
|
||||
/* ... */
|
||||
|
||||
return SSH_SK_ERR_GENERAL; /* Return appropriate error code */
|
||||
}
|
||||
|
||||
/* Implement other required callbacks: sign, load_resident_keys */
|
||||
/* ... */
|
||||
|
||||
/* Define your callback structure */
|
||||
static struct ssh_sk_callbacks_struct my_sk_callbacks = {
|
||||
.size = sizeof(struct ssh_sk_callbacks_struct),
|
||||
.api_version = my_sk_api_version,
|
||||
.enroll = my_sk_enroll,
|
||||
.sign = my_sk_sign, /* Your implementation */
|
||||
.load_resident_keys = my_sk_load_resident_keys, /* Your implementation */
|
||||
};
|
||||
|
||||
/* Usage example */
|
||||
void use_custom_callbacks(void)
|
||||
{
|
||||
ssh_pki_ctx pki_ctx = ssh_pki_ctx_new();
|
||||
|
||||
/* Set your custom callbacks */
|
||||
ssh_pki_ctx_options_set(pki_ctx,
|
||||
SSH_PKI_OPTION_SK_CALLBACKS,
|
||||
&my_sk_callbacks);
|
||||
|
||||
/* Pass custom options to your callbacks */
|
||||
ssh_pki_ctx_sk_callbacks_option_set(pki_ctx,
|
||||
"my_custom_option",
|
||||
"my_custom_value",
|
||||
false);
|
||||
|
||||
/* Use the context for enrollment, signing, etc. */
|
||||
}
|
||||
@endcode
|
||||
|
||||
@subsubsection fido2_custom_callbacks_options Passing Custom Options
|
||||
|
||||
The `ssh_pki_ctx_sk_callbacks_option_set()` function allows you to pass
|
||||
implementation-specific options as name/value string pairs:
|
||||
|
||||
@code
|
||||
ssh_pki_ctx_sk_callbacks_option_set(pki_ctx,
|
||||
"option_name",
|
||||
"option_value",
|
||||
required);
|
||||
@endcode
|
||||
|
||||
Parameters:
|
||||
- `option_name`: The name of the option (e.g., "device_path", "my_custom_param")
|
||||
- `option_value`: The string value for this option
|
||||
- `required`: If true, this option must be processed by the callback implementation
|
||||
and cannot be ignored. If false, the option is advisory and can be skipped if the
|
||||
callback implementation does not support it.
|
||||
|
||||
These options are passed to your callbacks in the `struct sk_option **options`
|
||||
parameter as a NULL-terminated array. Each `sk_option` has the following fields:
|
||||
- `name`: The option name (char *)
|
||||
- `value`: The option value (char *)
|
||||
- `required`: Whether the option must be processed (uint8_t, non-zero = required)
|
||||
|
||||
@subsubsection fido2_custom_callbacks_openssh OpenSSH Middleware Compatibility
|
||||
|
||||
Since libssh uses the same SK API as OpenSSH, middleware implementations developed
|
||||
for OpenSSH can be adapted with minimal changes.
|
||||
To adapt an OpenSSH middleware for libssh, create a wrapper that populates
|
||||
`ssh_sk_callbacks_struct` with pointers to the middleware's functions.
|
||||
|
||||
@subsection fido2_testing Testing and Environment Variables
|
||||
|
||||
Unit tests covering USB-HID enroll/sign/load_resident_keys operations can be found
|
||||
in the `tests/unittests/torture_sk_usbhid.c` file. To run these tests you
|
||||
must have libfido2 installed and the WITH_FIDO2=ON build option set.
|
||||
Additionally, you must ensure the following:
|
||||
|
||||
- An actual FIDO2 device must be connected to the test machine.
|
||||
- The TORTURE_SK_USBHID environment variable must be set.
|
||||
- The environment variable TORTURE_SK_PIN=<device PIN> must be set.
|
||||
|
||||
If these are not set, the tests are skipped.
|
||||
|
||||
The higher level PKI integration tests can be found in
|
||||
`tests/unittests/torture_pki_sk.c` and the tests related to the sshsig API
|
||||
can be found in `tests/unittests/torture_pki_sshsig.c`.
|
||||
These use the callback implementation provided by OpenSSH's sk-dummy.so,
|
||||
which simulates an authenticator without requiring any hardware. Hence, these tests
|
||||
can be run in the CI environment.
|
||||
However, these tests can also be configured to use the default USB-HID callbacks
|
||||
by setting the same environment variables as described above.
|
||||
|
||||
The following devices were tested during development:
|
||||
|
||||
- Yubico Security Key NFC - USB-A
|
||||
|
||||
*/
|
||||
92
doc/header.html
Normal file
@@ -0,0 +1,92 @@
|
||||
<!-- HTML header for doxygen 1.14.0-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_ICON-->
|
||||
<link rel="icon" href="$relpath^$projecticon" type="image/x-icon" />
|
||||
<!--END PROJECT_ICON-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">var page_layout=1;</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
<!--BEGIN COPY_CLIPBOARD-->
|
||||
<script type="text/javascript" src="$relpath^clipboard.js"></script>
|
||||
<!--END COPY_CLIPBOARD-->
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
$darkmode
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeDarkModeToggle.init()
|
||||
</script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeFragmentCopyButton.init()
|
||||
</script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeParagraphLink.init()
|
||||
</script>
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
|
||||
<script type="text/javascript">
|
||||
DoxygenAwesomeInteractiveToc.init()
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"$logosize/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr><td colspan="2">$searchbox</td></tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
@@ -48,6 +48,8 @@ Table of contents:
|
||||
|
||||
@subpage libssh_tutor_sftp_aio
|
||||
|
||||
@subpage libssh_tutor_fido2
|
||||
|
||||
@subpage libssh_tutor_todo
|
||||
|
||||
*/
|
||||
|
||||
@@ -19,12 +19,13 @@ the interesting functions as you go.
|
||||
|
||||
The libssh library provides:
|
||||
|
||||
- <strong>Key Exchange Methods</strong>: <i>sntrup761x25519-sha512@openssh.com, 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, mlkem768nistp256-sha256, mlkem1024nistp384-sha384, 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>GSSAPI Key Exchange Methods</strong>: gss-group14-sha256-*, gss-group16-sha512-*, gss-nistp256-sha256-*, gss-curve25519-sha256-*
|
||||
- <strong>Public Key Algorithms</strong>: ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa, rsa-sha2-512, rsa-sha2-256
|
||||
- <strong>Ciphers</strong>: <i>aes256-ctr, aes192-ctr, aes128-ctr</i>, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, blowfish-cbc
|
||||
- <strong>Compression Schemes</strong>: zlib, <i>zlib@openssh.com</i>, none
|
||||
- <strong>MAC hashes</strong>: hmac-sha1, hmac-sha2-256, hmac-sha2-512, hmac-md5
|
||||
- <strong>Authentication</strong>: none, password, public-key, keyboard-interactive, <i>gssapi-with-mic</i>
|
||||
- <strong>Authentication</strong>: none, password, public-key, keyboard-interactive, <i>gssapi-with-mic, gssapi-keyex</i>
|
||||
- <strong>Channels</strong>: shell, exec (incl. SCP wrapper), direct-tcpip, subsystem, <i>auth-agent-req@openssh.com</i>
|
||||
- <strong>Global Requests</strong>: tcpip-forward, forwarded-tcpip
|
||||
- <strong>Channel Requests</strong>: x11, pty, <i>exit-status, signal, exit-signal, keepalive@openssh.com, auth-agent-req@openssh.com</i>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Jan-Lukas Wynen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,22 +0,0 @@
|
||||
# that style
|
||||
A plain, more modern HTML style for Doxygen
|
||||
|
||||
## Requirements
|
||||
- Doxygen (tested with version 1.8.13)
|
||||
- *optional*: a sass/scss compiler if you want to modify the style
|
||||
|
||||
## Simple usage
|
||||
Tell Doxygen about the files for that style as shown in [doxyfile.conf](doxyfile.conf). You might need to adjust the
|
||||
paths depending on where you installed that style.
|
||||
When you run Doxygen, all files are copied into to generated HTML folder. So you don't need to keep the originals around
|
||||
unless you want to re-generate the documentation.
|
||||
|
||||
## Advanced
|
||||
that style uses a custom javascript to hack some nice stripes into some tables. It has to be loaded from HTML. Hence you need
|
||||
to use the provided custom header. Since its default content may change when Doxygen is updated, there might be syntax error in
|
||||
the generated HTML. If this is the case, you can remove the custom header (adjust your doxyfile.conf). This has no
|
||||
disadvantages other than removing the stripes.
|
||||
|
||||
[that_style.css](that_style.css) was generated from the scss files in the folder [sass](sass). If you want to change the style,
|
||||
use those files in order to have better control. For instance, you can easily change most colors by modifying the variables
|
||||
in the beginning of [that_style.scss](sass/that_style.scss).
|
||||
@@ -1,56 +0,0 @@
|
||||
<!-- HTML header for doxygen 1.8.13-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
<script src="$relpath^striped_bg.js"></script>
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 56px;">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign" style="padding-left: 0.5em;">
|
||||
<div id="projectname">$projectname
|
||||
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td style="padding-left: 0.5em;">
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<td>$searchbox</td>
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
@@ -1,97 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
viewBox="0 0 6.3499999 5.8208335"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="doc.svg"
|
||||
inkscape:version="0.92.1 r">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="11.139212"
|
||||
inkscape:cy="14.811193"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:showpageshadow="false"
|
||||
units="px"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-291.17915)">
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4d4d4d;stroke-width:0.26458329;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 3.315043,291.8406 H 1.4552083 v 4.49792 h 3.1749999 v -3.10055 z"
|
||||
id="path5095"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 3.1837239,291.84114 v 1.71186 h 1.4472656 v -0.31418 H 3.4473958 v -1.39768 z"
|
||||
id="path5128"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5132"
|
||||
width="2.1166668"
|
||||
height="0.26458332"
|
||||
x="1.8520833"
|
||||
y="293.82498" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect5136"
|
||||
width="1.0583334"
|
||||
height="0.26458332"
|
||||
x="1.8520832"
|
||||
y="294.35416" />
|
||||
<rect
|
||||
y="294.88333"
|
||||
x="1.8520832"
|
||||
height="0.26458332"
|
||||
width="1.8520833"
|
||||
id="rect5138"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4543"
|
||||
width="1.5875"
|
||||
height="0.26458332"
|
||||
x="1.8520832"
|
||||
y="295.41248" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
viewBox="0 0 6.3499998 5.8208335"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="folderclosed.svg"
|
||||
inkscape:export-filename="/home/jl/Prog/doxygen_style/folderclosed.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="51.113139"
|
||||
inkscape:cx="7.7057751"
|
||||
inkscape:cy="12.584171"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:snap-global="false"
|
||||
units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:measure-start="0,0"
|
||||
inkscape:measure-end="0,0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-291.17915)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 0.52916667,292.2374 -0.26458334,0.52925 v 3.43958 H 4.7625001 v -3.43958 H 2.38125 L 2.1166667,292.2374 Z"
|
||||
id="rect4498"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66145831;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 2.9104167,292.76665 2.38125,293.56034 H 0.26458333 v 0.26464 H 2.38125 l 0.5291667,-0.79375 h 1.8520834 v -0.26458 z"
|
||||
id="rect4500"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,83 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="22"
|
||||
viewBox="0 0 6.3499998 5.8208335"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="folderopen.svg"
|
||||
inkscape:export-filename="/home/jl/Prog/doxygen_style/folderopen.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="43.725861"
|
||||
inkscape:cx="8.2043861"
|
||||
inkscape:cy="13.464183"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:snap-global="false"
|
||||
units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:measure-start="0,0"
|
||||
inkscape:measure-end="0,0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-291.17915)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66145831;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 0.52916667,292.23748 -0.26458334,0.52917 v 3.43958 H 4.762461 l 7.8e-5,-3.43958 H 2.38125 l -0.2645833,-0.52917 z"
|
||||
id="path5228"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5279"
|
||||
d="M 1.0583333,293.5604 H 5.55625 L 4.7625,296.20603 H 0.26458333 Z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66145831;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5234"
|
||||
d="M 1.0583333,294.35415 H 3.175 l 0.5291667,-0.52917 H 5.55625 L 4.7625,296.20603 H 0.26458333 Z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.66145831;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.1 KiB |
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 5.8208332 5.8208335"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="mag_glass.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="8.961936"
|
||||
inkscape:cy="10.205344"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="false" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-291.17915)">
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 6.9101562 2.4082031 C 3.1105656 2.4082031 -5.9211895e-16 5.5081643 0 9.3027344 C 0 13.097342 3.1105656 16.197266 6.9101562 16.197266 C 8.2869348 16.197266 9.5698699 15.787508 10.650391 15.087891 L 15.162109 19.587891 L 16.636719 18.115234 L 12.214844 13.707031 C 13.214837 12.510659 13.818359 10.974238 13.818359 9.3027344 C 13.818359 5.5081643 10.709747 2.4082031 6.9101562 2.4082031 z M 6.9101562 4.9101562 C 9.3624717 4.9101562 11.324219 6.8631249 11.324219 9.3027344 C 11.324219 11.742382 9.3624717 13.695312 6.9101562 13.695312 C 4.4578408 13.695312 2.5019531 11.742382 2.5019531 9.3027344 C 2.5019531 6.8631249 4.4578408 4.9101562 6.9101562 4.9101562 z "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,291.17915)"
|
||||
id="rect4524" />
|
||||
<path
|
||||
transform="matrix(0.99422295,0,0,0.68955299,-0.83134947,91.755588)"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.63466448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:transform-center-y="0.25905895"
|
||||
d="m 5.6074138,294.49889 -1.0836583,-1.87695 2.1673165,0 z"
|
||||
id="path4491" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10.53333"
|
||||
height="32"
|
||||
viewBox="0 0 9.8749964 30"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="nav_edge_inter.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="8.6823304"
|
||||
inkscape:cy="16.225639"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1022.3622)">
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 0,1022.3622 v 15 15 l 8,-15 z"
|
||||
id="path4143"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#333333;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 1.2910156,1022.3496 -0.82421872,0.4473 7.87890622,14.5527 -7.87890622,14.5527 0.82421872,0.4473 8.1210938,-15 z"
|
||||
id="path5240"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="8.5333338"
|
||||
height="32"
|
||||
viewBox="0 0 8.0000001 30"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="nav_edge_left.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.3721385"
|
||||
inkscape:cy="14.16429"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="false"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1022.3622)">
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 0 0 L 0 32 L 8.5332031 16 L 0 0 z "
|
||||
transform="matrix(0.93749998,0,0,0.93749998,0,1022.3622)"
|
||||
id="rect4586" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 0,1022.3622 v 15 15 l 8,-15 z"
|
||||
id="path4143"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="8"
|
||||
height="30"
|
||||
viewBox="0 0 8.0000001 30"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="nav_edge.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.3721385"
|
||||
inkscape:cy="14.16429"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="false"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1022.3622)">
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 0,1022.3622 0,15 0,15 8,-15 -8,-15 z"
|
||||
id="path4143"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 1e-8,1022.3622 7.99999999,15 0,-15 -8,0 z m 7.99999999,15 -8,15 8,0 0,-15 z"
|
||||
id="rect4136"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="6"
|
||||
height="9"
|
||||
viewBox="0 0 1.5875 2.3812501"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.1 r"
|
||||
sodipodi:docname="splitbar_handle.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="8.7681488"
|
||||
inkscape:cy="-2.7929517"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
showguides="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1357"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4487" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-294.61873)">
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4485"
|
||||
width="0.26458335"
|
||||
height="0.26458332"
|
||||
x="0.26458332"
|
||||
y="294.8833" />
|
||||
<rect
|
||||
y="294.8833"
|
||||
x="1.0583333"
|
||||
height="0.26458332"
|
||||
width="0.26458335"
|
||||
id="rect4489"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
y="295.41248"
|
||||
x="0.26458329"
|
||||
height="0.26458332"
|
||||
width="0.26458335"
|
||||
id="rect4491"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4493"
|
||||
width="0.26458335"
|
||||
height="0.26458332"
|
||||
x="1.0583333"
|
||||
y="295.41248" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4495"
|
||||
width="0.26458335"
|
||||
height="0.26458332"
|
||||
x="0.26458332"
|
||||
y="295.94165" />
|
||||
<rect
|
||||
y="295.94165"
|
||||
x="1.0583333"
|
||||
height="0.26458332"
|
||||
width="0.26458335"
|
||||
id="rect4497"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
y="296.47079"
|
||||
x="0.26458329"
|
||||
height="0.26458332"
|
||||
width="0.26458335"
|
||||
id="rect4499"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4501"
|
||||
width="0.26458335"
|
||||
height="0.26458332"
|
||||
x="1.0583333"
|
||||
y="296.47079" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 483 B |
|
Before Width: | Height: | Size: 488 B |
@@ -1,32 +0,0 @@
|
||||
// Adds extra CSS classes "even" and "odd" to .memberdecls to allow
|
||||
// striped backgrounds.
|
||||
function MemberDeclsStriper () {
|
||||
var counter = 0;
|
||||
|
||||
this.stripe = function() {
|
||||
$(".memberdecls tbody").children().each(function(i) {
|
||||
|
||||
// reset counter at every heading -> always start with even
|
||||
if ($(this).is(".heading")) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
// add extra classes
|
||||
if (counter % 2 == 1) {
|
||||
$(this).addClass("odd");
|
||||
}
|
||||
else {
|
||||
$(this).addClass("even");
|
||||
}
|
||||
|
||||
// advance counter at every separator
|
||||
// this is the only way to reliably detect which table rows belong together
|
||||
if ($(this).is('[class^="separator"]')) {
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// execute the function
|
||||
$(document).ready(new MemberDeclsStriper().stripe);
|
||||
@@ -21,7 +21,7 @@ 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 connect_ssh(const char *host, const char *port, const char *user, int verbosity)
|
||||
{
|
||||
ssh_session session = NULL;
|
||||
int auth = 0;
|
||||
@@ -38,6 +38,13 @@ ssh_session connect_ssh(const char *host, const char *user, int verbosity)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -21,6 +21,6 @@ clients must be made or how a client should react.
|
||||
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;
|
||||
|
||||
@@ -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]);
|
||||
@@ -183,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;
|
||||
@@ -209,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);
|
||||
|
||||
@@ -182,7 +182,7 @@ int main(int argc, char **argv)
|
||||
ssh_session session = NULL;
|
||||
if (opts(argc, argv) < 0)
|
||||
return EXIT_FAILURE;
|
||||
session = connect_ssh(host, NULL, verbosity);
|
||||
session = connect_ssh(host, NULL, NULL, verbosity);
|
||||
if (session == NULL)
|
||||
return EXIT_FAILURE;
|
||||
create_files(session);
|
||||
|
||||
@@ -13,7 +13,7 @@ int main(void)
|
||||
int rc;
|
||||
uint64_t total = 0;
|
||||
uint64_t lastshown = 4096;
|
||||
session = connect_ssh("localhost", NULL, 0);
|
||||
session = connect_ssh("localhost", NULL, NULL, 0);
|
||||
if (session == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/sftp.h>
|
||||
|
||||
|
||||
#include "examples_common.h"
|
||||
#define MAXCMD 10
|
||||
@@ -88,22 +86,24 @@ static void add_cmd(char *cmd)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage : ssh [options] [login@]hostname\n"
|
||||
"sample client - libssh-%s\n"
|
||||
"Options :\n"
|
||||
" -l user : log in as user\n"
|
||||
" -p port : connect to port\n"
|
||||
" -r : use RSA to verify host public key\n"
|
||||
" -F file : parse configuration file instead of default one\n"
|
||||
fprintf(
|
||||
stderr,
|
||||
"Usage : ssh [options] [login@]hostname\n"
|
||||
"sample client - libssh-%s\n"
|
||||
"Options :\n"
|
||||
" -l user : log in as user\n"
|
||||
" -p port : connect to port\n"
|
||||
" -o option : set configuration option (e.g., -o Compression=yes)\n"
|
||||
" -r : use RSA to verify host public key\n"
|
||||
" -F file : parse configuration file instead of default one\n"
|
||||
#ifdef WITH_PCAP
|
||||
" -P file : create a pcap debugging file\n"
|
||||
" -P file : create a pcap debugging file\n"
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
" -T proxycommand : command to execute as a socket proxy\n"
|
||||
" -T proxycommand : command to execute as a socket proxy\n"
|
||||
#endif
|
||||
"\n",
|
||||
ssh_version(0));
|
||||
"\n",
|
||||
ssh_version(0));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -112,8 +112,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 +159,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 +177,7 @@ static int signal_delayed = 0;
|
||||
#ifdef SIGWINCH
|
||||
static void sigwindowchanged(int i)
|
||||
{
|
||||
(void) i;
|
||||
(void)i;
|
||||
signal_delayed = 1;
|
||||
}
|
||||
#endif
|
||||
@@ -213,18 +211,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 +251,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) {
|
||||
@@ -357,10 +355,8 @@ static int client(ssh_session session)
|
||||
}
|
||||
/* Parse configuration file if specified: The command-line options will
|
||||
* overwrite items loaded from configuration file */
|
||||
if (config_file != NULL) {
|
||||
ssh_options_parse_config(session, config_file);
|
||||
} else {
|
||||
ssh_options_parse_config(session, NULL);
|
||||
if (ssh_options_parse_config(session, config_file) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssh_connect(session)) {
|
||||
|
||||
@@ -631,6 +631,58 @@ auth_publickey(ssh_session session,
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
static int kbdint_check_response(ssh_session session)
|
||||
{
|
||||
int count, cmp;
|
||||
const char *answer = NULL;
|
||||
|
||||
count = ssh_userauth_kbdint_getnanswers(session);
|
||||
if (count != 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
answer = ssh_userauth_kbdint_getanswer(session, 0);
|
||||
cmp = strcasecmp("omnitrix", answer);
|
||||
if (cmp != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
answer = ssh_userauth_kbdint_getanswer(session, 1);
|
||||
cmp = strcmp("000", answer);
|
||||
if (cmp != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
auth_kbdint(ssh_message message, ssh_session session, void *userdata)
|
||||
{
|
||||
struct session_data_struct *sdata = (struct session_data_struct *)userdata;
|
||||
const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
|
||||
const char *instruction = "Most powerful weapon in the galaxy";
|
||||
const char *prompts[2] = {"Name of the weapon: ", "Destruct Code: "};
|
||||
char echo[] = {1, 0};
|
||||
if (!ssh_message_auth_kbdint_is_response(message)) {
|
||||
printf("User %s wants to auth with kbdint\n",
|
||||
ssh_message_auth_user(message));
|
||||
ssh_message_auth_interactive_request(message,
|
||||
name,
|
||||
instruction,
|
||||
2,
|
||||
prompts,
|
||||
echo);
|
||||
return SSH_AUTH_INFO;
|
||||
} else {
|
||||
if (kbdint_check_response(session)) {
|
||||
sdata->authenticated = 1;
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
static ssh_channel
|
||||
channel_open(ssh_session session, void *userdata)
|
||||
{
|
||||
@@ -720,14 +772,15 @@ handle_session(ssh_event event, ssh_session session)
|
||||
struct ssh_server_callbacks_struct server_cb = {
|
||||
.userdata = &sdata,
|
||||
.auth_password_function = auth_password,
|
||||
.auth_kbdint_function = auth_kbdint,
|
||||
.channel_open_request_session_function = channel_open,
|
||||
};
|
||||
|
||||
if (authorizedkeys[0]) {
|
||||
server_cb.auth_pubkey_function = auth_publickey;
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY);
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_INTERACTIVE);
|
||||
} else
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE);
|
||||
|
||||
ssh_callbacks_init(&server_cb);
|
||||
ssh_callbacks_init(&channel_cb);
|
||||
|
||||
@@ -29,6 +29,13 @@ if (WITH_SERVER)
|
||||
endif (WITH_SFTP)
|
||||
endif (WITH_SERVER)
|
||||
|
||||
if (WITH_FIDO2)
|
||||
set(libssh_HDRS
|
||||
${libssh_HDRS}
|
||||
sk_api.h
|
||||
)
|
||||
endif (WITH_FIDO2)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${libssh_HDRS}
|
||||
|
||||
@@ -52,42 +52,45 @@ typedef struct ssh_kbdint_struct* ssh_kbdint;
|
||||
ssh_kbdint ssh_kbdint_new(void);
|
||||
void ssh_kbdint_clean(ssh_kbdint kbd);
|
||||
void ssh_kbdint_free(ssh_kbdint kbd);
|
||||
int ssh_userauth_gssapi_keyex(ssh_session session);
|
||||
|
||||
/** @internal
|
||||
* States of authentication in the client-side. They describe
|
||||
* what was the last response from the server
|
||||
*/
|
||||
enum ssh_auth_state_e {
|
||||
/** No authentication asked */
|
||||
SSH_AUTH_STATE_NONE=0,
|
||||
/** Last authentication response was a partial success */
|
||||
SSH_AUTH_STATE_PARTIAL,
|
||||
/** Last authentication response was a success */
|
||||
SSH_AUTH_STATE_SUCCESS,
|
||||
/** Last authentication response was failed */
|
||||
SSH_AUTH_STATE_FAILED,
|
||||
/** Last authentication was erroneous */
|
||||
SSH_AUTH_STATE_ERROR,
|
||||
/** Last state was a keyboard-interactive ask for info */
|
||||
SSH_AUTH_STATE_INFO,
|
||||
/** Last state was a public key accepted for authentication */
|
||||
SSH_AUTH_STATE_PK_OK,
|
||||
/** We asked for a keyboard-interactive authentication */
|
||||
SSH_AUTH_STATE_KBDINT_SENT,
|
||||
/** We have sent an userauth request with gssapi-with-mic */
|
||||
SSH_AUTH_STATE_GSSAPI_REQUEST_SENT,
|
||||
/** We are exchanging tokens until authentication */
|
||||
SSH_AUTH_STATE_GSSAPI_TOKEN,
|
||||
/** We have sent the MIC and expecting to be authenticated */
|
||||
SSH_AUTH_STATE_GSSAPI_MIC_SENT,
|
||||
/** We have offered a pubkey to check if it is supported */
|
||||
SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
|
||||
/** We have sent pubkey and signature expecting to be authenticated */
|
||||
SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
|
||||
/** We have sent a password expecting to be authenticated */
|
||||
SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
|
||||
/** We have sent a request without auth information (method 'none') */
|
||||
SSH_AUTH_STATE_AUTH_NONE_SENT,
|
||||
/** No authentication asked */
|
||||
SSH_AUTH_STATE_NONE = 0,
|
||||
/** Last authentication response was a partial success */
|
||||
SSH_AUTH_STATE_PARTIAL,
|
||||
/** Last authentication response was a success */
|
||||
SSH_AUTH_STATE_SUCCESS,
|
||||
/** Last authentication response was failed */
|
||||
SSH_AUTH_STATE_FAILED,
|
||||
/** Last authentication was erroneous */
|
||||
SSH_AUTH_STATE_ERROR,
|
||||
/** Last state was a keyboard-interactive ask for info */
|
||||
SSH_AUTH_STATE_INFO,
|
||||
/** Last state was a public key accepted for authentication */
|
||||
SSH_AUTH_STATE_PK_OK,
|
||||
/** We asked for a keyboard-interactive authentication */
|
||||
SSH_AUTH_STATE_KBDINT_SENT,
|
||||
/** We have sent an userauth request with gssapi-with-mic */
|
||||
SSH_AUTH_STATE_GSSAPI_REQUEST_SENT,
|
||||
/** We are exchanging tokens until authentication */
|
||||
SSH_AUTH_STATE_GSSAPI_TOKEN,
|
||||
/** We have sent the MIC and expecting to be authenticated */
|
||||
SSH_AUTH_STATE_GSSAPI_MIC_SENT,
|
||||
/** We have offered a pubkey to check if it is supported */
|
||||
SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
|
||||
/** We have sent pubkey and signature expecting to be authenticated */
|
||||
SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
|
||||
/** We have sent a password expecting to be authenticated */
|
||||
SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
|
||||
/** We have sent a request without auth information (method 'none') */
|
||||
SSH_AUTH_STATE_AUTH_NONE_SENT,
|
||||
/** We have sent the MIC and expecting to be authenticated */
|
||||
SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT,
|
||||
};
|
||||
|
||||
/** @internal
|
||||
|
||||
@@ -54,6 +54,8 @@ struct ssh_bind_struct {
|
||||
char *pubkey_accepted_key_types;
|
||||
char* moduli_file;
|
||||
int rsa_min_size;
|
||||
bool gssapi_key_exchange;
|
||||
char *gssapi_key_exchange_algs;
|
||||
};
|
||||
|
||||
struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -220,36 +220,41 @@ typedef struct ssh_callbacks_struct *ssh_callbacks;
|
||||
* @param user User that wants to authenticate
|
||||
* @param password Password used for authentication
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
|
||||
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
|
||||
* @returns SSH_AUTH_DENIED Authentication failed.
|
||||
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
|
||||
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
|
||||
* are needed.
|
||||
* @returns `SSH_AUTH_DENIED` Authentication failed.
|
||||
*/
|
||||
typedef int (*ssh_auth_password_callback) (ssh_session session, const char *user, const char *password,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* @brief SSH authentication callback. Tries to authenticates user with the "none" method
|
||||
* which is anonymous or passwordless.
|
||||
* @brief SSH authentication callback. Tries to authenticates user with the
|
||||
* "none" method which is anonymous or passwordless.
|
||||
* @param session Current session handler
|
||||
* @param user User that wants to authenticate
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
|
||||
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
|
||||
* @returns SSH_AUTH_DENIED Authentication failed.
|
||||
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
|
||||
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
|
||||
* are needed.
|
||||
* @returns `SSH_AUTH_DENIED` Authentication failed.
|
||||
*/
|
||||
typedef int (*ssh_auth_none_callback) (ssh_session session, const char *user, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief SSH authentication callback. Tries to authenticates user with the "gssapi-with-mic" method
|
||||
* @brief SSH authentication callback. Tries to authenticates user with the
|
||||
* "gssapi-with-mic" method
|
||||
* @param session Current session handler
|
||||
* @param user Username of the user (can be spoofed)
|
||||
* @param principal Authenticated principal of the user, including realm.
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
|
||||
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
|
||||
* @returns SSH_AUTH_DENIED Authentication failed.
|
||||
* @warning Implementations should verify that parameter user matches in some way the principal.
|
||||
* user and principal can be different. Only the latter is guaranteed to be safe.
|
||||
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
|
||||
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
|
||||
* are needed.
|
||||
* @returns `SSH_AUTH_DENIED` Authentication failed.
|
||||
* @warning Implementations should verify that parameter user matches in some
|
||||
* way the principal. user and principal can be different. Only the latter is
|
||||
* guaranteed to be safe.
|
||||
*/
|
||||
typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *user, const char *principal,
|
||||
void *userdata);
|
||||
@@ -259,17 +264,29 @@ typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *us
|
||||
* @param session Current session handler
|
||||
* @param user User that wants to authenticate
|
||||
* @param pubkey public key used for authentication
|
||||
* @param signature_state SSH_PUBLICKEY_STATE_NONE if the key is not signed (simple public key probe),
|
||||
* SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be
|
||||
* replied with a SSH_AUTH_DENIED.
|
||||
* @param signature_state `SSH_PUBLICKEY_STATE_NONE` if the key is not signed
|
||||
* (simple public key probe), `SSH_PUBLICKEY_STATE_VALID` if the signature is
|
||||
* valid. Others values should be replied with a `SSH_AUTH_DENIED`.
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
|
||||
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
|
||||
* @returns SSH_AUTH_DENIED Authentication failed.
|
||||
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
|
||||
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
|
||||
* are needed.
|
||||
* @returns `SSH_AUTH_DENIED` Authentication failed.
|
||||
*/
|
||||
typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user, struct ssh_key_struct *pubkey,
|
||||
char signature_state, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief SSH authentication callback. Tries to authenticates user with the "keyboard-interactive" method
|
||||
* @param message Current message
|
||||
* @param session Current session handler
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
|
||||
* @returns SSH_AUTH_INFO More info required for authentication.
|
||||
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
|
||||
* @returns SSH_AUTH_DENIED Authentication failed.
|
||||
*/
|
||||
typedef int (*ssh_auth_kbdint_callback) (ssh_message message, ssh_session session, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Handles an SSH service request
|
||||
@@ -279,7 +296,6 @@ typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user,
|
||||
* @returns 0 if the request is to be allowed
|
||||
* @returns -1 if the request should not be allowed
|
||||
*/
|
||||
|
||||
typedef int (*ssh_service_request_callback) (ssh_session session, const char *service, void *userdata);
|
||||
|
||||
/**
|
||||
@@ -292,7 +308,7 @@ typedef int (*ssh_service_request_callback) (ssh_session session, const char *se
|
||||
*/
|
||||
typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session session, void *userdata);
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief handle the beginning of a GSSAPI authentication, server side.
|
||||
* Callback should select the oid and also acquire the server credential.
|
||||
* @param session current session handler
|
||||
@@ -307,29 +323,29 @@ typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session se
|
||||
typedef ssh_string (*ssh_gssapi_select_oid_callback) (ssh_session session, const char *user,
|
||||
int n_oid, ssh_string *oids, void *userdata);
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief handle the negotiation of a security context, server side.
|
||||
* @param session current session handler
|
||||
* @param[in] input_token input token provided by client
|
||||
* @param[out] output_token output of the gssapi accept_sec_context method,
|
||||
* NULL after completion.
|
||||
* @returns SSH_OK if the token was generated correctly or accept_sec_context
|
||||
* @returns `SSH_OK` if the token was generated correctly or accept_sec_context
|
||||
* returned GSS_S_COMPLETE
|
||||
* @returns SSH_ERROR in case of error
|
||||
* @returns `SSH_ERROR` in case of error
|
||||
* @warning It is not necessary to fill this callback in if libssh is linked
|
||||
* with libgssapi.
|
||||
*/
|
||||
typedef int (*ssh_gssapi_accept_sec_ctx_callback) (ssh_session session,
|
||||
ssh_string input_token, ssh_string *output_token, void *userdata);
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief Verify and authenticates a MIC, server side.
|
||||
* @param session current session handler
|
||||
* @param[in] mic input mic to be verified provided by client
|
||||
* @param[in] mic_buffer buffer of data to be signed.
|
||||
* @param[in] mic_buffer_size size of mic_buffer
|
||||
* @returns SSH_OK if the MIC was authenticated correctly
|
||||
* @returns SSH_ERROR in case of error
|
||||
* @returns `SSH_OK` if the MIC was authenticated correctly
|
||||
* @returns `SSH_ERROR` in case of error
|
||||
* @warning It is not necessary to fill this callback in if libssh is linked
|
||||
* with libgssapi.
|
||||
*/
|
||||
@@ -405,7 +421,7 @@ struct ssh_server_callbacks_struct {
|
||||
/** This function will be called when a gssapi token comes in.
|
||||
*/
|
||||
ssh_gssapi_accept_sec_ctx_callback gssapi_accept_sec_ctx_function;
|
||||
/* This function will be called when a MIC needs to be verified.
|
||||
/** This function will be called when a MIC needs to be verified.
|
||||
*/
|
||||
ssh_gssapi_verify_mic_callback gssapi_verify_mic_function;
|
||||
/**
|
||||
@@ -414,6 +430,12 @@ struct ssh_server_callbacks_struct {
|
||||
*/
|
||||
ssh_channel_open_request_direct_tcpip_callback
|
||||
channel_open_request_direct_tcpip_function;
|
||||
|
||||
/** This function gets called when a client tries to authenticate through
|
||||
* keyboard interactive method.
|
||||
*/
|
||||
ssh_auth_kbdint_callback auth_kbdint_function;
|
||||
|
||||
};
|
||||
typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
|
||||
|
||||
@@ -439,7 +461,7 @@ typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
|
||||
*
|
||||
* @param cb The callback structure itself.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
LIBSSH_API int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb);
|
||||
|
||||
@@ -570,14 +592,17 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/** @brief Prototype for a packet callback, to be called when a new packet arrives
|
||||
/** @brief Prototype for a packet callback, to be called when a new packet
|
||||
* arrives
|
||||
* @param session The current session of the packet
|
||||
* @param type packet type (see ssh2.h)
|
||||
* @param packet buffer containing the packet, excluding size, type and padding fields
|
||||
* @param packet buffer containing the packet, excluding size, type and padding
|
||||
* fields
|
||||
* @param user user argument to the callback
|
||||
* and are called each time a packet shows up
|
||||
* @returns SSH_PACKET_USED Packet was parsed and used
|
||||
* @returns SSH_PACKET_NOT_USED Packet was not used or understood, processing must continue
|
||||
* @returns `SSH_PACKET_USED` Packet was parsed and used
|
||||
* @returns `SSH_PACKET_NOT_USED` Packet was not used or understood, processing
|
||||
* must continue
|
||||
*/
|
||||
typedef int (*ssh_packet_callback) (ssh_session session, uint8_t type, ssh_buffer packet, void *user);
|
||||
|
||||
@@ -636,7 +661,7 @@ typedef struct ssh_packet_callbacks_struct *ssh_packet_callbacks;
|
||||
*
|
||||
* @param cb The callback structure itself.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb);
|
||||
|
||||
@@ -988,7 +1013,7 @@ typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks;
|
||||
*
|
||||
* @param cb The callback structure itself.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
* @warning this function will not replace existing callbacks but set the
|
||||
* new one atop of them.
|
||||
*/
|
||||
@@ -1007,7 +1032,7 @@ LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel,
|
||||
*
|
||||
* @param cb The callback structure itself.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*
|
||||
* @see ssh_set_channel_callbacks
|
||||
*/
|
||||
@@ -1024,7 +1049,7 @@ LIBSSH_API int ssh_add_channel_callbacks(ssh_channel channel,
|
||||
*
|
||||
* @param cb The callback structure to remove
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_ERROR on error.
|
||||
* @returns `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
|
||||
ssh_channel_callbacks cb);
|
||||
@@ -1057,7 +1082,7 @@ struct ssh_threads_callbacks_struct {
|
||||
* @param[in] cb A pointer to a ssh_threads_callbacks_struct structure, which
|
||||
* contains the different callbacks to be set.
|
||||
*
|
||||
* @returns Always returns SSH_OK.
|
||||
* @returns Always returns `SSH_OK`.
|
||||
*
|
||||
* @see ssh_threads_callbacks_struct
|
||||
* @see SSH_THREADS_PTHREAD
|
||||
@@ -1154,6 +1179,177 @@ struct ssh_jump_callbacks_struct {
|
||||
ssh_jump_authenticate_callback authenticate;
|
||||
};
|
||||
|
||||
/* Security key callbacks */
|
||||
|
||||
/*
|
||||
* Forward declarations for structs that have been defined in sk_api.h.
|
||||
* If you need to work with the fields inside them, please include
|
||||
* libssh/sk_api.h
|
||||
*/
|
||||
struct sk_enroll_response;
|
||||
struct sk_sign_response;
|
||||
struct sk_resident_key;
|
||||
struct sk_option;
|
||||
|
||||
#define LIBSSH_SK_API_VERSION_MAJOR 0x000a0000
|
||||
|
||||
/**
|
||||
* @brief FIDO2/U2F SK API version callback.
|
||||
*
|
||||
* Returns the version of the FIDO2/U2F API that the callbacks implement.
|
||||
* This callback allows custom callback implementations to specify their
|
||||
* SK API version for compatibility checking with libssh's security key
|
||||
* interface.
|
||||
*
|
||||
* @details Version compatibility is determined by comparing the major version
|
||||
* portion (upper 16 bits) of the returned value with SSH_SK_VERSION_MAJOR.
|
||||
*
|
||||
* For compatibility, implementations should return a version where:
|
||||
* (returned_version & SSH_SK_VERSION_MAJOR_MASK) == SSH_SK_VERSION_MAJOR
|
||||
*
|
||||
* This ensures that the callbacks' SK API matches the major version expected
|
||||
* by libssh, while allowing minor version differences for backward
|
||||
* compatibility.
|
||||
*
|
||||
* @see LIBSSH_SK_API_VERSION_MAJOR Current expected major API version
|
||||
* @see SSH_SK_VERSION_MAJOR_MASK Mask for extracting major version (0xffff0000)
|
||||
*/
|
||||
typedef uint32_t (*sk_api_version_callback)(void);
|
||||
|
||||
/**
|
||||
* @brief FIDO2/U2F key enrollment callback.
|
||||
*
|
||||
* Enrolls a new FIDO2/U2F security key credential (private key generation).
|
||||
* This callback handles the creation of new FIDO2/U2F credentials, including
|
||||
* both resident and non-resident keys.
|
||||
*
|
||||
* @param[in] alg The cryptographic algorithm to use
|
||||
* @param[in] challenge Random challenge data for enrollment
|
||||
* @param[in] challenge_len Length of the challenge data
|
||||
* @param[in] application Application identifier (relying party ID)
|
||||
* @param[in] flags Enrollment flags
|
||||
* @param[in] pin PIN for user verification (may be NULL)
|
||||
* @param[in] options Array of enrollment options (device path, user ID, etc.)
|
||||
* @param[out] enroll_response Enrollment response containing public key,
|
||||
* key handle, signature, and attestation data
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_SK_ERR_* codes on failure.
|
||||
*/
|
||||
typedef int (*sk_enroll_callback)(uint32_t alg,
|
||||
const uint8_t *challenge,
|
||||
size_t challenge_len,
|
||||
const char *application,
|
||||
uint8_t flags,
|
||||
const char *pin,
|
||||
struct sk_option **options,
|
||||
struct sk_enroll_response **enroll_response);
|
||||
|
||||
/**
|
||||
* @brief FIDO2/U2F security key signing callback.
|
||||
*
|
||||
* Signs data using a FIDO2 security key credential. This callback performs
|
||||
* cryptographic signing operations using previously enrolled FIDO2/U2F
|
||||
* credentials.
|
||||
*
|
||||
* @param[in] alg The cryptographic algorithm used by the key
|
||||
* @param[in] data Data to be signed
|
||||
* @param[in] data_len Length of the data to sign
|
||||
* @param[in] application Application identifier (relying party ID)
|
||||
* @param[in] key_handle Key handle identifying the credential
|
||||
* @param[in] key_handle_len Length of the key handle
|
||||
* @param[in] flags Signing flags
|
||||
* @param[in] pin PIN for user verification (may be NULL)
|
||||
* @param[in] options Array of signing options (device path, etc.)
|
||||
* @param[out] sign_response Signature response containing signature data,
|
||||
* flags, and counter information
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_SK_ERR_* codes on failure.
|
||||
*/
|
||||
typedef int (*sk_sign_callback)(uint32_t alg,
|
||||
const uint8_t *data,
|
||||
size_t data_len,
|
||||
const char *application,
|
||||
const uint8_t *key_handle,
|
||||
size_t key_handle_len,
|
||||
uint8_t flags,
|
||||
const char *pin,
|
||||
struct sk_option **options,
|
||||
struct sk_sign_response **sign_response);
|
||||
|
||||
/**
|
||||
* @brief FIDO2 security key resident keys loading callback.
|
||||
*
|
||||
* Enumerates and loads all resident keys (discoverable credentials) stored
|
||||
* on FIDO2 devices. Resident keys are credentials stored directly on
|
||||
* the device itself and can be discovered without prior knowledge
|
||||
* of key handles.
|
||||
*
|
||||
* @param[in] pin PIN for accessing resident keys (required for most operations)
|
||||
* @param[in] options Array of options (device path, etc.)
|
||||
* @param[out] resident_keys Array of resident key structures containing key
|
||||
* data, application IDs, user information, and metadata
|
||||
* @param[out] num_keys_found Number of resident keys found and loaded
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_SK_ERR_* codes on failure.
|
||||
*/
|
||||
typedef int (*sk_load_resident_keys_callback)(
|
||||
const char *pin,
|
||||
struct sk_option **options,
|
||||
struct sk_resident_key ***resident_keys,
|
||||
size_t *num_keys_found);
|
||||
|
||||
/**
|
||||
* @brief FIDO2/U2F security key callbacks structure.
|
||||
*
|
||||
* This structure contains callbacks for FIDO2/U2F operations.
|
||||
* It allows applications to provide custom implementations of FIDO2/U2F
|
||||
* operations to override the default libfido2-based implementation.
|
||||
*
|
||||
* @warning These callbacks will only be called if libssh was built with
|
||||
* FIDO2/U2F support enabled. (WITH_FIDO2 = ON).
|
||||
*/
|
||||
struct ssh_sk_callbacks_struct {
|
||||
/** DON'T SET THIS use ssh_callbacks_init() instead. */
|
||||
size_t size;
|
||||
|
||||
/**
|
||||
* This callback returns the SK API version used by the callback
|
||||
* implementation.
|
||||
*
|
||||
* @see sk_api_version_callback for detailed documentation
|
||||
*/
|
||||
sk_api_version_callback api_version;
|
||||
|
||||
/**
|
||||
* This callback enrolls a new FIDO2/U2F credential, generating
|
||||
* a new key pair and optionally storing it on the device itself
|
||||
* (resident keys).
|
||||
*
|
||||
* @see sk_enroll_callback for detailed documentation
|
||||
*/
|
||||
sk_enroll_callback enroll;
|
||||
|
||||
/**
|
||||
* This callback performs cryptographic signing operations using a
|
||||
* previously enrolled FIDO2/U2F credential.
|
||||
*
|
||||
* @see sk_sign_callback for detailed documentation
|
||||
*/
|
||||
sk_sign_callback sign;
|
||||
|
||||
/**
|
||||
* This callback enumerates and loads all resident keys (discoverable
|
||||
* credentials) stored on the FIDO2 device.
|
||||
*
|
||||
* @see sk_load_resident_keys_callback for detailed documentation
|
||||
*/
|
||||
sk_load_resident_keys_callback load_resident_keys;
|
||||
};
|
||||
|
||||
typedef struct ssh_sk_callbacks_struct *ssh_sk_callbacks;
|
||||
|
||||
const struct ssh_sk_callbacks_struct *ssh_sk_get_default_callbacks(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#ifndef LIBSSH_CONFIG_H_
|
||||
#define LIBSSH_CONFIG_H_
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
enum ssh_config_opcode_e {
|
||||
/* Unknown opcode */
|
||||
@@ -66,7 +67,14 @@ enum ssh_config_opcode_e {
|
||||
SOC_CONTROLMASTER,
|
||||
SOC_CONTROLPATH,
|
||||
SOC_CERTIFICATE,
|
||||
SOC_REQUIRED_RSA_SIZE,
|
||||
SOC_ADDRESSFAMILY,
|
||||
SOC_GSSAPIKEYEXCHANGE,
|
||||
SOC_GSSAPIKEXALGORITHMS,
|
||||
|
||||
SOC_MAX /* Keep this one last in the list */
|
||||
};
|
||||
enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword);
|
||||
int ssh_config_parse_line_cli(ssh_session session, const char *line);
|
||||
|
||||
#endif /* LIBSSH_CONFIG_H_ */
|
||||
|
||||
@@ -85,6 +85,24 @@ enum ssh_key_exchange_e {
|
||||
SSH_KEX_DH_GROUP14_SHA256,
|
||||
/* sntrup761x25519-sha512@openssh.com */
|
||||
SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM,
|
||||
/* sntrup761x25519-sha512 */
|
||||
SSH_KEX_SNTRUP761X25519_SHA512,
|
||||
/* mlkem768x25519-sha256 */
|
||||
SSH_KEX_MLKEM768X25519_SHA256,
|
||||
/* mlkem768nistp256-sha256 */
|
||||
SSH_KEX_MLKEM768NISTP256_SHA256,
|
||||
#ifdef HAVE_MLKEM1024
|
||||
/* mlkem1024nistp384-sha384 */
|
||||
SSH_KEX_MLKEM1024NISTP384_SHA384,
|
||||
#endif /* HAVE_MLKEM1024 */
|
||||
/* gss-group14-sha256-* */
|
||||
SSH_GSS_KEX_DH_GROUP14_SHA256,
|
||||
/* gss-group16-sha512-* */
|
||||
SSH_GSS_KEX_DH_GROUP16_SHA512,
|
||||
/* gss-nistp256-sha256-* */
|
||||
SSH_GSS_KEX_ECDH_NISTP256_SHA256,
|
||||
/* gss-curve25519-sha256-* */
|
||||
SSH_GSS_KEX_CURVE25519_SHA256,
|
||||
};
|
||||
|
||||
enum ssh_cipher_e {
|
||||
@@ -108,6 +126,9 @@ struct dh_ctx;
|
||||
|
||||
struct ssh_crypto_struct {
|
||||
bignum shared_secret;
|
||||
ssh_string hybrid_client_init;
|
||||
ssh_string hybrid_server_reply;
|
||||
ssh_string hybrid_shared_secret;
|
||||
struct dh_ctx *dh_ctx;
|
||||
#ifdef WITH_GEX
|
||||
size_t dh_pmin; size_t dh_pn; size_t dh_pmax; /* preferred group parameters */
|
||||
@@ -138,6 +159,14 @@ struct ssh_crypto_struct {
|
||||
ssh_curve25519_pubkey curve25519_client_pubkey;
|
||||
ssh_curve25519_pubkey curve25519_server_pubkey;
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_MLKEM
|
||||
EVP_PKEY *mlkem_privkey;
|
||||
#else
|
||||
unsigned char *mlkem_privkey;
|
||||
size_t mlkem_privkey_len;
|
||||
#endif
|
||||
ssh_string mlkem_client_pubkey;
|
||||
ssh_string mlkem_ciphertext;
|
||||
#ifdef HAVE_SNTRUP761
|
||||
ssh_sntrup761_privkey sntrup761_privkey;
|
||||
ssh_sntrup761_pubkey sntrup761_client_pubkey;
|
||||
|
||||
@@ -53,6 +53,7 @@ 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_curve25519_build_k(ssh_session session);
|
||||
|
||||
int ssh_client_curve25519_init(ssh_session session);
|
||||
void ssh_client_curve25519_remove_callbacks(ssh_session session);
|
||||
|
||||
@@ -48,6 +48,7 @@ extern "C" {
|
||||
|
||||
extern struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks;
|
||||
/* Backend-specific functions. */
|
||||
int ssh_ecdh_init(ssh_session session);
|
||||
int ssh_client_ecdh_init(ssh_session session);
|
||||
void ssh_client_ecdh_remove_callbacks(ssh_session session);
|
||||
int ecdh_build_k(ssh_session session);
|
||||
|
||||
@@ -29,12 +29,41 @@
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @internal
|
||||
* @brief ED25519 public key.
|
||||
* Ed25519 public key consist of 32 bytes.
|
||||
*/
|
||||
#define ED25519_PK_LEN 32
|
||||
|
||||
/** @internal
|
||||
* @brief ED25519 secret key.
|
||||
* Ed25519 secret key consist of 64 bytes.
|
||||
*/
|
||||
#define ED25519_SK_LEN 64
|
||||
|
||||
/** @internal
|
||||
* @brief ED25519 signature.
|
||||
* Ed25519 signatures consist of 64 bytes.
|
||||
*/
|
||||
#define ED25519_SIG_LEN 64
|
||||
|
||||
/** @internal
|
||||
* @brief ED25519 public key.
|
||||
* The public key consists of 32 bytes and can be used for signature
|
||||
* verification.
|
||||
*/
|
||||
typedef uint8_t ed25519_pubkey[ED25519_PK_LEN];
|
||||
|
||||
/** @internal
|
||||
* @brief ED25519 private key.
|
||||
* The private key consists of 64 bytes and should be kept secret.
|
||||
*/
|
||||
typedef uint8_t ed25519_privkey[ED25519_SK_LEN];
|
||||
|
||||
/** @internal
|
||||
* @brief ED25519 signature.
|
||||
* Ed25519 signatures consists of 64 bytes.
|
||||
*/
|
||||
typedef uint8_t ed25519_signature[ED25519_SIG_LEN];
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -46,7 +75,7 @@ extern "C" {
|
||||
* @param[out] pk generated public key
|
||||
* @param[out] sk generated secret key
|
||||
* @return 0 on success, -1 on error.
|
||||
* */
|
||||
*/
|
||||
int crypto_sign_ed25519_keypair(ed25519_pubkey pk, ed25519_privkey sk);
|
||||
|
||||
/** @internal
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
/* all OID begin with the tag identifier + length */
|
||||
#define SSH_OID_TAG 06
|
||||
|
||||
#define GSSAPI_KEY_EXCHANGE_SUPPORTED "gss-group14-sha256-," \
|
||||
"gss-group16-sha512-," \
|
||||
"gss-nistp256-sha256-," \
|
||||
"gss-curve25519-sha256-"
|
||||
|
||||
typedef struct ssh_gssapi_struct *ssh_gssapi;
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -44,14 +49,12 @@ enum ssh_gssapi_state_e {
|
||||
|
||||
struct ssh_gssapi_struct{
|
||||
enum ssh_gssapi_state_e state; /* current state */
|
||||
struct gss_OID_desc_struct mech; /* mechanism being elected for auth */
|
||||
gss_cred_id_t server_creds; /* credentials of server */
|
||||
gss_cred_id_t client_creds; /* creds delegated by the client */
|
||||
gss_ctx_id_t ctx; /* the authentication context */
|
||||
gss_name_t client_name; /* Identity of the client */
|
||||
char *user; /* username of client */
|
||||
char *canonic_user; /* canonic form of the client's username */
|
||||
char *service; /* name of the service */
|
||||
struct {
|
||||
gss_name_t server_name; /* identity of server */
|
||||
OM_uint32 flags; /* flags used for init context */
|
||||
@@ -65,6 +68,7 @@ struct ssh_gssapi_struct{
|
||||
int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids);
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server);
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic);
|
||||
int ssh_gssapi_server_oids(gss_OID_set *selected);
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token);
|
||||
@@ -76,7 +80,20 @@ int ssh_gssapi_init(ssh_session session);
|
||||
void ssh_gssapi_log_error(int verb, const char *msg_a, int maj_stat, int min_stat);
|
||||
int ssh_gssapi_auth_mic(ssh_session session);
|
||||
void ssh_gssapi_free(ssh_session session);
|
||||
int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids);
|
||||
char *ssh_gssapi_name_to_char(gss_name_t name);
|
||||
int ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host);
|
||||
OM_uint32 ssh_gssapi_init_ctx(struct ssh_gssapi_struct *gssapi,
|
||||
gss_buffer_desc *input_token,
|
||||
gss_buffer_desc *output_token,
|
||||
OM_uint32 *ret_flags);
|
||||
|
||||
char *ssh_gssapi_oid_hash(ssh_string oid);
|
||||
char *ssh_gssapi_kex_mechs(ssh_session session);
|
||||
int ssh_gssapi_check_client_config(ssh_session session);
|
||||
ssh_buffer ssh_gssapi_build_mic(ssh_session session, const char *context);
|
||||
int ssh_gssapi_auth_keyex_mic(ssh_session session,
|
||||
gss_buffer_desc *mic_token_buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
51
include/libssh/hybrid_mlkem.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2025 by Red Hat, Inc.
|
||||
*
|
||||
* Author: Sahana Prasad <sahana@redhat.com>
|
||||
* Author: Pavol Žáčik <pzacik@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 HYBRID_MLKEM_H_
|
||||
#define HYBRID_MLKEM_H_
|
||||
|
||||
#include "libssh/mlkem.h"
|
||||
#include "libssh/wrapper.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NISTP256_SHARED_SECRET_SIZE 32
|
||||
#define NISTP384_SHARED_SECRET_SIZE 48
|
||||
|
||||
int ssh_client_hybrid_mlkem_init(ssh_session session);
|
||||
void ssh_client_hybrid_mlkem_remove_callbacks(ssh_session session);
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
void ssh_server_hybrid_mlkem_init(ssh_session session);
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HYBRID_MLKEM_H_ */
|
||||
36
include/libssh/kex-gss.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* kex-gss.h - GSSAPI key exchange
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2024 by Gauravsingh Sisodia <xaerru@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; either version 2.1 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* The SSH Library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with the SSH Library; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef KEX_GSS_H_
|
||||
#define KEX_GSS_H_
|
||||
|
||||
#include "config.h"
|
||||
#ifdef WITH_GSSAPI
|
||||
|
||||
int ssh_client_gss_kex_init(ssh_session session);
|
||||
void ssh_server_gss_kex_init(ssh_session session);
|
||||
int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet);
|
||||
void ssh_client_gss_kex_remove_callbacks(ssh_session session);
|
||||
void ssh_client_gss_kex_remove_callback_hostkey(ssh_session session);
|
||||
|
||||
#endif /* WITH_GSSAPI */
|
||||
#endif /* KEX_GSS_H_ */
|
||||
@@ -31,6 +31,9 @@ struct ssh_kex_struct {
|
||||
char *methods[SSH_KEX_METHODS];
|
||||
};
|
||||
|
||||
/* crypto.h needs ssh_kex_struct so it is included below the struct definition */
|
||||
#include "libssh/crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -64,6 +67,7 @@ int ssh_make_sessionid(ssh_session session);
|
||||
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie);
|
||||
int ssh_hashbufout_add_cookie(ssh_session session);
|
||||
int ssh_generate_session_keys(ssh_session session);
|
||||
bool ssh_kex_is_gss(struct ssh_crypto_struct *crypto);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ int ssh_gcry_rand_range(bignum rnd, bignum max);
|
||||
#define bignum_rand_range(rnd, max) ssh_gcry_rand_range(rnd, max);
|
||||
#define bignum_dup(orig, dest) do { \
|
||||
if (*(dest) == NULL) { \
|
||||
*(dest) = gcry_mpi_copy(orig); \
|
||||
*(dest) = gcry_mpi_copy((const gcry_mpi_t)orig); \
|
||||
} else { \
|
||||
gcry_mpi_set(*(dest), orig); \
|
||||
gcry_mpi_set(*(dest), (const gcry_mpi_t)orig); \
|
||||
} \
|
||||
} while(0)
|
||||
/* Helper functions for data conversions. */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2003-2025 by Aris Adamantiadis and the libssh team
|
||||
* Copyright (c) 2003-2026 by Aris Adamantiadis and the libssh team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -107,6 +107,7 @@ typedef struct ssh_session_struct* ssh_session;
|
||||
typedef struct ssh_string_struct* ssh_string;
|
||||
typedef struct ssh_event_struct* ssh_event;
|
||||
typedef struct ssh_connector_struct * ssh_connector;
|
||||
typedef struct ssh_pki_ctx_struct *ssh_pki_ctx;
|
||||
typedef void* ssh_gssapi_creds;
|
||||
|
||||
/* Socket type */
|
||||
@@ -151,13 +152,14 @@ enum ssh_auth_e {
|
||||
};
|
||||
|
||||
/* auth flags */
|
||||
#define SSH_AUTH_METHOD_UNKNOWN 0x0000u
|
||||
#define SSH_AUTH_METHOD_NONE 0x0001u
|
||||
#define SSH_AUTH_METHOD_PASSWORD 0x0002u
|
||||
#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
|
||||
#define SSH_AUTH_METHOD_HOSTBASED 0x0008u
|
||||
#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
|
||||
#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
|
||||
#define SSH_AUTH_METHOD_UNKNOWN 0x0000u
|
||||
#define SSH_AUTH_METHOD_NONE 0x0001u
|
||||
#define SSH_AUTH_METHOD_PASSWORD 0x0002u
|
||||
#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
|
||||
#define SSH_AUTH_METHOD_HOSTBASED 0x0008u
|
||||
#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
|
||||
#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
|
||||
#define SSH_AUTH_METHOD_GSSAPI_KEYEX 0x0040u
|
||||
|
||||
/* messages */
|
||||
enum ssh_requests_e {
|
||||
@@ -370,6 +372,12 @@ enum ssh_control_master_options_e {
|
||||
SSH_CONTROL_MASTER_AUTOASK
|
||||
};
|
||||
|
||||
enum ssh_address_family_options_e {
|
||||
SSH_ADDRESS_FAMILY_ANY,
|
||||
SSH_ADDRESS_FAMILY_INET,
|
||||
SSH_ADDRESS_FAMILY_INET6
|
||||
};
|
||||
|
||||
enum ssh_options_e {
|
||||
SSH_OPTIONS_HOST,
|
||||
SSH_OPTIONS_PORT,
|
||||
@@ -420,6 +428,10 @@ enum ssh_options_e {
|
||||
SSH_OPTIONS_CERTIFICATE,
|
||||
SSH_OPTIONS_PROXYJUMP,
|
||||
SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
|
||||
SSH_OPTIONS_PKI_CONTEXT,
|
||||
SSH_OPTIONS_ADDRESS_FAMILY,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -721,9 +733,17 @@ LIBSSH_API int ssh_key_cmp(const ssh_key k1,
|
||||
const ssh_key k2,
|
||||
enum ssh_keycmp_e what);
|
||||
LIBSSH_API ssh_key ssh_key_dup(const ssh_key key);
|
||||
LIBSSH_API uint32_t ssh_key_get_sk_flags(const ssh_key key);
|
||||
LIBSSH_API ssh_string ssh_key_get_sk_application(const ssh_key key);
|
||||
LIBSSH_API ssh_string ssh_key_get_sk_user_id(const ssh_key key);
|
||||
|
||||
SSH_DEPRECATED LIBSSH_API int
|
||||
ssh_pki_generate(enum ssh_keytypes_e type, int parameter, ssh_key *pkey);
|
||||
|
||||
LIBSSH_API int ssh_pki_generate_key(enum ssh_keytypes_e type,
|
||||
ssh_pki_ctx pki_context,
|
||||
ssh_key *pkey);
|
||||
|
||||
LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
|
||||
ssh_key *pkey);
|
||||
LIBSSH_API int ssh_pki_import_privkey_base64(const char *b64_key,
|
||||
const char *passphrase,
|
||||
ssh_auth_callback auth_fn,
|
||||
@@ -844,6 +864,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);
|
||||
@@ -901,6 +922,7 @@ enum sshsig_digest_e {
|
||||
LIBSSH_API int sshsig_sign(const void *data,
|
||||
size_t data_length,
|
||||
ssh_key privkey,
|
||||
ssh_pki_ctx pki_context,
|
||||
const char *sig_namespace,
|
||||
enum sshsig_digest_e hash_alg,
|
||||
char **signature);
|
||||
@@ -910,6 +932,83 @@ LIBSSH_API int sshsig_verify(const void *data,
|
||||
const char *sig_namespace,
|
||||
ssh_key *sign_key);
|
||||
|
||||
/* PKI context API */
|
||||
|
||||
enum ssh_pki_options_e {
|
||||
SSH_PKI_OPTION_RSA_KEY_SIZE,
|
||||
|
||||
/* Security Key options */
|
||||
SSH_PKI_OPTION_SK_APPLICATION,
|
||||
SSH_PKI_OPTION_SK_FLAGS,
|
||||
SSH_PKI_OPTION_SK_USER_ID,
|
||||
SSH_PKI_OPTION_SK_CHALLENGE,
|
||||
SSH_PKI_OPTION_SK_CALLBACKS,
|
||||
};
|
||||
|
||||
/* FIDO2/U2F Operation Flags */
|
||||
|
||||
/** Requires user presence confirmation (tap/touch) */
|
||||
#ifndef SSH_SK_USER_PRESENCE_REQD
|
||||
#define SSH_SK_USER_PRESENCE_REQD 0x01
|
||||
#endif
|
||||
|
||||
/** Requires user verification (PIN/biometric) - FIDO2 only */
|
||||
#ifndef SSH_SK_USER_VERIFICATION_REQD
|
||||
#define SSH_SK_USER_VERIFICATION_REQD 0x04
|
||||
#endif
|
||||
|
||||
/** Force resident key enrollment even if a resident key with given user ID
|
||||
* already exists - FIDO2 only */
|
||||
#ifndef SSH_SK_FORCE_OPERATION
|
||||
#define SSH_SK_FORCE_OPERATION 0x10
|
||||
#endif
|
||||
|
||||
/** Create/use resident key stored on authenticator - FIDO2 only */
|
||||
#ifndef SSH_SK_RESIDENT_KEY
|
||||
#define SSH_SK_RESIDENT_KEY 0x20
|
||||
#endif
|
||||
|
||||
LIBSSH_API ssh_pki_ctx ssh_pki_ctx_new(void);
|
||||
|
||||
LIBSSH_API int ssh_pki_ctx_options_set(ssh_pki_ctx context,
|
||||
enum ssh_pki_options_e option,
|
||||
const void *value);
|
||||
|
||||
LIBSSH_API int ssh_pki_ctx_set_sk_pin_callback(ssh_pki_ctx context,
|
||||
ssh_auth_callback pin_callback,
|
||||
void *userdata);
|
||||
|
||||
#define SSH_SK_OPTION_NAME_DEVICE_PATH "device"
|
||||
#define SSH_SK_OPTION_NAME_USER_ID "user"
|
||||
|
||||
LIBSSH_API int ssh_pki_ctx_sk_callbacks_option_set(ssh_pki_ctx context,
|
||||
const char *name,
|
||||
const char *value,
|
||||
bool required);
|
||||
|
||||
LIBSSH_API int ssh_pki_ctx_sk_callbacks_options_clear(ssh_pki_ctx context);
|
||||
|
||||
LIBSSH_API int
|
||||
ssh_pki_ctx_get_sk_attestation_buffer(const struct ssh_pki_ctx_struct *context,
|
||||
ssh_buffer *attestation_buffer);
|
||||
|
||||
LIBSSH_API void ssh_pki_ctx_free(ssh_pki_ctx context);
|
||||
|
||||
#define SSH_PKI_CTX_FREE(x) \
|
||||
do { \
|
||||
if ((x) != NULL) { \
|
||||
ssh_pki_ctx_free(x); \
|
||||
x = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Security key resident keys API */
|
||||
|
||||
LIBSSH_API int
|
||||
ssh_sk_resident_keys_load(const struct ssh_pki_ctx_struct *pki_context,
|
||||
ssh_key **resident_keys_result,
|
||||
size_t *num_keys_found_result);
|
||||
|
||||
#ifndef LIBSSH_LEGACY_0_4
|
||||
#include "libssh/legacy.h"
|
||||
#endif
|
||||
|
||||
73
include/libssh/mlkem.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2025 by Red Hat, Inc.
|
||||
*
|
||||
* Author: Pavol Žáčik <pzacik@redhat.com>
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, 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.
|
||||
*/
|
||||
|
||||
#ifndef MLKEM_H_
|
||||
#define MLKEM_H_
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct mlkem_type_info {
|
||||
size_t pubkey_size;
|
||||
size_t ciphertext_size;
|
||||
#ifdef HAVE_GCRYPT_MLKEM
|
||||
size_t privkey_size;
|
||||
enum gcry_kem_algos alg;
|
||||
#elif defined(HAVE_OPENSSL_MLKEM)
|
||||
const char *name;
|
||||
#else
|
||||
size_t privkey_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern const struct mlkem_type_info MLKEM768_INFO;
|
||||
#ifdef HAVE_MLKEM1024
|
||||
extern const struct mlkem_type_info MLKEM1024_INFO;
|
||||
#endif
|
||||
|
||||
#define MLKEM_SHARED_SECRET_SIZE 32
|
||||
|
||||
typedef unsigned char ssh_mlkem_shared_secret[MLKEM_SHARED_SECRET_SIZE];
|
||||
|
||||
const struct mlkem_type_info *
|
||||
kex_type_to_mlkem_info(enum ssh_key_exchange_e kex_type);
|
||||
|
||||
int ssh_mlkem_init(ssh_session session);
|
||||
|
||||
int ssh_mlkem_encapsulate(ssh_session session,
|
||||
ssh_mlkem_shared_secret shared_secret);
|
||||
|
||||
int ssh_mlkem_decapsulate(const ssh_session session,
|
||||
ssh_mlkem_shared_secret shared_secret);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MLKEM_H_ */
|
||||
127
include/libssh/mlkem_native.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2025 by Red Hat, Inc.
|
||||
*
|
||||
* Author: Jakub Jelen <jjelen@redhat.com>
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, 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.
|
||||
*/
|
||||
|
||||
#ifndef MLKEM_NATIVE_H_
|
||||
#define MLKEM_NATIVE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
A monomorphic instance of libcrux_ml_kem.types.MlKemPrivateKey
|
||||
with const generics
|
||||
- $2400size_t
|
||||
*/
|
||||
typedef struct libcrux_ml_kem_types_MlKemPrivateKey_d9_s {
|
||||
uint8_t value[2400U];
|
||||
} libcrux_ml_kem_types_MlKemPrivateKey_d9;
|
||||
|
||||
/**
|
||||
A monomorphic instance of libcrux_ml_kem.types.MlKemPublicKey
|
||||
with const generics
|
||||
- $1184size_t
|
||||
*/
|
||||
typedef struct libcrux_ml_kem_types_MlKemPublicKey_30_s {
|
||||
uint8_t value[1184U];
|
||||
} libcrux_ml_kem_types_MlKemPublicKey_30;
|
||||
|
||||
typedef struct libcrux_ml_kem_mlkem768_MlKem768KeyPair_s {
|
||||
libcrux_ml_kem_types_MlKemPrivateKey_d9 sk;
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 pk;
|
||||
} libcrux_ml_kem_mlkem768_MlKem768KeyPair;
|
||||
|
||||
typedef struct libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s {
|
||||
uint8_t value[1088U];
|
||||
} libcrux_ml_kem_mlkem768_MlKem768Ciphertext;
|
||||
|
||||
/**
|
||||
A monomorphic instance of K.
|
||||
with types libcrux_ml_kem_types_MlKemCiphertext[[$1088size_t]],
|
||||
uint8_t[32size_t]
|
||||
|
||||
*/
|
||||
typedef struct tuple_c2_s {
|
||||
libcrux_ml_kem_mlkem768_MlKem768Ciphertext fst;
|
||||
uint8_t snd[32U];
|
||||
} tuple_c2;
|
||||
|
||||
/**
|
||||
Generate ML-KEM 768 Key Pair
|
||||
*/
|
||||
libcrux_ml_kem_mlkem768_MlKem768KeyPair
|
||||
libcrux_ml_kem_mlkem768_portable_generate_key_pair(uint8_t randomness[64U]);
|
||||
|
||||
/**
|
||||
Validate a public key.
|
||||
|
||||
Returns `true` if valid, and `false` otherwise.
|
||||
*/
|
||||
bool libcrux_ml_kem_mlkem768_portable_validate_public_key(
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
|
||||
|
||||
/**
|
||||
Encapsulate ML-KEM 768
|
||||
|
||||
Generates an ([`MlKem768Ciphertext`], [`MlKemSharedSecret`]) tuple.
|
||||
The input is a reference to an [`MlKem768PublicKey`] and [`SHARED_SECRET_SIZE`]
|
||||
bytes of `randomness`.
|
||||
*/
|
||||
tuple_c2 libcrux_ml_kem_mlkem768_portable_encapsulate(
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
|
||||
uint8_t randomness[32U]);
|
||||
|
||||
/**
|
||||
Decapsulate ML-KEM 768
|
||||
|
||||
Generates an [`MlKemSharedSecret`].
|
||||
The input is a reference to an [`MlKem768PrivateKey`] and an
|
||||
[`MlKem768Ciphertext`].
|
||||
*/
|
||||
void libcrux_ml_kem_mlkem768_portable_decapsulate(
|
||||
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
|
||||
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
|
||||
uint8_t ret[32U]);
|
||||
|
||||
/* rename some types to be a bit more ergonomic */
|
||||
#define libcrux_mlkem768_keypair libcrux_ml_kem_mlkem768_MlKem768KeyPair_s
|
||||
#define libcrux_mlkem768_pk libcrux_ml_kem_types_MlKemPublicKey_30_s
|
||||
#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_d9_s
|
||||
#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s
|
||||
#define libcrux_mlkem768_enc_result tuple_c2_s
|
||||
/* defines for PRNG inputs */
|
||||
#define LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN 64U
|
||||
#define LIBCRUX_ML_KEM_ENC_PRNG_LEN 32
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MLKEM_NATIVE_H_ */
|
||||
@@ -46,6 +46,10 @@
|
||||
#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
|
||||
@@ -86,6 +90,14 @@ 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;
|
||||
|
||||
/* Resident key specific metadata */
|
||||
ssh_string sk_user_id;
|
||||
};
|
||||
|
||||
struct ssh_signature_struct {
|
||||
@@ -137,6 +149,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);
|
||||
|
||||
103
include/libssh/pki_context.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PKI_CONTEXT_H
|
||||
#define PKI_CONTEXT_H
|
||||
|
||||
#include "libssh/callbacks.h"
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
/**
|
||||
* @brief Security key context structure
|
||||
*
|
||||
* Context structure containing all parameters and callbacks
|
||||
* needed for FIDO2/U2F security key operations.
|
||||
*/
|
||||
struct ssh_pki_ctx_struct {
|
||||
/** @brief Desired RSA modulus size in bits
|
||||
*
|
||||
* Specified size of RSA keys to generate. If set to 0, defaults to 3072
|
||||
* bits. Must be greater than or equal to 1024, as anything below is
|
||||
* considered insecure.
|
||||
*/
|
||||
int rsa_key_size;
|
||||
|
||||
/** @brief Security key callbacks
|
||||
*
|
||||
* Provides enroll/sign/load_resident_keys operations.
|
||||
*/
|
||||
const struct ssh_sk_callbacks_struct *sk_callbacks;
|
||||
|
||||
/** @brief Application identifier string for the security key credential
|
||||
*
|
||||
* FIDO2 relying party identifier, typically "ssh:user@hostname" format.
|
||||
* This is required for all security key operations.
|
||||
*/
|
||||
char *sk_application;
|
||||
|
||||
/** @brief FIDO2 operation flags
|
||||
*
|
||||
* Bitfield controlling authenticator behavior. Combine with bitwise OR:
|
||||
* - SSH_SK_USER_PRESENCE_REQD (0x01): Require user touch
|
||||
* - SSH_SK_USER_VERIFICATION_REQD (0x04): Require PIN/biometric
|
||||
* - SSH_SK_FORCE_OPERATION (0x10): Override duplicate detection
|
||||
* - SSH_SK_RESIDENT_KEY (0x20): Create discoverable credential
|
||||
*/
|
||||
uint8_t sk_flags;
|
||||
|
||||
/** @brief PIN callback for authenticator user verification (optional)
|
||||
*
|
||||
* Callback invoked to obtain a PIN or perform user verification when
|
||||
* SSH_SK_USER_VERIFICATION_REQD is set or the authenticator requires it.
|
||||
* If NULL, no interactive PIN retrieval is performed.
|
||||
*/
|
||||
ssh_auth_callback sk_pin_callback;
|
||||
|
||||
/** @brief User supplied pointer passed to callbacks (optional)
|
||||
*
|
||||
* Generic pointer set by the application and forwarded to
|
||||
* interactive callbacks (e.g. PIN callback) to allow applications to
|
||||
* carry state context.
|
||||
*/
|
||||
void *sk_userdata;
|
||||
|
||||
/** @brief Custom challenge data for enrollment (optional)
|
||||
*
|
||||
* Buffer containing challenge data signed by the authenticator.
|
||||
* If NULL, a random 32-byte challenge is automatically generated.
|
||||
*/
|
||||
ssh_buffer sk_challenge_buffer;
|
||||
|
||||
/** @brief Options to be passed to the sk_callbacks (optional)
|
||||
*
|
||||
* NULL-terminated array of sk_option pointers owned by this context.
|
||||
*/
|
||||
struct sk_option **sk_callbacks_options;
|
||||
|
||||
/** @brief The buffer used to store attestation information returned in a
|
||||
* key enrollment operation
|
||||
*/
|
||||
ssh_buffer sk_attestation_buffer;
|
||||
};
|
||||
|
||||
/* Internal PKI context functions */
|
||||
ssh_pki_ctx ssh_pki_ctx_dup(const ssh_pki_ctx context);
|
||||
|
||||
#endif /* PKI_CONTEXT_H */
|
||||
@@ -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);
|
||||
@@ -163,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
|
||||
|
||||
90
include/libssh/pki_sk.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PKI_SK_H
|
||||
#define PKI_SK_H
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/pki.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SSH_SK_MAX_USER_ID_LEN 64
|
||||
|
||||
/**
|
||||
* @brief Enroll a new security key using a U2F/FIDO2 authenticator
|
||||
*
|
||||
* Creates a new security key credential configured according to the parameters
|
||||
* in the PKI context. This function handles key enrollment for both ECDSA and
|
||||
* Ed25519 algorithms, generates appropriate challenges, and returns the
|
||||
* enrolled key with optional attestation data.
|
||||
*
|
||||
* The PKI context must be configured with appropriate security key parameters
|
||||
* using ssh_pki_ctx_options_set() before calling this function. Required
|
||||
* options include SSH_PKI_OPTION_SK_APPLICATION, SSH_PKI_OPTION_SK_USER_ID, and
|
||||
* SSH_PKI_OPTION_SK_CALLBACKS.
|
||||
*
|
||||
* @param[in] context The PKI context containing security key configuration and
|
||||
* parameters
|
||||
* @param[in] key_type The type of key to enroll (SSH_KEYTYPE_SK_ECDSA or
|
||||
* SSH_KEYTYPE_SK_ED25519)
|
||||
* @param[out] enrolled_key_result Pointer to store the enrolled ssh_key
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on failure
|
||||
*
|
||||
* @see ssh_pki_ctx_new()
|
||||
* @see ssh_pki_ctx_options_set()
|
||||
* @see ssh_pki_ctx_get_sk_attestation_buffer()
|
||||
*/
|
||||
int pki_sk_enroll_key(ssh_pki_ctx context,
|
||||
enum ssh_keytypes_e key_type,
|
||||
ssh_key *enrolled_key_result);
|
||||
|
||||
/**
|
||||
* @brief Sign arbitrary data using a security key and a PKI context
|
||||
*
|
||||
* This function performs signing operations configured according to the
|
||||
* parameters in the PKI context and returns a properly formatted
|
||||
* ssh_signature. The caller must free the signature when it is no longer
|
||||
* needed.
|
||||
*
|
||||
* The PKI context should be configured with appropriate security key parameters
|
||||
* using ssh_pki_ctx_options_set() before calling this function. The security
|
||||
* key must have been previously enrolled or loaded.
|
||||
*
|
||||
* @param[in] context The PKI context containing security key configuration and
|
||||
* parameters
|
||||
* @param[in] key The security key to use for signing
|
||||
* @param[in] data The data to sign
|
||||
* @param[in] data_len Length of data to sign
|
||||
*
|
||||
* @return A valid ssh_signature on success, NULL on failure
|
||||
*
|
||||
* @see ssh_pki_ctx_new()
|
||||
* @see ssh_pki_ctx_options_set()
|
||||
* @see pki_sk_enroll_key()
|
||||
* @see ssh_signature_free()
|
||||
*/
|
||||
ssh_signature pki_sk_do_sign(ssh_pki_ctx context,
|
||||
const ssh_key key,
|
||||
const uint8_t *data,
|
||||
size_t data_len);
|
||||
|
||||
#endif /* PKI_SK_H */
|
||||
@@ -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);
|
||||
|
||||
@@ -365,9 +365,29 @@ int ssh_connector_remove_event(ssh_connector connector);
|
||||
/** Get the size of an array */
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
|
||||
#ifndef HAVE_EXPLICIT_BZERO
|
||||
void explicit_bzero(void *s, size_t n);
|
||||
#endif /* !HAVE_EXPLICIT_BZERO */
|
||||
/** Securely zero memory in a way that won't be optimized away */
|
||||
#if defined(HAVE_MEMSET_EXPLICIT)
|
||||
#define ssh_burn(ptr, len) memset_explicit((ptr), '\0', (len))
|
||||
#elif defined(HAVE_EXPLICIT_BZERO)
|
||||
#define ssh_burn(ptr, len) explicit_bzero((ptr), (len))
|
||||
#elif defined(HAVE_MEMSET_S)
|
||||
#define ssh_burn(ptr, len) memset_s((ptr), (len), '\0', (len))
|
||||
#elif defined(HAVE_SECURE_ZERO_MEMORY)
|
||||
#define ssh_burn(ptr, len) SecureZeroMemory((ptr), (len))
|
||||
#else
|
||||
#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION)
|
||||
#define ssh_burn(ptr, len) \
|
||||
do { \
|
||||
memset((ptr), '\0', (len)); \
|
||||
__asm__ volatile("" : : "g"(ptr) : "memory"); \
|
||||
} while (0)
|
||||
#else
|
||||
#define ssh_burn(ptr, len) \
|
||||
do { \
|
||||
memset((ptr), '\0', (len)); \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void burn_free(void *ptr, size_t len);
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ enum ssh_bind_options_e {
|
||||
SSH_BIND_OPTIONS_MODULI,
|
||||
SSH_BIND_OPTIONS_RSA_MIN_SIZE,
|
||||
SSH_BIND_OPTIONS_IMPORT_KEY_STR,
|
||||
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
|
||||
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
|
||||
};
|
||||
|
||||
typedef struct ssh_bind_struct* ssh_bind;
|
||||
@@ -118,7 +120,7 @@ LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o);
|
||||
*
|
||||
* @param[in] userdata A pointer to private data to pass to the callbacks.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
|
||||
*
|
||||
* @code
|
||||
* struct ssh_callbacks_struct cb = {
|
||||
@@ -172,7 +174,7 @@ LIBSSH_API void ssh_bind_fd_toaccept(ssh_bind ssh_bind_o);
|
||||
* @param ssh_bind_o The ssh server bind to accept a connection.
|
||||
* @param session A preallocated ssh session
|
||||
* @see ssh_new
|
||||
* @return SSH_OK when a connection is established
|
||||
* @return `SSH_OK` when a connection is established
|
||||
*/
|
||||
LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session);
|
||||
|
||||
@@ -186,7 +188,7 @@ LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session);
|
||||
* inbound connection
|
||||
* @see ssh_new
|
||||
* @see ssh_bind_accept
|
||||
* @return SSH_OK when a connection is established
|
||||
* @return `SSH_OK` when a connection is established
|
||||
*/
|
||||
LIBSSH_API int ssh_bind_accept_fd(ssh_bind ssh_bind_o, ssh_session session,
|
||||
socket_t fd);
|
||||
@@ -198,7 +200,7 @@ LIBSSH_API ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session);
|
||||
*
|
||||
* @param session A connected ssh session
|
||||
* @see ssh_bind_accept
|
||||
* @return SSH_OK if the key exchange was successful
|
||||
* @return `SSH_OK` if the key exchange was successful
|
||||
*/
|
||||
LIBSSH_API int ssh_handle_key_exchange(ssh_session session);
|
||||
|
||||
@@ -215,9 +217,8 @@ LIBSSH_API int ssh_handle_key_exchange(ssh_session session);
|
||||
* @see ssh_handle_key_exchange
|
||||
* @see ssh_options_set
|
||||
*
|
||||
* @return SSH_OK if initialization succeeds.
|
||||
* @return `SSH_OK` if initialization succeeds.
|
||||
*/
|
||||
|
||||
LIBSSH_API int ssh_server_init_kex(ssh_session session);
|
||||
|
||||
/**
|
||||
@@ -246,6 +247,7 @@ LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o);
|
||||
* SSH_AUTH_METHOD_HOSTBASED
|
||||
* SSH_AUTH_METHOD_INTERACTIVE
|
||||
* SSH_AUTH_METHOD_GSSAPI_MIC
|
||||
* SSH_AUTH_METHOD_GSSAPI_KEYEX
|
||||
*/
|
||||
LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
|
||||
|
||||
@@ -257,7 +259,7 @@ LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
|
||||
*
|
||||
* @param[in] banner The server's banner.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
LIBSSH_API int ssh_send_issue_banner(ssh_session session, const ssh_string banner);
|
||||
|
||||
|
||||
@@ -58,16 +58,17 @@ enum ssh_dh_state_e {
|
||||
};
|
||||
|
||||
enum ssh_pending_call_e {
|
||||
SSH_PENDING_CALL_NONE = 0,
|
||||
SSH_PENDING_CALL_CONNECT,
|
||||
SSH_PENDING_CALL_AUTH_NONE,
|
||||
SSH_PENDING_CALL_AUTH_PASSWORD,
|
||||
SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
|
||||
SSH_PENDING_CALL_AUTH_PUBKEY,
|
||||
SSH_PENDING_CALL_AUTH_AGENT,
|
||||
SSH_PENDING_CALL_AUTH_KBDINT_INIT,
|
||||
SSH_PENDING_CALL_AUTH_KBDINT_SEND,
|
||||
SSH_PENDING_CALL_AUTH_GSSAPI_MIC
|
||||
SSH_PENDING_CALL_NONE = 0,
|
||||
SSH_PENDING_CALL_CONNECT,
|
||||
SSH_PENDING_CALL_AUTH_NONE,
|
||||
SSH_PENDING_CALL_AUTH_PASSWORD,
|
||||
SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
|
||||
SSH_PENDING_CALL_AUTH_PUBKEY,
|
||||
SSH_PENDING_CALL_AUTH_AGENT,
|
||||
SSH_PENDING_CALL_AUTH_KBDINT_INIT,
|
||||
SSH_PENDING_CALL_AUTH_KBDINT_SEND,
|
||||
SSH_PENDING_CALL_AUTH_GSSAPI_MIC,
|
||||
SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX,
|
||||
};
|
||||
|
||||
/* libssh calls may block an undefined amount of time */
|
||||
@@ -89,6 +90,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 */
|
||||
@@ -198,6 +202,8 @@ struct ssh_session_struct {
|
||||
*/
|
||||
bool first_kex_follows_guess_wrong;
|
||||
|
||||
ssh_string gssapi_key_exchange_mic;
|
||||
|
||||
ssh_buffer in_hashbuf;
|
||||
ssh_buffer out_hashbuf;
|
||||
struct ssh_crypto_struct *current_crypto;
|
||||
@@ -262,6 +268,8 @@ struct ssh_session_struct {
|
||||
char compressionlevel;
|
||||
char *gss_server_identity;
|
||||
char *gss_client_identity;
|
||||
bool gssapi_key_exchange;
|
||||
char *gssapi_key_exchange_algs;
|
||||
int gss_delegate_creds;
|
||||
int flags;
|
||||
int exp_flags;
|
||||
@@ -274,6 +282,7 @@ struct ssh_session_struct {
|
||||
bool identities_only;
|
||||
int control_master;
|
||||
char *control_path;
|
||||
int address_family;
|
||||
} opts;
|
||||
|
||||
/* server options */
|
||||
@@ -285,6 +294,10 @@ struct ssh_session_struct {
|
||||
/* counters */
|
||||
ssh_counter socket_counter;
|
||||
ssh_counter raw_counter;
|
||||
|
||||
/* PKI context structure containing various parameters to configure PKI
|
||||
* operations */
|
||||
struct ssh_pki_ctx_struct *pki_context;
|
||||
};
|
||||
|
||||
/** @internal
|
||||
|
||||
@@ -74,6 +74,21 @@ typedef struct sftp_file_struct* sftp_file;
|
||||
typedef struct sftp_message_struct* sftp_message;
|
||||
typedef struct sftp_packet_struct* sftp_packet;
|
||||
typedef struct sftp_request_queue_struct* sftp_request_queue;
|
||||
|
||||
/**
|
||||
* @brief SFTP session handle.
|
||||
*
|
||||
* This type represents an active SFTP session associated with an SSH channel.
|
||||
* It is created and destroyed via the libssh SFTP API and is internally
|
||||
* managed by libssh. It is used by applications to perform SFTP operations
|
||||
* such as file access and directory management.
|
||||
*
|
||||
* The internal structure of this type is opaque and must not be accessed
|
||||
* directly by applications.
|
||||
*
|
||||
* @see sftp_new
|
||||
* @see sftp_free
|
||||
*/
|
||||
typedef struct sftp_session_struct* sftp_session;
|
||||
typedef struct sftp_status_message_struct* sftp_status_message;
|
||||
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
|
||||
|
||||
283
include/libssh/sk_api.h
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is a copy of the OpenSSH project's sk-api.h file pulled from
|
||||
* https://github.com/openssh/openssh-portable/commit/a9cbe10da2be5be76755af0cea029db0f9c1f263
|
||||
* with only the flags, algorithms, error codes, and struct definitions. The
|
||||
* function declarations and other OpenSSH-specific code have been removed.
|
||||
*/
|
||||
|
||||
#ifndef SK_API_H
|
||||
#define SK_API_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* FIDO2/U2F Operation Flags */
|
||||
|
||||
/** Requires user presence confirmation (tap/touch) */
|
||||
#ifndef SSH_SK_USER_PRESENCE_REQD
|
||||
#define SSH_SK_USER_PRESENCE_REQD 0x01
|
||||
#endif
|
||||
|
||||
/** Requires user verification (PIN/biometric) - FIDO2 only */
|
||||
#ifndef SSH_SK_USER_VERIFICATION_REQD
|
||||
#define SSH_SK_USER_VERIFICATION_REQD 0x04
|
||||
#endif
|
||||
|
||||
/** Force resident key enrollment even if a resident key with given user ID
|
||||
* already exists - FIDO2 only */
|
||||
#ifndef SSH_SK_FORCE_OPERATION
|
||||
#define SSH_SK_FORCE_OPERATION 0x10
|
||||
#endif
|
||||
|
||||
/** Create/use resident key stored on authenticator - FIDO2 only */
|
||||
#ifndef SSH_SK_RESIDENT_KEY
|
||||
#define SSH_SK_RESIDENT_KEY 0x20
|
||||
#endif
|
||||
|
||||
/* Algorithms */
|
||||
|
||||
/** ECDSA with P-256 curve */
|
||||
#define SSH_SK_ECDSA 0x00
|
||||
|
||||
/** Ed25519 - FIDO2 only */
|
||||
#define SSH_SK_ED25519 0x01
|
||||
|
||||
/* Error codes */
|
||||
|
||||
/** General unspecified failure */
|
||||
#define SSH_SK_ERR_GENERAL -1
|
||||
|
||||
/** Requested algorithm/feature/option not supported */
|
||||
#define SSH_SK_ERR_UNSUPPORTED -2
|
||||
|
||||
/** PIN (or other user verification) required but either missing or invalid */
|
||||
#define SSH_SK_ERR_PIN_REQUIRED -3
|
||||
|
||||
/** No suitable security key / authenticator device was found */
|
||||
#define SSH_SK_ERR_DEVICE_NOT_FOUND -4
|
||||
|
||||
/** Attempt to create a resident key that already exists (duplicate) */
|
||||
#define SSH_SK_ERR_CREDENTIAL_EXISTS -5
|
||||
|
||||
/**
|
||||
* @brief Response structure for FIDO2/U2F key enrollment operations
|
||||
*
|
||||
* Contains all data returned by a FIDO2/U2F authenticator after successful
|
||||
* enrollment of a new credential.
|
||||
*/
|
||||
struct sk_enroll_response {
|
||||
/** @brief FIDO2/U2F authenticator flags from the enrollment operation
|
||||
*
|
||||
* Contains flags indicating authenticator capabilities and state during
|
||||
* enrollment, such as user presence (UP), user verification
|
||||
* (UV), and resident key.
|
||||
*/
|
||||
uint8_t flags;
|
||||
|
||||
/** @brief Public key data in standard format
|
||||
*
|
||||
* For ECDSA (P-256): 65 bytes in SEC1 uncompressed point format
|
||||
* (0x04 prefix + 32-byte X coordinate + 32-byte Y coordinate)
|
||||
* For Ed25519: 32 bytes containing the raw public key (FIDO2 only)
|
||||
*/
|
||||
uint8_t *public_key;
|
||||
|
||||
/** @brief Length of public_key buffer in bytes
|
||||
*
|
||||
* Expected values: 65 for ECDSA P-256, 32 for Ed25519
|
||||
*/
|
||||
size_t public_key_len;
|
||||
|
||||
/** @brief Opaque credential handle/ID used to identify this key
|
||||
*
|
||||
* Authenticator-generated binary data that uniquely identifies this
|
||||
* credential. Used in subsequent sign operations to specify which
|
||||
* key to use. Format and contents are authenticator-specific.
|
||||
*/
|
||||
uint8_t *key_handle;
|
||||
|
||||
/** @brief Length of key_handle buffer in bytes
|
||||
*
|
||||
* Length varies by authenticator.
|
||||
*/
|
||||
size_t key_handle_len;
|
||||
|
||||
/** @brief Enrollment signature over the enrollment data
|
||||
*
|
||||
* FIDO2/U2F authenticator signature proving the credential was created
|
||||
* by this specific authenticator. Used for enrollment verification.
|
||||
* Format depends on algorithm.
|
||||
*/
|
||||
uint8_t *signature;
|
||||
|
||||
/** @brief Length of signature buffer in bytes
|
||||
*
|
||||
* Length varies by algorithm.
|
||||
*/
|
||||
size_t signature_len;
|
||||
|
||||
/** @brief X.509 attestation certificate
|
||||
*
|
||||
* Certificate that attests to the authenticity of the authenticator
|
||||
* and the enrollment operation. Used to verify the authenticator's
|
||||
* identity and manufacturer.
|
||||
*/
|
||||
uint8_t *attestation_cert;
|
||||
|
||||
/** @brief Length of attestation_cert buffer in bytes */
|
||||
size_t attestation_cert_len;
|
||||
|
||||
/** @brief FIDO2/U2F authenticator data from enrollment
|
||||
*
|
||||
* CBOR-encoded authenticator data containing RP ID hash, flags,
|
||||
* counter, and attested credential data. Used for attestation
|
||||
* verification according to the FIDO2 specification.
|
||||
*/
|
||||
uint8_t *authdata;
|
||||
|
||||
/** @brief Length of authdata buffer in bytes
|
||||
*
|
||||
* Length varies depending on credential data and extensions.
|
||||
*/
|
||||
size_t authdata_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Response structure for FIDO2/U2F key signing operations
|
||||
*
|
||||
* Contains signature components and metadata returned by a FIDO2/U2F
|
||||
* authenticator after a successful signing operation.
|
||||
*/
|
||||
struct sk_sign_response {
|
||||
/** @brief FIDO2/U2F authenticator flags from the signing operation
|
||||
*
|
||||
* Contains flags indicating authenticator state during signing,
|
||||
* including user presence (UP) and user verification (UV) flags.
|
||||
* Used to verify that proper user interaction occurred while signing.
|
||||
*/
|
||||
uint8_t flags;
|
||||
|
||||
/** @brief Authenticator signature counter value
|
||||
*
|
||||
* Monotonically increasing counter maintained by the authenticator.
|
||||
* Incremented on each successful signing operation. Used to detect
|
||||
* cloned or duplicated authenticators.
|
||||
*/
|
||||
uint32_t counter;
|
||||
|
||||
/** @brief R component of ECDSA signature or Ed25519 signature */
|
||||
uint8_t *sig_r;
|
||||
|
||||
/** @brief Length of sig_r buffer in bytes */
|
||||
size_t sig_r_len;
|
||||
|
||||
/** @brief S component of ECDSA signature */
|
||||
uint8_t *sig_s;
|
||||
|
||||
/** @brief Length of sig_s buffer in bytes */
|
||||
size_t sig_s_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing a resident/discoverable credential
|
||||
*
|
||||
* Represents a FIDO2 resident key (discoverable credential) that is
|
||||
* stored on the authenticator and can be discovered without providing
|
||||
* a credential ID.
|
||||
*/
|
||||
struct sk_resident_key {
|
||||
/** @brief Cryptographic algorithm identifier for this key
|
||||
*
|
||||
* SSH_SK_ECDSA (0x00): ECDSA with P-256 curve
|
||||
* SSH_SK_ED25519 (0x01): Ed25519 signature algorithm
|
||||
*/
|
||||
uint32_t alg;
|
||||
|
||||
/** @brief Slot/index number of this key on the authenticator
|
||||
*
|
||||
* Zero-based index indicating the position of this resident key
|
||||
* in the authenticator's internal storage. Used for key management
|
||||
* and identification when multiple resident keys exist.
|
||||
*/
|
||||
size_t slot;
|
||||
|
||||
/** @brief Relying Party (application) identifier string
|
||||
*
|
||||
* The RP ID (typically a domain name) that this resident key
|
||||
* is associated with. Determines which application/service
|
||||
* this key can be used for.
|
||||
*/
|
||||
char *application;
|
||||
|
||||
/** @brief Embedded enrollment response containing key material
|
||||
*
|
||||
* Contains the same data as returned during initial enrollment,
|
||||
* including public key, key handle, and associated metadata.
|
||||
*/
|
||||
struct sk_enroll_response key;
|
||||
|
||||
/** @brief Flags associated with this resident key
|
||||
*
|
||||
* SSH_SK_USER_PRESENCE_REQD: Requires user presence for operations
|
||||
* SSH_SK_USER_VERIFICATION_REQD: Requires user verification
|
||||
* (PIN/biometric)
|
||||
*/
|
||||
uint8_t flags;
|
||||
|
||||
/** @brief User identifier associated with this resident key
|
||||
*
|
||||
* Binary user ID that was provided during key enrollment.
|
||||
* Used to identify which user account this key belongs to.
|
||||
*/
|
||||
uint8_t *user_id;
|
||||
|
||||
/** @brief Length of user_id buffer in bytes
|
||||
*
|
||||
* Length of the user identifier.
|
||||
*/
|
||||
size_t user_id_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configuration option structure for FIDO2/U2F operations
|
||||
*
|
||||
* Represents a single configuration parameter that can be passed
|
||||
* to FIDO2/U2F middleware.
|
||||
*/
|
||||
struct sk_option {
|
||||
/** @brief Option name/key identifier */
|
||||
char *name;
|
||||
|
||||
/** @brief Option value as bytes */
|
||||
char *value;
|
||||
|
||||
/** @brief Indicates if this option is required for the operation
|
||||
*
|
||||
* Non-zero if this option must be processed and cannot be ignored.
|
||||
* Zero if this option is advisory and can be skipped if the
|
||||
* middleware does not support it.
|
||||
*/
|
||||
uint8_t required;
|
||||
};
|
||||
|
||||
/** Current SK API version */
|
||||
#define SSH_SK_VERSION_MAJOR 0x000a0000
|
||||
#define SSH_SK_VERSION_MAJOR_MASK 0xffff0000
|
||||
|
||||
#endif /* SK_API_H */
|
||||
213
include/libssh/sk_common.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SK_COMMON_H
|
||||
#define SK_COMMON_H
|
||||
|
||||
#include "libssh/callbacks.h"
|
||||
#include "libssh/sk_api.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SK_MAX_USER_ID_LEN 64
|
||||
|
||||
#define SK_NOT_SUPPORTED_MSG \
|
||||
"Security Key functionality is not supported in this build of libssh. " \
|
||||
"Please enable support by building using the WITH_FIDO2 build option."
|
||||
|
||||
/**
|
||||
* @brief Convert security key error code to human-readable string
|
||||
*
|
||||
* Converts a security key error code to a descriptive string representation
|
||||
* that can be used for logging user-facing error messages.
|
||||
*
|
||||
* @param[in] sk_err The security key error code to convert.
|
||||
*
|
||||
* @return Constant string describing the error. Never returns NULL.
|
||||
* Returns "Unknown error" for unrecognized error codes.
|
||||
*
|
||||
* @note The returned string is statically allocated and should not be freed.
|
||||
*/
|
||||
const char *ssh_sk_err_to_string(int sk_err);
|
||||
|
||||
/**
|
||||
* @brief Securely clear the contents of an sk_enroll_response structure
|
||||
*
|
||||
* Overwrites sensitive data within the enrollment response structure with
|
||||
* zeros to prevent information leakage. This function only clears and frees the
|
||||
* contents and does not free the structure itself.
|
||||
*
|
||||
* @param[in] enroll_response The enrollment response structure to clear.
|
||||
* Can be NULL (no operation performed).
|
||||
*
|
||||
* @note This function only frees the memory for the contents and does not free
|
||||
* memory for the structure itself. Use sk_enroll_response_free() for complete
|
||||
* cleanup, which also performs secure clearing internally.
|
||||
*/
|
||||
void sk_enroll_response_burn(struct sk_enroll_response *enroll_response);
|
||||
|
||||
/**
|
||||
* @brief Securely free an sk_enroll_response structure
|
||||
*
|
||||
* Performs secure clearing of sensitive data within the enrollment response
|
||||
* structure before freeing the allocated memory. This function internally
|
||||
* calls sk_enroll_response_burn() before deallocation.
|
||||
*
|
||||
* @param[in] enroll_response The enrollment response structure to free.
|
||||
* Can be NULL (no operation performed).
|
||||
*
|
||||
* @note Developers do not need to call sk_enroll_response_burn() before
|
||||
* calling this function, as secure clearing is performed automatically.
|
||||
*/
|
||||
void sk_enroll_response_free(struct sk_enroll_response *enroll_response);
|
||||
|
||||
/**
|
||||
* @brief Free an sk_sign_response structure
|
||||
*
|
||||
* Frees the memory allocated for a sign response structure and all its
|
||||
* associated data. This function performs secure clearing of sensitive
|
||||
* data before deallocation.
|
||||
*
|
||||
* @param[in] sign_response The sign response structure to free.
|
||||
* Can be NULL (no operation performed).
|
||||
*
|
||||
* @note This is a secure free operation that clears sensitive data before
|
||||
* memory deallocation to prevent information leakage.
|
||||
*/
|
||||
void sk_sign_response_free(struct sk_sign_response *sign_response);
|
||||
|
||||
/**
|
||||
* @brief Free an sk_resident_key structure
|
||||
*
|
||||
* Frees the memory allocated for a resident key structure and all its
|
||||
* associated data. This function performs secure clearing of sensitive
|
||||
* data before deallocation.
|
||||
*
|
||||
* @param[in] resident_key The resident key structure to free.
|
||||
* Can be NULL (no operation performed).
|
||||
*
|
||||
* @note This is a secure free operation that clears sensitive data before
|
||||
* memory deallocation to prevent information leakage.
|
||||
*/
|
||||
void sk_resident_key_free(struct sk_resident_key *resident_key);
|
||||
|
||||
/**
|
||||
* @brief Free an sk_option array and all its contents
|
||||
*
|
||||
* Frees a NULL-terminated array of sk_option structures, including all
|
||||
* allocated memory for option names and values within each structure.
|
||||
*
|
||||
* @param[in] options NULL-terminated array of sk_option pointers to free.
|
||||
* Can be NULL (no operation performed).
|
||||
*
|
||||
* @note The options array must be NULL-terminated for proper freeing.
|
||||
* Each sk_option structure and its name/value strings will be freed.
|
||||
*/
|
||||
void sk_options_free(struct sk_option **options);
|
||||
|
||||
/**
|
||||
* @brief Validate options and extract values for specific keys
|
||||
*
|
||||
* Validates that all required options are supported and extracts values
|
||||
* for the specified keys. This function is primarily intended for use
|
||||
* by the SK callback implementations.
|
||||
*
|
||||
* @param[in] options NULL-terminated array of sk_option pointers to validate.
|
||||
* @param[in] keys NULL-terminated array of supported option keys.
|
||||
* @param[out] values Pointer to array that will be allocated and filled with
|
||||
* copied values (same order as keys). The caller must free
|
||||
* this array and all contained strings when done.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if unsupported required options found
|
||||
* or memory allocation fails.
|
||||
*
|
||||
* @note The values array is allocated by this function and contains copies
|
||||
* of the option values. The caller must free both the array and all
|
||||
* non-NULL string values within it. Values for keys not found in
|
||||
* options will be set to NULL.
|
||||
*/
|
||||
int sk_options_validate_get(const struct sk_option **options,
|
||||
const char **keys,
|
||||
char ***values);
|
||||
|
||||
/**
|
||||
* @brief Duplicate an array of sk_option structures
|
||||
*
|
||||
* Creates a deep copy of an array of security key options. Each option
|
||||
* structure and its string fields are duplicated.
|
||||
*
|
||||
* @param[in] options The array of options to duplicate. Must be
|
||||
* NULL-terminated array of struct sk_option pointers.
|
||||
* Can be NULL.
|
||||
*
|
||||
* @return A newly allocated array of duplicated options on success,
|
||||
* NULL on failure or if options is NULL.
|
||||
* The returned array should be freed with SK_OPTIONS_FREE().
|
||||
*/
|
||||
struct sk_option **sk_options_dup(const struct sk_option **options);
|
||||
|
||||
/**
|
||||
* @brief Check version compatibility of security key callbacks
|
||||
*
|
||||
* Validates that the provided security key callbacks use an SK API
|
||||
* version whose major portion is the same as the major version that libssh
|
||||
* supports.
|
||||
*
|
||||
* @param[in] callbacks Pointer to the sk_callbacks structure to check.
|
||||
*
|
||||
* @return true if the callbacks are compatible, false otherwise.
|
||||
*/
|
||||
bool sk_callbacks_check_compatibility(
|
||||
const struct ssh_sk_callbacks_struct *callbacks);
|
||||
|
||||
/* Convenience macros for secure freeing with NULL checks and pointer reset */
|
||||
#define SK_ENROLL_RESPONSE_FREE(x) \
|
||||
do { \
|
||||
if ((x) != NULL) { \
|
||||
sk_enroll_response_free(x); \
|
||||
x = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SK_SIGN_RESPONSE_FREE(x) \
|
||||
do { \
|
||||
if ((x) != NULL) { \
|
||||
sk_sign_response_free(x); \
|
||||
x = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SK_RESIDENT_KEY_FREE(x) \
|
||||
do { \
|
||||
if ((x) != NULL) { \
|
||||
sk_resident_key_free(x); \
|
||||
x = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SK_OPTIONS_FREE(x) \
|
||||
do { \
|
||||
if ((x) != NULL) { \
|
||||
sk_options_free(x); \
|
||||
x = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* SK_COMMON_H */
|
||||
37
include/libssh/sk_usbhid.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SK_USBHID_H
|
||||
#define SK_USBHID_H
|
||||
|
||||
/**
|
||||
* @brief Get the USB-HID security key callbacks.
|
||||
*
|
||||
* This function returns a pointer to the implementation of
|
||||
* security key callbacks for FIDO2/U2F devices using the USB-HID
|
||||
* protocol.
|
||||
*
|
||||
* @return Pointer to the ssh_sk_callbacks_struct
|
||||
*
|
||||
* @see ssh_sk_callbacks_struct
|
||||
*/
|
||||
const struct ssh_sk_callbacks_struct *ssh_sk_get_usbhid_callbacks(void);
|
||||
|
||||
#endif /* SK_USBHID_H */
|
||||
@@ -35,10 +35,6 @@ extern "C" {
|
||||
#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
|
||||
|
||||
@@ -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,14 +16,23 @@
|
||||
#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
|
||||
#define SSH2_MSG_KEX_DH_GEX_INIT 32
|
||||
#define SSH2_MSG_KEX_DH_GEX_REPLY 33
|
||||
#define SSH2_MSG_KEX_DH_GEX_REQUEST 34
|
||||
|
||||
#define SSH2_MSG_KEXGSS_INIT 30
|
||||
#define SSH2_MSG_KEXGSS_CONTINUE 31
|
||||
#define SSH2_MSG_KEXGSS_COMPLETE 32
|
||||
#define SSH2_MSG_KEXGSS_HOSTKEY 33
|
||||
#define SSH2_MSG_KEXGSS_ERROR 34
|
||||
#define SSH2_MSG_KEXGSS_GROUPREQ 40
|
||||
#define SSH2_MSG_KEXGSS_GROUP 41
|
||||
|
||||
#define SSH2_MSG_USERAUTH_REQUEST 50
|
||||
#define SSH2_MSG_USERAUTH_FAILURE 51
|
||||
#define SSH2_MSG_USERAUTH_SUCCESS 52
|
||||
|
||||
@@ -75,6 +75,7 @@ MD5CTX md5_init(void);
|
||||
void md5_ctx_free(MD5CTX);
|
||||
int md5_update(MD5CTX c, const void *data, size_t len);
|
||||
int md5_final(unsigned char *md, MD5CTX c);
|
||||
int md5(const unsigned char *digest, size_t len, unsigned char *hash);
|
||||
|
||||
SHACTX sha1_init(void);
|
||||
void sha1_ctx_free(SHACTX);
|
||||
|
||||
@@ -56,6 +56,18 @@ if (MINGW AND Threads_FOUND)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (HAVE_LIBFIDO2)
|
||||
set(LIBSSH_PRIVATE_INCLUDE_DIRS
|
||||
${LIBSSH_PRIVATE_INCLUDE_DIRS}
|
||||
${LIBFIDO2_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(LIBSSH_LINK_LIBRARIES
|
||||
${LIBSSH_LINK_LIBRARIES}
|
||||
${LIBFIDO2_LIBRARIES}
|
||||
)
|
||||
endif (HAVE_LIBFIDO2)
|
||||
|
||||
# The ws2_32 needs to be last for mingw to build
|
||||
# https://gitlab.com/libssh/libssh-mirror/-/issues/84
|
||||
if (WIN32)
|
||||
@@ -93,6 +105,7 @@ set(libssh_SRCS
|
||||
error.c
|
||||
getpass.c
|
||||
gzip.c
|
||||
hybrid_mlkem.c
|
||||
init.c
|
||||
kdf.c
|
||||
kex.c
|
||||
@@ -103,12 +116,14 @@ set(libssh_SRCS
|
||||
match.c
|
||||
messages.c
|
||||
misc.c
|
||||
mlkem.c
|
||||
options.c
|
||||
packet.c
|
||||
packet_cb.c
|
||||
packet_crypt.c
|
||||
pcap.c
|
||||
pki.c
|
||||
pki_context.c
|
||||
pki_container_openssh.c
|
||||
poll.c
|
||||
session.c
|
||||
@@ -182,6 +197,13 @@ if (WITH_GCRYPT)
|
||||
curve25519_gcrypt.c
|
||||
)
|
||||
endif(HAVE_GCRYPT_CURVE25519)
|
||||
|
||||
if (HAVE_GCRYPT_MLKEM)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
mlkem_gcrypt.c
|
||||
)
|
||||
endif (HAVE_GCRYPT_MLKEM)
|
||||
elseif (WITH_MBEDTLS)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
@@ -235,6 +257,12 @@ else (WITH_GCRYPT)
|
||||
chachapoly.c
|
||||
)
|
||||
endif (NOT HAVE_OPENSSL_EVP_CHACHA20)
|
||||
if (HAVE_OPENSSL_MLKEM)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
mlkem_crypto.c
|
||||
)
|
||||
endif (HAVE_OPENSSL_MLKEM)
|
||||
endif (WITH_GCRYPT)
|
||||
|
||||
if (WITH_SFTP)
|
||||
@@ -273,6 +301,7 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
gssapi.c
|
||||
kex-gss.c
|
||||
)
|
||||
endif (WITH_GSSAPI AND GSSAPI_FOUND)
|
||||
|
||||
@@ -286,6 +315,34 @@ if (NOT WITH_NACL)
|
||||
endif()
|
||||
endif (NOT WITH_NACL)
|
||||
|
||||
if (NOT HAVE_MLKEM1024)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
mlkem_native.c
|
||||
external/libcrux_mlkem768_sha3.c
|
||||
)
|
||||
if (WITH_WERROR_DECLARATION_AFTER_STATEMENT_FLAG)
|
||||
set_source_files_properties(external/libcrux_mlkem768_sha3.c
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS -Wno-error=declaration-after-statement)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_FIDO2)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
sk_common.c
|
||||
pki_sk.c
|
||||
)
|
||||
|
||||
if (HAVE_LIBFIDO2)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
sk_usbhid.c
|
||||
)
|
||||
endif (HAVE_LIBFIDO2)
|
||||
endif (WITH_FIDO2)
|
||||
|
||||
# Set the path to the default map file
|
||||
set(MAP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.map")
|
||||
|
||||
|
||||
384
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,
|
||||
|
||||
158
src/auth.c
@@ -32,19 +32,19 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/agent.h"
|
||||
#include "libssh/auth.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/gssapi.h"
|
||||
#include "libssh/keys.h"
|
||||
#include "libssh/legacy.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/packet.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/keys.h"
|
||||
#include "libssh/auth.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/gssapi.h"
|
||||
#include "libssh/legacy.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
/**
|
||||
* @defgroup libssh_auth The SSH authentication functions
|
||||
@@ -88,6 +88,7 @@ static int ssh_auth_response_termination(void *user)
|
||||
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
|
||||
case SSH_AUTH_STATE_GSSAPI_TOKEN:
|
||||
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
|
||||
case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
|
||||
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
|
||||
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
|
||||
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
|
||||
@@ -118,9 +119,14 @@ static const char *ssh_auth_get_current_method(ssh_session session)
|
||||
case SSH_AUTH_METHOD_INTERACTIVE:
|
||||
method = "keyboard interactive";
|
||||
break;
|
||||
#ifdef WITH_GSSAPI
|
||||
case SSH_AUTH_METHOD_GSSAPI_MIC:
|
||||
method = "gssapi";
|
||||
break;
|
||||
case SSH_AUTH_METHOD_GSSAPI_KEYEX:
|
||||
method = "gssapi-keyex";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -175,6 +181,7 @@ static int ssh_userauth_get_response(ssh_session session)
|
||||
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
|
||||
case SSH_AUTH_STATE_GSSAPI_TOKEN:
|
||||
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
|
||||
case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
|
||||
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
|
||||
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
|
||||
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
|
||||
@@ -269,9 +276,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
|
||||
if (strstr(auth_methods, "hostbased") != NULL) {
|
||||
session->auth.supported_methods |= SSH_AUTH_METHOD_HOSTBASED;
|
||||
}
|
||||
#ifdef WITH_GSSAPI
|
||||
if (strstr(auth_methods, "gssapi-with-mic") != NULL) {
|
||||
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_MIC;
|
||||
}
|
||||
if (strstr(auth_methods, "gssapi-keyex") != NULL) {
|
||||
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_KEYEX;
|
||||
}
|
||||
#endif
|
||||
|
||||
end:
|
||||
session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN;
|
||||
@@ -536,7 +548,8 @@ static int build_pubkey_auth_request(ssh_session session,
|
||||
int rc;
|
||||
const char *auth_method = "publickey";
|
||||
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND &&
|
||||
session->current_crypto->server_pubkey != NULL) {
|
||||
auth_method = "publickey-hostbound-v00@openssh.com";
|
||||
}
|
||||
|
||||
@@ -555,7 +568,8 @@ static int build_pubkey_auth_request(ssh_session session,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND &&
|
||||
session->current_crypto->server_pubkey != NULL) {
|
||||
rc = add_hostbound_pubkey(session);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
@@ -1027,12 +1041,11 @@ int ssh_userauth_agent(ssh_session session, const char *username)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1846,7 +1859,7 @@ void ssh_kbdint_free(ssh_kbdint kbd)
|
||||
if (kbd->prompts) {
|
||||
for (i = 0; i < n; i++) {
|
||||
if (kbd->prompts[i] != NULL) {
|
||||
explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i]));
|
||||
ssh_burn(kbd->prompts[i], strlen(kbd->prompts[i]));
|
||||
}
|
||||
SAFE_FREE(kbd->prompts[i]);
|
||||
}
|
||||
@@ -1857,7 +1870,7 @@ void ssh_kbdint_free(ssh_kbdint kbd)
|
||||
if (kbd->answers) {
|
||||
for (i = 0; i < n; i++) {
|
||||
if (kbd->answers[i] != NULL) {
|
||||
explicit_bzero(kbd->answers[i], strlen(kbd->answers[i]));
|
||||
ssh_burn(kbd->answers[i], strlen(kbd->answers[i]));
|
||||
}
|
||||
SAFE_FREE(kbd->answers[i]);
|
||||
}
|
||||
@@ -1882,7 +1895,7 @@ void ssh_kbdint_clean(ssh_kbdint kbd)
|
||||
n = kbd->nprompts;
|
||||
if (kbd->prompts) {
|
||||
for (i = 0; i < n; i++) {
|
||||
explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i]));
|
||||
ssh_burn(kbd->prompts[i], strlen(kbd->prompts[i]));
|
||||
SAFE_FREE(kbd->prompts[i]);
|
||||
}
|
||||
SAFE_FREE(kbd->prompts);
|
||||
@@ -1892,7 +1905,7 @@ void ssh_kbdint_clean(ssh_kbdint kbd)
|
||||
|
||||
if (kbd->answers) {
|
||||
for (i = 0; i < n; i++) {
|
||||
explicit_bzero(kbd->answers[i], strlen(kbd->answers[i]));
|
||||
ssh_burn(kbd->answers[i], strlen(kbd->answers[i]));
|
||||
SAFE_FREE(kbd->answers[i]);
|
||||
}
|
||||
SAFE_FREE(kbd->answers);
|
||||
@@ -2373,8 +2386,8 @@ ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
|
||||
}
|
||||
|
||||
if (session->kbdint->answers[i]) {
|
||||
explicit_bzero(session->kbdint->answers[i],
|
||||
strlen(session->kbdint->answers[i]));
|
||||
ssh_burn(session->kbdint->answers[i],
|
||||
strlen(session->kbdint->answers[i]));
|
||||
SAFE_FREE(session->kbdint->answers[i]);
|
||||
}
|
||||
|
||||
@@ -2447,4 +2460,111 @@ pending:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to authenticate through the "gssapi-keyex" method.
|
||||
*
|
||||
* @param[in] session The ssh session to use.
|
||||
*
|
||||
* @returns
|
||||
* - `SSH_AUTH_ERROR`: A serious error happened.
|
||||
* - `SSH_AUTH_DENIED`: Authentication failed : use another method.
|
||||
* - `SSH_AUTH_PARTIAL`: You've been partially authenticated, you still
|
||||
* have to use another method.
|
||||
* - `SSH_AUTH_SUCCESS`: Authentication success.
|
||||
* - `SSH_AUTH_AGAIN`: In nonblocking mode, you've got to call this again
|
||||
* later.
|
||||
*/
|
||||
int ssh_userauth_gssapi_keyex(ssh_session session)
|
||||
{
|
||||
int rc = SSH_AUTH_DENIED;
|
||||
#ifdef WITH_GSSAPI
|
||||
OM_uint32 min_stat;
|
||||
gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
switch (session->pending_call_state) {
|
||||
case SSH_PENDING_CALL_NONE:
|
||||
break;
|
||||
case SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX:
|
||||
goto pending;
|
||||
default:
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Wrong state (%d) during pending SSH call",
|
||||
session->pending_call_state);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Check if GSSAPI Key exchange was performed */
|
||||
if (!ssh_kex_is_gss(session->current_crypto)) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Attempt to authenticate with gssapi-keyex without "
|
||||
"doing GSSAPI Key exchange.");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->gssapi == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_userauth_request_service(session);
|
||||
if (rc == SSH_AGAIN) {
|
||||
return SSH_AUTH_AGAIN;
|
||||
} else if (rc == SSH_ERROR) {
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-keyex");
|
||||
|
||||
session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_KEYEX;
|
||||
session->auth.state = SSH_AUTH_STATE_NONE;
|
||||
session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX;
|
||||
|
||||
SAFE_FREE(session->gssapi->user);
|
||||
session->gssapi->user = strdup(session->opts.username);
|
||||
if (session->gssapi->user == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
rc = ssh_gssapi_auth_keyex_mic(session, &mic_token_buf);
|
||||
if (rc != SSH_OK) {
|
||||
session->auth.state = SSH_AUTH_STATE_NONE;
|
||||
session->pending_call_state = SSH_PENDING_CALL_NONE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bsssdP",
|
||||
SSH2_MSG_USERAUTH_REQUEST,
|
||||
session->opts.username,
|
||||
"ssh-connection",
|
||||
"gssapi-keyex",
|
||||
mic_token_buf.length,
|
||||
(size_t)mic_token_buf.length,
|
||||
mic_token_buf.value);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
session->auth.state = SSH_AUTH_STATE_NONE;
|
||||
session->pending_call_state = SSH_PENDING_CALL_NONE;
|
||||
gss_release_buffer(&min_stat, &mic_token_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
gss_release_buffer(&min_stat, &mic_token_buf);
|
||||
|
||||
session->auth.state = SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT;
|
||||
|
||||
ssh_packet_send(session);
|
||||
|
||||
pending:
|
||||
rc = ssh_userauth_get_response(session);
|
||||
if (rc != SSH_AUTH_AGAIN) {
|
||||
session->pending_call_state = SSH_PENDING_CALL_NONE;
|
||||
}
|
||||
#else
|
||||
(void)session; /* unused */
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
63
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) {
|
||||
@@ -261,8 +245,13 @@ int ssh_bind_listen(ssh_bind sshbind) {
|
||||
sshbind->ecdsa == NULL &&
|
||||
sshbind->ed25519 == NULL) {
|
||||
rc = ssh_bind_import_keys(sshbind);
|
||||
if (rc != SSH_OK) {
|
||||
return SSH_ERROR;
|
||||
if (rc == SSH_ERROR) {
|
||||
if (!sshbind->gssapi_key_exchange) {
|
||||
ssh_set_error(sshbind, SSH_FATAL, "No usable hostkeys found");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"No usable hostkeys found: Using \"null\" hostkey algorithm");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,10 +278,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)
|
||||
@@ -402,6 +391,7 @@ void ssh_bind_free(ssh_bind sshbind){
|
||||
SAFE_FREE(sshbind->rsakey);
|
||||
SAFE_FREE(sshbind->ecdsakey);
|
||||
SAFE_FREE(sshbind->ed25519key);
|
||||
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
|
||||
|
||||
ssh_key_free(sshbind->rsa);
|
||||
sshbind->rsa = NULL;
|
||||
@@ -479,6 +469,17 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
|
||||
}
|
||||
|
||||
session->common.log_verbosity = sshbind->common.log_verbosity;
|
||||
session->opts.gssapi_key_exchange = sshbind->gssapi_key_exchange;
|
||||
|
||||
if (sshbind->gssapi_key_exchange_algs != NULL) {
|
||||
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
|
||||
session->opts.gssapi_key_exchange_algs =
|
||||
strdup(sshbind->gssapi_key_exchange_algs);
|
||||
if (session->opts.gssapi_key_exchange_algs == NULL) {
|
||||
ssh_set_error_oom(sshbind);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (sshbind->banner != NULL) {
|
||||
session->server_opts.custombanner = strdup(sshbind->banner);
|
||||
@@ -505,7 +506,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);
|
||||
@@ -522,8 +526,13 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
|
||||
sshbind->ecdsa == NULL &&
|
||||
sshbind->ed25519 == NULL) {
|
||||
rc = ssh_bind_import_keys(sshbind);
|
||||
if (rc != SSH_OK) {
|
||||
return SSH_ERROR;
|
||||
if (rc == SSH_ERROR) {
|
||||
if (!sshbind->gssapi_key_exchange) {
|
||||
ssh_set_error(sshbind, SSH_FATAL, "No usable hostkeys found");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"No usable hostkeys found: Using \"null\" hostkey algorithm");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,19 @@ 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 && l <= INT_MAX && (*parser_flags & PARSING)) {
|
||||
int i = (int)l;
|
||||
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSA_MIN_SIZE, &i);
|
||||
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);
|
||||
|
||||
69
src/buffer.c
@@ -156,10 +156,10 @@ void ssh_buffer_free(struct ssh_buffer_struct *buffer)
|
||||
|
||||
if (buffer->secure && buffer->allocated > 0) {
|
||||
/* burn the data */
|
||||
explicit_bzero(buffer->data, buffer->allocated);
|
||||
ssh_burn(buffer->data, buffer->allocated);
|
||||
SAFE_FREE(buffer->data);
|
||||
|
||||
explicit_bzero(buffer, sizeof(struct ssh_buffer_struct));
|
||||
ssh_burn(buffer, sizeof(struct ssh_buffer_struct));
|
||||
} else {
|
||||
SAFE_FREE(buffer->data);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ static int realloc_buffer(struct ssh_buffer_struct *buffer, uint32_t needed)
|
||||
return -1;
|
||||
}
|
||||
memcpy(new, buffer->data, buffer->used);
|
||||
explicit_bzero(buffer->data, buffer->used);
|
||||
ssh_burn(buffer->data, buffer->used);
|
||||
SAFE_FREE(buffer->data);
|
||||
} else {
|
||||
new = realloc(buffer->data, needed);
|
||||
@@ -241,7 +241,7 @@ static void buffer_shift(ssh_buffer buffer)
|
||||
|
||||
if (buffer->secure) {
|
||||
void *ptr = buffer->data + buffer->used;
|
||||
explicit_bzero(ptr, burn_pos);
|
||||
ssh_burn(ptr, burn_pos);
|
||||
}
|
||||
|
||||
buffer_verify(buffer);
|
||||
@@ -266,7 +266,7 @@ int ssh_buffer_reinit(struct ssh_buffer_struct *buffer)
|
||||
buffer_verify(buffer);
|
||||
|
||||
if (buffer->secure && buffer->allocated > 0) {
|
||||
explicit_bzero(buffer->data, buffer->allocated);
|
||||
ssh_burn(buffer->data, buffer->allocated);
|
||||
}
|
||||
buffer->used = 0;
|
||||
buffer->pos = 0;
|
||||
@@ -617,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
|
||||
*
|
||||
@@ -1305,28 +1352,28 @@ cleanup:
|
||||
case 'b':
|
||||
o.byte = va_arg(ap_copy, uint8_t *);
|
||||
if (buffer->secure) {
|
||||
explicit_bzero(o.byte, sizeof(uint8_t));
|
||||
ssh_burn(o.byte, sizeof(uint8_t));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
o.word = va_arg(ap_copy, uint16_t *);
|
||||
if (buffer->secure) {
|
||||
explicit_bzero(o.word, sizeof(uint16_t));
|
||||
ssh_burn(o.word, sizeof(uint16_t));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
o.dword = va_arg(ap_copy, uint32_t *);
|
||||
if (buffer->secure) {
|
||||
explicit_bzero(o.dword, sizeof(uint32_t));
|
||||
ssh_burn(o.dword, sizeof(uint32_t));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
o.qword = va_arg(ap_copy, uint64_t *);
|
||||
if (buffer->secure) {
|
||||
explicit_bzero(o.qword, sizeof(uint64_t));
|
||||
ssh_burn(o.qword, sizeof(uint64_t));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -1344,7 +1391,7 @@ cleanup:
|
||||
case 's':
|
||||
o.cstring = va_arg(ap_copy, char **);
|
||||
if (buffer->secure) {
|
||||
explicit_bzero(*o.cstring, strlen(*o.cstring));
|
||||
ssh_burn(*o.cstring, strlen(*o.cstring));
|
||||
}
|
||||
SAFE_FREE(*o.cstring);
|
||||
break;
|
||||
@@ -1352,7 +1399,7 @@ cleanup:
|
||||
len = va_arg(ap_copy, size_t);
|
||||
o.data = va_arg(ap_copy, void **);
|
||||
if (buffer->secure) {
|
||||
explicit_bzero(*o.data, len);
|
||||
ssh_burn(*o.data, len);
|
||||
}
|
||||
SAFE_FREE(*o.data);
|
||||
break;
|
||||
|
||||
280
src/channels.c
@@ -321,7 +321,7 @@ static int ssh_channel_open_termination(void *c)
|
||||
*
|
||||
* @param[in] payload The buffer containing additional payload for the query.
|
||||
*
|
||||
* @return SSH_OK if successful; SSH_ERROR otherwise.
|
||||
* @return `SSH_OK` if successful; `SSH_ERROR` otherwise.
|
||||
*/
|
||||
static int
|
||||
channel_open(ssh_channel channel,
|
||||
@@ -433,7 +433,7 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id)
|
||||
* @brief grows the local window and sends a packet to the other party
|
||||
* @param session SSH session
|
||||
* @param channel SSH channel
|
||||
* @return SSH_OK if successful; SSH_ERROR otherwise.
|
||||
* @return `SSH_OK` if successful; `SSH_ERROR` otherwise.
|
||||
*/
|
||||
static int grow_window(ssh_session session,
|
||||
ssh_channel channel)
|
||||
@@ -636,11 +636,13 @@ SSH_PACKET_CALLBACK(channel_rcv_data)
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Channel receiving %" PRIu32 " bytes data%s (local win=%" PRIu32
|
||||
" remote win=%" PRIu32 ")",
|
||||
" remote win=%" PRIu32 ") on channel %" PRIu32 ":%" PRIu32,
|
||||
len,
|
||||
is_stderr ? " in stderr" : "",
|
||||
is_stderr ? " in stderr" : "",
|
||||
channel->local_window,
|
||||
channel->remote_window);
|
||||
channel->remote_window,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
|
||||
if (len > channel->local_window) {
|
||||
SSH_LOG(SSH_LOG_RARE,
|
||||
@@ -831,8 +833,10 @@ SSH_PACKET_CALLBACK(channel_rcv_request)
|
||||
channel->exit.status = true;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"received exit-status %u",
|
||||
channel->exit.code);
|
||||
"received exit-status %u on channel %" PRIu32 ":%" PRIu32,
|
||||
channel->exit.code,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
|
||||
ssh_callbacks_execute_list(channel->callbacks,
|
||||
ssh_channel_callbacks,
|
||||
@@ -1055,9 +1059,9 @@ int channel_default_bufferize(ssh_channel channel,
|
||||
*
|
||||
* @param[in] channel An allocated channel.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @see ssh_channel_open_forward()
|
||||
@@ -1080,15 +1084,15 @@ int ssh_channel_open_session(ssh_channel channel)
|
||||
|
||||
/**
|
||||
* @brief Open an agent authentication forwarding channel. This type of channel
|
||||
* can be opened by a server towards a client in order to provide SSH-Agent services
|
||||
* to the server-side process. This channel can only be opened if the client
|
||||
* claimed support by sending a channel request beforehand.
|
||||
* can be opened by a server towards a client in order to provide SSH-Agent
|
||||
* services to the server-side process. This channel can only be opened if the
|
||||
* client claimed support by sending a channel request beforehand.
|
||||
*
|
||||
* @param[in] channel An allocated channel.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @see ssh_channel_open_forward()
|
||||
@@ -1106,7 +1110,6 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Open a TCP/IP forwarding channel.
|
||||
*
|
||||
@@ -1123,14 +1126,14 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
|
||||
* @param[in] localport The port on the host from where the connection
|
||||
* originated. This is mostly for logging purposes.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @warning This function does not bind the local port and does not automatically
|
||||
* forward the content of a socket to the channel. You still have to
|
||||
* use ssh_channel_read and ssh_channel_write for this.
|
||||
* @warning This function does not bind the local port and does not
|
||||
* automatically forward the content of a socket to the channel. You still have
|
||||
* to use ssh_channel_read and ssh_channel_write for this.
|
||||
*/
|
||||
int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
|
||||
int remoteport, const char *sourcehost, int localport)
|
||||
@@ -1195,16 +1198,16 @@ error:
|
||||
* @param[in] localport The port on the host from where the connection
|
||||
* originated. This is mostly for logging purposes.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK on` success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @warning This function does not bind the local port and does not
|
||||
* automatically forward the content of a socket to the channel.
|
||||
* You still have to use ssh_channel_read and ssh_channel_write for this.
|
||||
* automatically forward the content of a socket to the channel.
|
||||
* You still have to use ssh_channel_read and ssh_channel_write for this.
|
||||
* @warning Requires support of OpenSSH for UNIX domain socket forwarding.
|
||||
*/
|
||||
*/
|
||||
int ssh_channel_open_forward_unix(ssh_channel channel,
|
||||
const char *remotepath,
|
||||
const char *sourcehost,
|
||||
@@ -1355,7 +1358,7 @@ void ssh_channel_do_free(ssh_channel channel)
|
||||
*
|
||||
* @param[in] channel The channel to send the eof to.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
|
||||
*
|
||||
* Example:
|
||||
@code
|
||||
@@ -1432,7 +1435,7 @@ error:
|
||||
*
|
||||
* @param[in] channel The channel to close.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
|
||||
*
|
||||
* @see ssh_channel_free()
|
||||
* @see ssh_channel_is_eof()
|
||||
@@ -1520,11 +1523,11 @@ static int ssh_waitsession_unblocked(void *s)
|
||||
/**
|
||||
* @internal
|
||||
* @brief Flushes a channel (and its session) until the output buffer
|
||||
* is empty, or timeout elapsed.
|
||||
* is empty, or timeout elapsed.
|
||||
* @param channel SSH channel
|
||||
* @return SSH_OK On success,
|
||||
* SSH_ERROR On error.
|
||||
* SSH_AGAIN Timeout elapsed (or in nonblocking mode).
|
||||
* @return `SSH_OK` On success,
|
||||
* `SSH_ERROR` On error.
|
||||
* `SSH_AGAIN` Timeout elapsed (or in nonblocking mode).
|
||||
*/
|
||||
int ssh_channel_flush(ssh_channel channel)
|
||||
{
|
||||
@@ -1715,7 +1718,7 @@ uint32_t ssh_channel_window_size(ssh_channel channel)
|
||||
*
|
||||
* @param[in] len The length of the buffer to write to.
|
||||
*
|
||||
* @return The number of bytes written, SSH_ERROR on error.
|
||||
* @return The number of bytes written, `SSH_ERROR` on error.
|
||||
*
|
||||
* @see ssh_channel_read()
|
||||
*/
|
||||
@@ -1921,7 +1924,10 @@ static int channel_request(ssh_channel channel, const char *request,
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Sent a SSH_MSG_CHANNEL_REQUEST %s", request);
|
||||
"Sent a SSH_MSG_CHANNEL_REQUEST %s on channel %" PRIu32 ":%" PRIu32,
|
||||
request,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
if (reply == 0) {
|
||||
channel->request_state = SSH_CHANNEL_REQ_STATE_NONE;
|
||||
return SSH_OK;
|
||||
@@ -1941,13 +1947,20 @@ pending:
|
||||
rc=SSH_ERROR;
|
||||
break;
|
||||
case SSH_CHANNEL_REQ_STATE_DENIED:
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
"Channel request %s failed", request);
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Channel request %s failed on channel %" PRIu32 ":%" PRIu32,
|
||||
request,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
rc=SSH_ERROR;
|
||||
break;
|
||||
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Channel request %s success",request);
|
||||
"Channel request %s success on channel %" PRIu32 ":%" PRIu32,
|
||||
request,
|
||||
channel->local_channel,
|
||||
channel->remote_channel);
|
||||
rc=SSH_OK;
|
||||
break;
|
||||
case SSH_CHANNEL_REQ_STATE_PENDING:
|
||||
@@ -1983,9 +1996,9 @@ error:
|
||||
*
|
||||
* @param[in] modes_len Number of bytes in 'modes'
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*/
|
||||
int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal,
|
||||
@@ -2041,6 +2054,19 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request a PTY with a specific size using current TTY modes.
|
||||
*
|
||||
* Encodes @p terminal modes from the current TTY and sends a PTY request
|
||||
* for the given channel, terminal type, and size in columns/rows.
|
||||
*
|
||||
* @param[in] channel The channel to send the request on.
|
||||
* @param[in] terminal The terminal type (e.g. "xterm").
|
||||
* @param[in] col Number of columns.
|
||||
* @param[in] row Number of rows.
|
||||
*
|
||||
* @return `SSH_OK` on success; `SSH_ERROR` on failure.
|
||||
*/
|
||||
int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
|
||||
int col, int row)
|
||||
{
|
||||
@@ -2063,9 +2089,9 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
|
||||
*
|
||||
* @param[in] channel The channel to send the request.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @see ssh_channel_request_pty_size()
|
||||
@@ -2084,7 +2110,7 @@ int ssh_channel_request_pty(ssh_channel channel)
|
||||
*
|
||||
* @param[in] rows The new number of rows.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
|
||||
*
|
||||
* @warning Do not call it from a signal handler if you are not sure any other
|
||||
* libssh function using the same channel/session is running at the
|
||||
@@ -2125,9 +2151,9 @@ error:
|
||||
*
|
||||
* @param[in] channel The channel to send the request.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*/
|
||||
int ssh_channel_request_shell(ssh_channel channel)
|
||||
@@ -2146,9 +2172,9 @@ int ssh_channel_request_shell(ssh_channel channel)
|
||||
*
|
||||
* @param[in] subsys The subsystem to request (for example "sftp").
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @warning You normally don't have to call it for sftp, see sftp_new().
|
||||
@@ -2196,9 +2222,9 @@ error:
|
||||
*
|
||||
* @param[in] channel The channel to request the sftp subsystem.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @note You should use sftp_new() which does this for you.
|
||||
@@ -2252,9 +2278,9 @@ static char *generate_cookie(void)
|
||||
*
|
||||
* @param[in] screen_number The screen number.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*/
|
||||
int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
|
||||
@@ -2387,15 +2413,16 @@ ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an "auth-agent-req" channel request over an existing session channel.
|
||||
* @brief Send an "auth-agent-req" channel request over an existing session
|
||||
* channel.
|
||||
*
|
||||
* This client-side request will enable forwarding the agent over an secure tunnel.
|
||||
* When the server is ready to open one authentication agent channel, an
|
||||
* This client-side request will enable forwarding the agent over an secure
|
||||
* tunnel. When the server is ready to open one authentication agent channel, an
|
||||
* ssh_channel_open_request_auth_agent_callback event will be generated.
|
||||
*
|
||||
* @param[in] channel The channel to send signal.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
|
||||
*/
|
||||
int ssh_channel_request_auth_agent(ssh_channel channel) {
|
||||
if (channel == NULL) {
|
||||
@@ -2476,9 +2503,9 @@ static int ssh_global_request_termination(void *s)
|
||||
*
|
||||
* @param[in] reply Set if you expect a reply from server.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*/
|
||||
int ssh_global_request(ssh_session session,
|
||||
@@ -2570,7 +2597,7 @@ error:
|
||||
|
||||
/**
|
||||
* @brief Sends the "tcpip-forward" global request to ask the server to begin
|
||||
* listening for inbound connections.
|
||||
* listening for inbound connections.
|
||||
*
|
||||
* @param[in] session The ssh session to send the request.
|
||||
*
|
||||
@@ -2585,9 +2612,9 @@ error:
|
||||
* @param[in] bound_port The pointer to get actual bound port. Pass NULL to
|
||||
* ignore.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
**/
|
||||
int ssh_channel_listen_forward(ssh_session session,
|
||||
@@ -2694,9 +2721,9 @@ ssh_channel ssh_channel_open_forward_port(ssh_session session, int timeout_ms, i
|
||||
*
|
||||
* @param[in] port The bound port on the server.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*/
|
||||
int ssh_channel_cancel_forward(ssh_session session,
|
||||
@@ -2745,9 +2772,9 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port)
|
||||
*
|
||||
* @param[in] value The value to set.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
* @warning Some environment variables may be refused by security reasons.
|
||||
*/
|
||||
@@ -2801,9 +2828,9 @@ error:
|
||||
* @param[in] cmd The command to execute
|
||||
* (e.g. "ls ~/ -al | grep -i reports").
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* Example:
|
||||
@@ -2866,9 +2893,9 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a signal to remote process (as described in RFC 4254, section 6.9).
|
||||
* @brief Send a signal to remote process (as described in RFC 4254,
|
||||
* section 6.9).
|
||||
*
|
||||
* Sends a signal 'sig' to the remote process.
|
||||
* Note, that remote system may not support signals concept.
|
||||
@@ -2892,7 +2919,7 @@ error:
|
||||
* SIGUSR1 -> USR1 \n
|
||||
* SIGUSR2 -> USR2 \n
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
|
||||
*/
|
||||
int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
|
||||
{
|
||||
@@ -2925,7 +2952,6 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a break signal to the server (as described in RFC 4335).
|
||||
*
|
||||
@@ -2937,7 +2963,7 @@ error:
|
||||
*
|
||||
* @param[in] length The break-length in milliseconds to send.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
|
||||
*/
|
||||
int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
|
||||
{
|
||||
@@ -2975,13 +3001,14 @@ error:
|
||||
* @param[out] buffer The buffer which will get the data.
|
||||
*
|
||||
* @param[in] count The count of bytes to be read. If it is bigger than 0,
|
||||
* the exact size will be read, else (bytes=0) it will
|
||||
* return once anything is available.
|
||||
* the exact size will be read, else (bytes=0) it will return once anything is
|
||||
* available.
|
||||
*
|
||||
* @param is_stderr A boolean value to mark reading from the stderr stream.
|
||||
*
|
||||
* @return The number of bytes read, 0 on end of file, SSH_AGAIN on
|
||||
* timeout and SSH_ERROR on error.
|
||||
* @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
|
||||
* on timeout and `SSH_ERROR` on error.
|
||||
*
|
||||
* @deprecated Please use ssh_channel_read instead
|
||||
* @warning This function doesn't work in nonblocking/timeout mode
|
||||
* @see ssh_channel_read
|
||||
@@ -3084,11 +3111,11 @@ static int ssh_channel_read_termination(void *s)
|
||||
*
|
||||
* @param[in] is_stderr A boolean value to mark reading from the stderr flow.
|
||||
*
|
||||
* @return The number of bytes read, 0 on end of file, SSH_AGAIN on
|
||||
* timeout and SSH_ERROR on error.
|
||||
* @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
|
||||
* on timeout and `SSH_ERROR` on error.
|
||||
*
|
||||
* @warning This function may return less than count bytes of data, and won't
|
||||
* block until count bytes have been read.
|
||||
* block until count bytes have been read.
|
||||
*/
|
||||
int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
|
||||
{
|
||||
@@ -3113,8 +3140,8 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
|
||||
* @param[in] timeout_ms A timeout in milliseconds. A value of -1 means
|
||||
* infinite timeout.
|
||||
*
|
||||
* @return The number of bytes read, 0 on end of file, SSH_AGAIN on
|
||||
* timeout, SSH_ERROR on error.
|
||||
* @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
|
||||
* on timeout, `SSH_ERROR` on error.
|
||||
*
|
||||
* @warning This function may return less than count bytes of data, and won't
|
||||
* block until count bytes have been read.
|
||||
@@ -3224,8 +3251,8 @@ int ssh_channel_read_timeout(ssh_channel channel,
|
||||
*
|
||||
* @param[in] is_stderr A boolean to select the stderr stream.
|
||||
*
|
||||
* @return The number of bytes read, SSH_AGAIN if nothing is
|
||||
* available, SSH_ERROR on error, and SSH_EOF if the channel is EOF.
|
||||
* @return The number of bytes read, `SSH_AGAIN` if nothing is
|
||||
* available, `SSH_ERROR` on error, and `SSH_EOF` if the channel is EOF.
|
||||
*
|
||||
* @see ssh_channel_is_eof()
|
||||
*/
|
||||
@@ -3282,11 +3309,11 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
|
||||
* @param[in] is_stderr A boolean to select the stderr stream.
|
||||
*
|
||||
* @return The number of bytes available for reading, 0 if nothing
|
||||
* is available or SSH_ERROR on error.
|
||||
* is available or `SSH_ERROR` on error.
|
||||
* When a channel is freed the function returns
|
||||
* SSH_ERROR immediately.
|
||||
* `SSH_ERROR` immediately.
|
||||
*
|
||||
* @warning When the channel is in EOF state, the function returns SSH_EOF.
|
||||
* @warning When the channel is in EOF state, the function returns `SSH_EOF`.
|
||||
*
|
||||
* @see ssh_channel_is_eof()
|
||||
*/
|
||||
@@ -3329,18 +3356,19 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr)
|
||||
*
|
||||
* @param[in] channel The channel to poll.
|
||||
* @param[in] timeout Set an upper limit on the time for which this function
|
||||
* will block, in milliseconds. Specifying a negative value
|
||||
* means an infinite timeout. This parameter is passed to
|
||||
* the poll() function.
|
||||
* will block, in milliseconds. Specifying a negative
|
||||
* value means an infinite timeout. This parameter is
|
||||
* passed to the poll() function.
|
||||
* @param[in] is_stderr A boolean to select the stderr stream.
|
||||
*
|
||||
* @return The number of bytes available for reading,
|
||||
* 0 if nothing is available (timeout elapsed),
|
||||
* SSH_EOF on end of file,
|
||||
* SSH_ERROR on error.
|
||||
* `SSH_EOF` on end of file,
|
||||
* `SSH_ERROR` on error.
|
||||
*
|
||||
* @warning When the channel is in EOF state, the function returns SSH_EOF.
|
||||
* When a channel is freed the function returns SSH_ERROR immediately.
|
||||
* @warning When the channel is in EOF state, the function returns `SSH_EOF`.
|
||||
* When a channel is freed the function returns `SSH_ERROR`
|
||||
* immediately.
|
||||
*
|
||||
* @see ssh_channel_is_eof()
|
||||
*/
|
||||
@@ -3441,12 +3469,12 @@ static int ssh_channel_exit_status_termination(void *c)
|
||||
* @param[out] pcore_dumped A pointer to store a boolean value if it dumped a
|
||||
* core.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_AGAIN if we don't have a status
|
||||
* or an SSH error.
|
||||
* @return `SSH_OK` on success, `SSH_AGAIN` if we don't have a
|
||||
* status or an SSH error.
|
||||
* @warning This function may block until a timeout (or never)
|
||||
* if the other side is not willing to close the channel.
|
||||
* When a channel is freed the function returns
|
||||
* SSH_ERROR immediately.
|
||||
* `SSH_ERROR` immediately.
|
||||
*
|
||||
* If you're looking for an async handling of this register a callback for the
|
||||
* exit status!
|
||||
@@ -3510,11 +3538,11 @@ int ssh_channel_get_exit_state(ssh_channel channel,
|
||||
* @param[in] channel The channel to get the status from.
|
||||
*
|
||||
* @return The exit status, -1 if no exit status has been returned
|
||||
* (yet), or SSH_ERROR on error.
|
||||
* (yet), or `SSH_ERROR` on error.
|
||||
* @warning This function may block until a timeout (or never)
|
||||
* if the other side is not willing to close the channel.
|
||||
* When a channel is freed the function returns
|
||||
* SSH_ERROR immediately.
|
||||
* `SSH_ERROR` immediately.
|
||||
*
|
||||
* If you're looking for an async handling of this register a callback for the
|
||||
* exit status.
|
||||
@@ -3627,9 +3655,9 @@ static size_t count_ptrs(ssh_channel *ptrs)
|
||||
*
|
||||
* @param[in] timeout Timeout as defined by select(2).
|
||||
*
|
||||
* @return SSH_OK on a successful operation, SSH_EINTR if the
|
||||
* @return `SSH_OK` on a successful operation, `SSH_EINTR` if the
|
||||
* select(2) syscall was interrupted, then relaunch the
|
||||
* function, or SSH_ERROR on error.
|
||||
* function, or `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
|
||||
ssh_channel *exceptchans, struct timeval * timeout)
|
||||
@@ -3826,14 +3854,14 @@ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len
|
||||
* @param[in] localport The source port (your local computer). It's optional
|
||||
* and for logging purpose.
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
*
|
||||
* @warning This function does not bind the local port and does not automatically
|
||||
* forward the content of a socket to the channel. You still have to
|
||||
* use ssh_channel_read and ssh_channel_write for this.
|
||||
* @warning This function does not bind the local port and does not
|
||||
* automatically forward the content of a socket to the channel. You
|
||||
* still have to use ssh_channel_read and ssh_channel_write for this.
|
||||
*/
|
||||
int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
|
||||
int remoteport, const char *sourcehost, int localport)
|
||||
@@ -3891,13 +3919,13 @@ error:
|
||||
*
|
||||
* @param[in] orig_port The source port (the local server).
|
||||
*
|
||||
* @return SSH_OK on success,
|
||||
* SSH_ERROR if an error occurred,
|
||||
* SSH_AGAIN if in nonblocking mode and call has
|
||||
* @return `SSH_OK` on success,
|
||||
* `SSH_ERROR` if an error occurred,
|
||||
* `SSH_AGAIN` if in nonblocking mode and call has
|
||||
* to be done again.
|
||||
* @warning This function does not bind the local port and does not automatically
|
||||
* forward the content of a socket to the channel. You still have to
|
||||
* use shh_channel_read and ssh_channel_write for this.
|
||||
* @warning This function does not bind the local port and does not
|
||||
* automatically forward the content of a socket to the channel. You
|
||||
* still have to use shh_channel_read and ssh_channel_write for this.
|
||||
*/
|
||||
int ssh_channel_open_x11(ssh_channel channel,
|
||||
const char *orig_addr, int orig_port)
|
||||
@@ -3952,7 +3980,7 @@ error:
|
||||
*
|
||||
* @param[in] exit_status The exit status to send
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
|
||||
*/
|
||||
int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status)
|
||||
{
|
||||
@@ -3996,7 +4024,7 @@ error:
|
||||
* @param[in] errmsg A CRLF explanation text about the error condition
|
||||
* @param[in] lang The language used in the message (format: RFC 3066)
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
|
||||
*/
|
||||
int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig,
|
||||
int core, const char *errmsg, const char *lang)
|
||||
|
||||
36
src/client.c
@@ -30,14 +30,15 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/packet.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/socket.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/kex-gss.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/packet.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/socket.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#ifdef WITH_GEX
|
||||
#include "libssh/dh-gex.h"
|
||||
#endif /* WITH_GEX */
|
||||
@@ -46,6 +47,7 @@
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/kex.h"
|
||||
#include "libssh/hybrid_mlkem.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef HAVE_PTHREAD
|
||||
@@ -264,6 +266,14 @@ int dh_handshake(ssh_session session)
|
||||
switch (session->dh_handshake_state) {
|
||||
case DH_STATE_INIT:
|
||||
switch (session->next_crypto->kex_type) {
|
||||
#ifdef WITH_GSSAPI
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
rc = ssh_client_gss_kex_init(session);
|
||||
break;
|
||||
#endif
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
@@ -291,10 +301,18 @@ int dh_handshake(ssh_session 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
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
rc = ssh_client_hybrid_mlkem_init(session);
|
||||
break;
|
||||
default:
|
||||
rc = SSH_ERROR;
|
||||
}
|
||||
@@ -602,8 +620,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() &&
|
||||
@@ -845,6 +862,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));
|
||||
@@ -904,7 +922,7 @@ error:
|
||||
*/
|
||||
const char *ssh_copyright(void)
|
||||
{
|
||||
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2025 "
|
||||
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2026 "
|
||||
"Aris Adamantiadis, Andreas Schneider "
|
||||
"and libssh contributors. "
|
||||
"Distributed under the LGPL, please refer to COPYING "
|
||||
|
||||
599
src/config.c
@@ -60,100 +60,104 @@
|
||||
struct ssh_config_keyword_table_s {
|
||||
const char *name;
|
||||
enum ssh_config_opcode_e opcode;
|
||||
bool cli_supported;
|
||||
};
|
||||
|
||||
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{ "host", SOC_HOST },
|
||||
{ "match", SOC_MATCH },
|
||||
{ "hostname", SOC_HOSTNAME },
|
||||
{ "port", SOC_PORT },
|
||||
{ "user", SOC_USERNAME },
|
||||
{ "identityfile", SOC_IDENTITY },
|
||||
{ "ciphers", SOC_CIPHERS },
|
||||
{ "macs", SOC_MACS },
|
||||
{ "compression", SOC_COMPRESSION },
|
||||
{ "connecttimeout", SOC_TIMEOUT },
|
||||
{ "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK },
|
||||
{ "userknownhostsfile", SOC_KNOWNHOSTS },
|
||||
{ "proxycommand", SOC_PROXYCOMMAND },
|
||||
{ "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY },
|
||||
{ "gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY },
|
||||
{ "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS },
|
||||
{ "include", SOC_INCLUDE },
|
||||
{ "bindaddress", SOC_BINDADDRESS},
|
||||
{ "globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE},
|
||||
{ "loglevel", SOC_LOGLEVEL},
|
||||
{ "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS},
|
||||
{ "kexalgorithms", SOC_KEXALGORITHMS},
|
||||
{ "gssapiauthentication", SOC_GSSAPIAUTHENTICATION},
|
||||
{ "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION},
|
||||
{ "passwordauthentication", SOC_PASSWORDAUTHENTICATION},
|
||||
{ "pubkeyauthentication", SOC_PUBKEYAUTHENTICATION},
|
||||
{ "addkeystoagent", SOC_UNSUPPORTED},
|
||||
{ "addressfamily", SOC_UNSUPPORTED},
|
||||
{ "batchmode", SOC_UNSUPPORTED},
|
||||
{ "canonicaldomains", SOC_UNSUPPORTED},
|
||||
{ "canonicalizefallbacklocal", SOC_UNSUPPORTED},
|
||||
{ "canonicalizehostname", SOC_UNSUPPORTED},
|
||||
{ "canonicalizemaxdots", SOC_UNSUPPORTED},
|
||||
{ "canonicalizepermittedcnames", SOC_UNSUPPORTED},
|
||||
{ "certificatefile", SOC_CERTIFICATE},
|
||||
{ "kbdinteractiveauthentication", SOC_UNSUPPORTED},
|
||||
{ "checkhostip", SOC_UNSUPPORTED},
|
||||
{ "connectionattempts", SOC_UNSUPPORTED},
|
||||
{ "enablesshkeysign", SOC_UNSUPPORTED},
|
||||
{ "fingerprinthash", SOC_UNSUPPORTED},
|
||||
{ "forwardagent", SOC_UNSUPPORTED},
|
||||
{ "hashknownhosts", SOC_UNSUPPORTED},
|
||||
{ "hostbasedauthentication", SOC_UNSUPPORTED},
|
||||
{ "hostbasedacceptedalgorithms", SOC_UNSUPPORTED},
|
||||
{ "hostkeyalias", SOC_UNSUPPORTED},
|
||||
{ "identitiesonly", SOC_IDENTITIESONLY},
|
||||
{ "identityagent", SOC_IDENTITYAGENT},
|
||||
{ "ipqos", SOC_UNSUPPORTED},
|
||||
{ "kbdinteractivedevices", SOC_UNSUPPORTED},
|
||||
{ "nohostauthenticationforlocalhost", SOC_UNSUPPORTED},
|
||||
{ "numberofpasswordprompts", SOC_UNSUPPORTED},
|
||||
{ "pkcs11provider", SOC_UNSUPPORTED},
|
||||
{ "preferredauthentications", SOC_UNSUPPORTED},
|
||||
{ "proxyjump", SOC_PROXYJUMP},
|
||||
{ "proxyusefdpass", SOC_UNSUPPORTED},
|
||||
{ "pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES},
|
||||
{ "rekeylimit", SOC_REKEYLIMIT},
|
||||
{ "remotecommand", SOC_UNSUPPORTED},
|
||||
{ "revokedhostkeys", SOC_UNSUPPORTED},
|
||||
{ "serveralivecountmax", SOC_UNSUPPORTED},
|
||||
{ "serveraliveinterval", SOC_UNSUPPORTED},
|
||||
{ "streamlocalbindmask", SOC_UNSUPPORTED},
|
||||
{ "streamlocalbindunlink", SOC_UNSUPPORTED},
|
||||
{ "syslogfacility", SOC_UNSUPPORTED},
|
||||
{ "tcpkeepalive", SOC_UNSUPPORTED},
|
||||
{ "updatehostkeys", SOC_UNSUPPORTED},
|
||||
{ "verifyhostkeydns", SOC_UNSUPPORTED},
|
||||
{ "visualhostkey", SOC_UNSUPPORTED},
|
||||
{ "clearallforwardings", SOC_NA},
|
||||
{ "controlmaster", SOC_NA},
|
||||
{ "controlpersist", SOC_NA},
|
||||
{ "controlpath", SOC_NA},
|
||||
{ "dynamicforward", SOC_NA},
|
||||
{ "escapechar", SOC_NA},
|
||||
{ "exitonforwardfailure", SOC_NA},
|
||||
{ "forwardx11", SOC_NA},
|
||||
{ "forwardx11timeout", SOC_NA},
|
||||
{ "forwardx11trusted", SOC_NA},
|
||||
{ "gatewayports", SOC_NA},
|
||||
{ "ignoreunknown", SOC_NA},
|
||||
{ "localcommand", SOC_NA},
|
||||
{ "localforward", SOC_NA},
|
||||
{ "permitlocalcommand", SOC_NA},
|
||||
{ "remoteforward", SOC_NA},
|
||||
{ "requesttty", SOC_NA},
|
||||
{ "sendenv", SOC_NA},
|
||||
{ "tunnel", SOC_NA},
|
||||
{ "tunneldevice", SOC_NA},
|
||||
{ "xauthlocation", SOC_NA},
|
||||
{ "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES},
|
||||
{ NULL, SOC_UNKNOWN }
|
||||
{"host", SOC_HOST, true},
|
||||
{"match", SOC_MATCH, false},
|
||||
{"hostname", SOC_HOSTNAME, true},
|
||||
{"port", SOC_PORT, true},
|
||||
{"user", SOC_USERNAME, true},
|
||||
{"identityfile", SOC_IDENTITY, true},
|
||||
{"ciphers", SOC_CIPHERS, true},
|
||||
{"macs", SOC_MACS, true},
|
||||
{"compression", SOC_COMPRESSION, true},
|
||||
{"connecttimeout", SOC_TIMEOUT, true},
|
||||
{"stricthostkeychecking", SOC_STRICTHOSTKEYCHECK, true},
|
||||
{"userknownhostsfile", SOC_KNOWNHOSTS, true},
|
||||
{"proxycommand", SOC_PROXYCOMMAND, true},
|
||||
{"gssapiserveridentity", SOC_GSSAPISERVERIDENTITY, false},
|
||||
{"gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY, false},
|
||||
{"gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS, true},
|
||||
{"include", SOC_INCLUDE, true},
|
||||
{"bindaddress", SOC_BINDADDRESS, true},
|
||||
{"globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE, true},
|
||||
{"loglevel", SOC_LOGLEVEL, true},
|
||||
{"hostkeyalgorithms", SOC_HOSTKEYALGORITHMS, true},
|
||||
{"kexalgorithms", SOC_KEXALGORITHMS, true},
|
||||
{"gssapiauthentication", SOC_GSSAPIAUTHENTICATION, true},
|
||||
{"kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION, true},
|
||||
{"passwordauthentication", SOC_PASSWORDAUTHENTICATION, true},
|
||||
{"pubkeyauthentication", SOC_PUBKEYAUTHENTICATION, true},
|
||||
{"addkeystoagent", SOC_UNSUPPORTED, true},
|
||||
{"addressfamily", SOC_ADDRESSFAMILY, true},
|
||||
{"batchmode", SOC_UNSUPPORTED, true},
|
||||
{"canonicaldomains", SOC_UNSUPPORTED, true},
|
||||
{"canonicalizefallbacklocal", SOC_UNSUPPORTED, true},
|
||||
{"canonicalizehostname", SOC_UNSUPPORTED, true},
|
||||
{"canonicalizemaxdots", SOC_UNSUPPORTED, true},
|
||||
{"canonicalizepermittedcnames", SOC_UNSUPPORTED, true},
|
||||
{"certificatefile", SOC_CERTIFICATE, true},
|
||||
{"kbdinteractiveauthentication", SOC_UNSUPPORTED, true},
|
||||
{"checkhostip", SOC_UNSUPPORTED, true},
|
||||
{"connectionattempts", SOC_UNSUPPORTED, true},
|
||||
{"enablesshkeysign", SOC_UNSUPPORTED, true},
|
||||
{"fingerprinthash", SOC_UNSUPPORTED, true},
|
||||
{"forwardagent", SOC_UNSUPPORTED, true},
|
||||
{"hashknownhosts", SOC_UNSUPPORTED, true},
|
||||
{"hostbasedauthentication", SOC_UNSUPPORTED, true},
|
||||
{"hostbasedacceptedalgorithms", SOC_UNSUPPORTED, true},
|
||||
{"hostkeyalias", SOC_UNSUPPORTED, true},
|
||||
{"identitiesonly", SOC_IDENTITIESONLY, true},
|
||||
{"identityagent", SOC_IDENTITYAGENT, true},
|
||||
{"ipqos", SOC_UNSUPPORTED, true},
|
||||
{"kbdinteractivedevices", SOC_UNSUPPORTED, true},
|
||||
{"nohostauthenticationforlocalhost", SOC_UNSUPPORTED, true},
|
||||
{"numberofpasswordprompts", SOC_UNSUPPORTED, true},
|
||||
{"pkcs11provider", SOC_UNSUPPORTED, true},
|
||||
{"preferredauthentications", SOC_UNSUPPORTED, true},
|
||||
{"proxyjump", SOC_PROXYJUMP, true},
|
||||
{"proxyusefdpass", SOC_UNSUPPORTED, true},
|
||||
{"pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES, true},
|
||||
{"rekeylimit", SOC_REKEYLIMIT, true},
|
||||
{"remotecommand", SOC_UNSUPPORTED, true},
|
||||
{"revokedhostkeys", SOC_UNSUPPORTED, true},
|
||||
{"serveralivecountmax", SOC_UNSUPPORTED, true},
|
||||
{"serveraliveinterval", SOC_UNSUPPORTED, true},
|
||||
{"streamlocalbindmask", SOC_UNSUPPORTED, true},
|
||||
{"streamlocalbindunlink", SOC_UNSUPPORTED, true},
|
||||
{"syslogfacility", SOC_UNSUPPORTED, true},
|
||||
{"tcpkeepalive", SOC_UNSUPPORTED, true},
|
||||
{"updatehostkeys", SOC_UNSUPPORTED, true},
|
||||
{"verifyhostkeydns", SOC_UNSUPPORTED, true},
|
||||
{"visualhostkey", SOC_UNSUPPORTED, true},
|
||||
{"clearallforwardings", SOC_NA, true},
|
||||
{"controlmaster", SOC_NA, true},
|
||||
{"controlpersist", SOC_NA, true},
|
||||
{"controlpath", SOC_NA, true},
|
||||
{"dynamicforward", SOC_NA, true},
|
||||
{"escapechar", SOC_NA, true},
|
||||
{"exitonforwardfailure", SOC_NA, true},
|
||||
{"forwardx11", SOC_NA, true},
|
||||
{"forwardx11timeout", SOC_NA, true},
|
||||
{"forwardx11trusted", SOC_NA, true},
|
||||
{"gatewayports", SOC_NA, true},
|
||||
{"ignoreunknown", SOC_NA, true},
|
||||
{"localcommand", SOC_NA, true},
|
||||
{"localforward", SOC_NA, true},
|
||||
{"permitlocalcommand", SOC_NA, true},
|
||||
{"remoteforward", SOC_NA, true},
|
||||
{"requesttty", SOC_NA, true},
|
||||
{"sendenv", SOC_NA, true},
|
||||
{"tunnel", SOC_NA, true},
|
||||
{"tunneldevice", SOC_NA, true},
|
||||
{"xauthlocation", SOC_NA, true},
|
||||
{"pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES, true},
|
||||
{"requiredrsasize", SOC_REQUIRED_RSA_SIZE, true},
|
||||
{"gssapikeyexchange", SOC_GSSAPIKEYEXCHANGE, true},
|
||||
{"gssapikexalgorithms", SOC_GSSAPIKEXALGORITHMS, true},
|
||||
{NULL, SOC_UNKNOWN, false},
|
||||
};
|
||||
|
||||
enum ssh_config_match_e {
|
||||
@@ -188,19 +192,48 @@ static struct ssh_config_match_keyword_table_s
|
||||
{NULL, MATCH_UNKNOWN},
|
||||
};
|
||||
|
||||
static int ssh_config_parse_line(ssh_session session, const char *line,
|
||||
unsigned int count, int *parsing, unsigned int depth, bool global);
|
||||
int ssh_config_parse_line(ssh_session session,
|
||||
const char *line,
|
||||
unsigned int count,
|
||||
int *parsing,
|
||||
unsigned int depth,
|
||||
bool global);
|
||||
|
||||
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
|
||||
int i;
|
||||
static int ssh_config_parse_line_internal(ssh_session session,
|
||||
const char *line,
|
||||
unsigned int count,
|
||||
int *parsing,
|
||||
unsigned int depth,
|
||||
bool global,
|
||||
bool is_cli,
|
||||
bool fail_on_unknown);
|
||||
|
||||
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
|
||||
if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
|
||||
return ssh_config_keyword_table[i].opcode;
|
||||
int ssh_config_parse_line_cli(ssh_session session, const char *line);
|
||||
|
||||
enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
|
||||
if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
|
||||
return ssh_config_keyword_table[i].opcode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SOC_UNKNOWN;
|
||||
return SOC_UNKNOWN;
|
||||
}
|
||||
|
||||
static bool ssh_config_is_cli_supported(enum ssh_config_opcode_e opcode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
|
||||
if (opcode == ssh_config_keyword_table[i].opcode) {
|
||||
return ssh_config_keyword_table[i].cli_supported;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define LIBSSH_CONF_MAX_DEPTH 16
|
||||
@@ -734,13 +767,76 @@ ssh_match_localnetwork(const char *addrlist, bool negate)
|
||||
}
|
||||
#endif /* HAVE_IFADDRS_H */
|
||||
|
||||
static int
|
||||
ssh_config_parse_line(ssh_session session,
|
||||
const char *line,
|
||||
unsigned int count,
|
||||
int *parsing,
|
||||
unsigned int depth,
|
||||
bool global)
|
||||
static enum ssh_options_e
|
||||
ssh_config_get_auth_option(enum ssh_config_opcode_e opcode)
|
||||
{
|
||||
struct auth_option_map {
|
||||
enum ssh_config_opcode_e opcode;
|
||||
const char *name;
|
||||
enum ssh_options_e option;
|
||||
};
|
||||
|
||||
static struct auth_option_map auth_options[] = {
|
||||
{
|
||||
SOC_GSSAPIAUTHENTICATION,
|
||||
"GSSAPIAuthentication",
|
||||
SSH_OPTIONS_GSSAPI_AUTH,
|
||||
},
|
||||
{
|
||||
SOC_KBDINTERACTIVEAUTHENTICATION,
|
||||
"KbdInteractiveAuthentication",
|
||||
SSH_OPTIONS_KBDINT_AUTH,
|
||||
},
|
||||
{
|
||||
SOC_PASSWORDAUTHENTICATION,
|
||||
"PasswordAuthentication",
|
||||
SSH_OPTIONS_PASSWORD_AUTH,
|
||||
},
|
||||
{
|
||||
SOC_PUBKEYAUTHENTICATION,
|
||||
"PubkeyAuthentication",
|
||||
SSH_OPTIONS_PUBKEY_AUTH,
|
||||
},
|
||||
{0, NULL, 0},
|
||||
};
|
||||
|
||||
for (struct auth_option_map *map = auth_options; map->name != NULL; map++) {
|
||||
if (map->opcode == opcode) {
|
||||
return map->option;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define CHECK_COND_OR_FAIL(cond, error_message) \
|
||||
if ((cond)) { \
|
||||
SSH_LOG(SSH_LOG_DEBUG, \
|
||||
"line %d: %s: %s", \
|
||||
count, \
|
||||
error_message, \
|
||||
keyword); \
|
||||
if (fail_on_unknown) { \
|
||||
ssh_set_error(session, \
|
||||
SSH_FATAL, \
|
||||
is_cli ? "%s '%s' value on CLI" \
|
||||
: "%s '%s' value at line %d", \
|
||||
error_message, \
|
||||
keyword, \
|
||||
is_cli ? 0 : count); \
|
||||
SAFE_FREE(x); \
|
||||
return SSH_ERROR; \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
|
||||
static int ssh_config_parse_line_internal(ssh_session session,
|
||||
const char *line,
|
||||
unsigned int count,
|
||||
int *parsing,
|
||||
unsigned int depth,
|
||||
bool global,
|
||||
bool is_cli,
|
||||
bool fail_on_unknown)
|
||||
{
|
||||
enum ssh_config_opcode_e opcode;
|
||||
const char *p = NULL, *p2 = NULL;
|
||||
@@ -755,6 +851,9 @@ ssh_config_parse_line(ssh_session session,
|
||||
|
||||
/* Ignore empty lines */
|
||||
if (line == NULL || *line == '\0') {
|
||||
if (is_cli) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -780,6 +879,16 @@ ssh_config_parse_line(ssh_session session,
|
||||
}
|
||||
|
||||
opcode = ssh_config_get_opcode(keyword);
|
||||
if (is_cli && !ssh_config_is_cli_supported(opcode)) {
|
||||
ssh_set_error(
|
||||
session,
|
||||
SSH_FATAL,
|
||||
"Option '%s' is not supported in command-line configuration",
|
||||
keyword);
|
||||
SAFE_FREE(x);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (*parsing == 1 &&
|
||||
opcode != SOC_HOST &&
|
||||
opcode != SOC_MATCH &&
|
||||
@@ -1050,82 +1159,91 @@ ssh_config_parse_line(ssh_session session,
|
||||
}
|
||||
case SOC_HOSTNAME:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
char *z = ssh_path_expand_escape(session, p);
|
||||
if (z == NULL) {
|
||||
z = strdup(p);
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, z);
|
||||
free(z);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
char *z = ssh_path_expand_escape(session, p);
|
||||
if (z == NULL) {
|
||||
z = strdup(p);
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, z);
|
||||
free(z);
|
||||
}
|
||||
break;
|
||||
case SOC_PORT:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
|
||||
}
|
||||
break;
|
||||
case SOC_USERNAME:
|
||||
if (session->opts.username == NULL) {
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_USER, p);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SOC_IDENTITY:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
|
||||
}
|
||||
break;
|
||||
case SOC_CIPHERS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
|
||||
ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
|
||||
ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
|
||||
}
|
||||
break;
|
||||
case SOC_MACS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
|
||||
ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
|
||||
ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
|
||||
}
|
||||
break;
|
||||
case SOC_COMPRESSION:
|
||||
i = ssh_config_get_yesno(&s, -1);
|
||||
if (i >= 0 && *parsing) {
|
||||
if (i) {
|
||||
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
|
||||
} else {
|
||||
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
|
||||
}
|
||||
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
|
||||
if (*parsing) {
|
||||
if (i) {
|
||||
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
|
||||
} else {
|
||||
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOC_TIMEOUT:
|
||||
l = ssh_config_get_long(&s, -1);
|
||||
if (l >= 0 && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l);
|
||||
CHECK_COND_OR_FAIL(l < 0, "Invalid argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l);
|
||||
}
|
||||
break;
|
||||
case SOC_STRICTHOSTKEYCHECK:
|
||||
i = ssh_config_get_yesno(&s, -1);
|
||||
if (i >= 0 && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
|
||||
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
|
||||
}
|
||||
break;
|
||||
case SOC_KNOWNHOSTS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
|
||||
}
|
||||
break;
|
||||
case SOC_PROXYCOMMAND:
|
||||
p = ssh_config_get_cmd(&s);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
/* We share the seen value with the ProxyJump */
|
||||
if (p && *parsing && !seen[SOC_PROXYJUMP]) {
|
||||
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
|
||||
if (*parsing && !seen[SOC_PROXYJUMP]) {
|
||||
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
|
||||
}
|
||||
break;
|
||||
case SOC_PROXYJUMP:
|
||||
@@ -1145,37 +1263,43 @@ ssh_config_parse_line(ssh_session session,
|
||||
break;
|
||||
case SOC_GSSAPISERVERIDENTITY:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
|
||||
}
|
||||
break;
|
||||
case SOC_GSSAPICLIENTIDENTITY:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
|
||||
}
|
||||
break;
|
||||
case SOC_GSSAPIDELEGATECREDENTIALS:
|
||||
i = ssh_config_get_yesno(&s, -1);
|
||||
if (i >=0 && *parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
|
||||
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
|
||||
}
|
||||
break;
|
||||
case SOC_BINDADDRESS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_BINDADDR, p);
|
||||
}
|
||||
break;
|
||||
case SOC_GLOBALKNOWNHOSTSFILE:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, p);
|
||||
}
|
||||
break;
|
||||
case SOC_LOGLEVEL:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
int value = -1;
|
||||
|
||||
if (strcasecmp(p, "quiet") == 0) {
|
||||
@@ -1193,6 +1317,7 @@ ssh_config_parse_line(ssh_session session,
|
||||
strcasecmp(p, "DEBUG3") == 0) {
|
||||
value = SSH_LOG_TRACE;
|
||||
}
|
||||
CHECK_COND_OR_FAIL(value == -1, "Invalid value");
|
||||
if (value != -1) {
|
||||
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &value);
|
||||
}
|
||||
@@ -1200,19 +1325,22 @@ ssh_config_parse_line(ssh_session session,
|
||||
break;
|
||||
case SOC_HOSTKEYALGORITHMS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
|
||||
}
|
||||
break;
|
||||
case SOC_PUBKEYACCEPTEDKEYTYPES:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
|
||||
}
|
||||
break;
|
||||
case SOC_KEXALGORITHMS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, p);
|
||||
}
|
||||
break;
|
||||
@@ -1220,6 +1348,7 @@ ssh_config_parse_line(ssh_session session,
|
||||
/* Parse the data limit */
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p == NULL) {
|
||||
CHECK_COND_OR_FAIL(1, "Missing data limit");
|
||||
break;
|
||||
} else if (strcmp(p, "default") == 0) {
|
||||
/* Default rekey limits enforced automatically */
|
||||
@@ -1228,8 +1357,7 @@ ssh_config_parse_line(ssh_session session,
|
||||
char *endp = NULL;
|
||||
ll = strtoll(p, &endp, 10);
|
||||
if (p == endp || ll < 0) {
|
||||
/* No number or negative */
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
|
||||
CHECK_COND_OR_FAIL(1, "Invalid data limit");
|
||||
break;
|
||||
}
|
||||
switch (*endp) {
|
||||
@@ -1267,19 +1395,19 @@ ssh_config_parse_line(ssh_session session,
|
||||
break;
|
||||
}
|
||||
if (*endp != ' ' && *endp != '\0') {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Invalid trailing characters after the rekey limit: %s",
|
||||
endp);
|
||||
CHECK_COND_OR_FAIL(1, "Invalid trailing characters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ll > -1 && *parsing) {
|
||||
CHECK_COND_OR_FAIL(ll < 0, "Invalid data limit");
|
||||
if (*parsing) {
|
||||
uint64_t v = (uint64_t)ll;
|
||||
ssh_options_set(session, SSH_OPTIONS_REKEY_DATA, &v);
|
||||
}
|
||||
/* Parse the time limit */
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p == NULL) {
|
||||
CHECK_COND_OR_FAIL(1, "Missing time limit");
|
||||
break;
|
||||
} else if (strcmp(p, "none") == 0) {
|
||||
ll = 0;
|
||||
@@ -1288,7 +1416,7 @@ ssh_config_parse_line(ssh_session session,
|
||||
ll = strtoll(p, &endp, 10);
|
||||
if (p == endp || ll < 0) {
|
||||
/* No number or negative */
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
|
||||
CHECK_COND_OR_FAIL(1, "Invalid time limit");
|
||||
break;
|
||||
}
|
||||
switch (*endp) {
|
||||
@@ -1341,11 +1469,11 @@ ssh_config_parse_line(ssh_session session,
|
||||
break;
|
||||
}
|
||||
if (*endp != '\0') {
|
||||
SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the"
|
||||
" rekey limit: %s", endp);
|
||||
CHECK_COND_OR_FAIL(1, "Invalid trailing characters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_COND_OR_FAIL(ll < 0, "Invalid time limit");
|
||||
if (ll > -1 && *parsing) {
|
||||
uint32_t v = (uint32_t)ll;
|
||||
ssh_options_set(session, SSH_OPTIONS_REKEY_TIME, &v);
|
||||
@@ -1354,56 +1482,44 @@ ssh_config_parse_line(ssh_session session,
|
||||
case SOC_GSSAPIAUTHENTICATION:
|
||||
case SOC_KBDINTERACTIVEAUTHENTICATION:
|
||||
case SOC_PASSWORDAUTHENTICATION:
|
||||
case SOC_PUBKEYAUTHENTICATION:
|
||||
case SOC_PUBKEYAUTHENTICATION: {
|
||||
enum ssh_options_e option = ssh_config_get_auth_option(opcode);
|
||||
i = ssh_config_get_yesno(&s, 0);
|
||||
if (i>=0 && *parsing) {
|
||||
switch(opcode){
|
||||
case SOC_GSSAPIAUTHENTICATION:
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_AUTH, &i);
|
||||
break;
|
||||
case SOC_KBDINTERACTIVEAUTHENTICATION:
|
||||
ssh_options_set(session, SSH_OPTIONS_KBDINT_AUTH, &i);
|
||||
break;
|
||||
case SOC_PASSWORDAUTHENTICATION:
|
||||
ssh_options_set(session, SSH_OPTIONS_PASSWORD_AUTH, &i);
|
||||
break;
|
||||
case SOC_PUBKEYAUTHENTICATION:
|
||||
ssh_options_set(session, SSH_OPTIONS_PUBKEY_AUTH, &i);
|
||||
break;
|
||||
/* make gcc happy */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK_COND_OR_FAIL(i < 0, "Authentication option");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, option, &i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOC_NA:
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d",
|
||||
keyword, count);
|
||||
break;
|
||||
CHECK_COND_OR_FAIL(1, "Unapplicable option");
|
||||
break;
|
||||
case SOC_UNSUPPORTED:
|
||||
SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d",
|
||||
keyword, count);
|
||||
break;
|
||||
CHECK_COND_OR_FAIL(1, "Unsupported option");
|
||||
break;
|
||||
case SOC_UNKNOWN:
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
|
||||
keyword, count);
|
||||
break;
|
||||
CHECK_COND_OR_FAIL(1, "Unknown option");
|
||||
break;
|
||||
case SOC_IDENTITYAGENT:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p);
|
||||
}
|
||||
break;
|
||||
case SOC_IDENTITIESONLY:
|
||||
i = ssh_config_get_yesno(&s, -1);
|
||||
if (i >= 0 && *parsing) {
|
||||
bool b = i;
|
||||
ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
|
||||
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
|
||||
if (*parsing) {
|
||||
bool b = i;
|
||||
ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
|
||||
}
|
||||
break;
|
||||
case SOC_CONTROLMASTER:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "ControlMaster");
|
||||
if (*parsing) {
|
||||
int value = -1;
|
||||
|
||||
if (strcasecmp(p, "auto") == 0) {
|
||||
@@ -1418,6 +1534,7 @@ ssh_config_parse_line(ssh_session session,
|
||||
value = SSH_CONTROL_MASTER_ASK;
|
||||
}
|
||||
|
||||
CHECK_COND_OR_FAIL(value == -1, "Invalid argument");
|
||||
if (value != -1) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
|
||||
}
|
||||
@@ -1435,10 +1552,64 @@ ssh_config_parse_line(ssh_session session,
|
||||
break;
|
||||
case SOC_CERTIFICATE:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
|
||||
}
|
||||
break;
|
||||
case SOC_GSSAPIKEYEXCHANGE: {
|
||||
i = ssh_config_get_yesno(&s, -1);
|
||||
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
|
||||
if (*parsing) {
|
||||
bool b = (i == 1) ? true : false;
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SOC_GSSAPIKEXALGORITHMS:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, p);
|
||||
}
|
||||
break;
|
||||
case SOC_REQUIRED_RSA_SIZE:
|
||||
l = ssh_config_get_long(&s, -1);
|
||||
CHECK_COND_OR_FAIL(l < 0 || l > INT_MAX, "Invalid argument");
|
||||
if (*parsing) {
|
||||
i = (int)l;
|
||||
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &i);
|
||||
}
|
||||
break;
|
||||
case SOC_ADDRESSFAMILY:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"line %d: no argument after keyword \"addressfamily\"",
|
||||
count);
|
||||
SAFE_FREE(x);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (*parsing) {
|
||||
int value = -1;
|
||||
|
||||
if (strcasecmp(p, "any") == 0) {
|
||||
value = SSH_ADDRESS_FAMILY_ANY;
|
||||
} else if (strcasecmp(p, "inet") == 0) {
|
||||
value = SSH_ADDRESS_FAMILY_INET;
|
||||
} else if (strcasecmp(p, "inet6") == 0) {
|
||||
value = SSH_ADDRESS_FAMILY_INET6;
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"line %d: invalid argument \"%s\"",
|
||||
count,
|
||||
p);
|
||||
SAFE_FREE(x);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_ADDRESS_FAMILY, &value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
|
||||
opcode);
|
||||
@@ -1451,6 +1622,38 @@ ssh_config_parse_line(ssh_session session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef CHECK_COND_OR_FAIL
|
||||
|
||||
int ssh_config_parse_line(ssh_session session,
|
||||
const char *line,
|
||||
unsigned int count,
|
||||
int *parsing,
|
||||
unsigned int depth,
|
||||
bool global)
|
||||
{
|
||||
return ssh_config_parse_line_internal(session,
|
||||
line,
|
||||
count,
|
||||
parsing,
|
||||
depth,
|
||||
global,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
int ssh_config_parse_line_cli(ssh_session session, const char *line)
|
||||
{
|
||||
int parsing = 1;
|
||||
return ssh_config_parse_line_internal(session,
|
||||
line,
|
||||
0,
|
||||
&parsing,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
}
|
||||
|
||||
/* @brief Parse configuration from a file pointer
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
|
||||
@@ -109,7 +109,8 @@ static int ssh_connect_socket_close(socket_t s)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int getai(const char *host, int port, struct addrinfo **ai)
|
||||
static int
|
||||
getai(const char *host, int port, int ai_family, struct addrinfo **ai)
|
||||
{
|
||||
const char *service = NULL;
|
||||
struct addrinfo hints;
|
||||
@@ -118,7 +119,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
|
||||
ZERO_STRUCT(hints);
|
||||
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_family = ai_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (port == 0) {
|
||||
@@ -165,14 +166,39 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
{
|
||||
socket_t s = -1, first = -1;
|
||||
int rc;
|
||||
int ai_family;
|
||||
static const char *ai_family_str = NULL;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo *itr = NULL;
|
||||
char addrname[NI_MAXHOST], portname[NI_MAXSERV];
|
||||
|
||||
rc = getai(host, port, &ai);
|
||||
switch (session->opts.address_family) {
|
||||
case SSH_ADDRESS_FAMILY_INET:
|
||||
ai_family = PF_INET;
|
||||
ai_family_str = "inet";
|
||||
break;
|
||||
case SSH_ADDRESS_FAMILY_INET6:
|
||||
ai_family = PF_INET6;
|
||||
ai_family_str = "inet6";
|
||||
break;
|
||||
case SSH_ADDRESS_FAMILY_ANY:
|
||||
default:
|
||||
ai_family = PF_UNSPEC;
|
||||
ai_family_str = "any";
|
||||
}
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Resolve target hostname %s port %d (%s)",
|
||||
host,
|
||||
port,
|
||||
ai_family_str);
|
||||
rc = getai(host, port, ai_family, &ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s)",
|
||||
host, gai_strerror(rc));
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s): %s",
|
||||
host,
|
||||
ai_family_str,
|
||||
gai_strerror(rc));
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -192,13 +218,18 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
struct addrinfo *bind_ai = NULL;
|
||||
struct addrinfo *bind_itr = NULL;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Resolving bind address %s (%s)",
|
||||
bind_addr,
|
||||
ai_family_str);
|
||||
|
||||
rc = getai(bind_addr, 0, &bind_ai);
|
||||
rc = getai(bind_addr, 0, ai_family, &bind_ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s)",
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s): %s",
|
||||
bind_addr,
|
||||
ai_family_str,
|
||||
gai_strerror(rc));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
@@ -252,7 +283,28 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
}
|
||||
}
|
||||
|
||||
rc = getnameinfo(itr->ai_addr,
|
||||
itr->ai_addrlen,
|
||||
addrname,
|
||||
sizeof(addrname),
|
||||
portname,
|
||||
sizeof(portname),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"getnameinfo failed: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Connecting to host %s [%s] port %s",
|
||||
host,
|
||||
addrname,
|
||||
portname);
|
||||
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
|
||||
if (rc == -1) {
|
||||
if ((errno != 0) && (errno != EINPROGRESS)) {
|
||||
@@ -263,6 +315,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
s = -1;
|
||||
} else {
|
||||
if (first == -1) {
|
||||
SSH_LOG(SSH_LOG_PACKET, "EINPROGRESS => Store for later.");
|
||||
first = s;
|
||||
} else { /* errno == EINPROGRESS */
|
||||
/* save only the first "working" socket */
|
||||
@@ -282,6 +335,9 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
* connection, otherwise return the first address without error or error */
|
||||
if (s == -1) {
|
||||
s = first;
|
||||
} else if (s != first && first != -1) {
|
||||
/* Clean up the saved socket if any */
|
||||
ssh_connect_socket_close(first);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
138
src/connector.c
@@ -83,6 +83,20 @@ static ssize_t ssh_connector_fd_write(ssh_connector connector,
|
||||
uint32_t len);
|
||||
static bool ssh_connector_fd_is_socket(socket_t socket);
|
||||
|
||||
/**
|
||||
* @brief Create a new SSH connector.
|
||||
*
|
||||
* Allocates and initializes a new connector object for moving data between
|
||||
* an SSH session and file descriptors. The connector is created with invalid
|
||||
* file descriptors and callback structures initialized, but not yet attached
|
||||
* to any channels or sockets.
|
||||
*
|
||||
* @param[in] session The SSH session to associate with the connector.
|
||||
*
|
||||
* @return A newly allocated connector on success, or NULL if an
|
||||
* error occurred. On error, an out-of-memory error is
|
||||
* set on the session.
|
||||
*/
|
||||
ssh_connector ssh_connector_new(ssh_session session)
|
||||
{
|
||||
ssh_connector connector;
|
||||
@@ -112,8 +126,20 @@ ssh_connector ssh_connector_new(ssh_session session)
|
||||
return connector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free an SSH connector.
|
||||
*
|
||||
* Cleans up and deallocates a connector created by ssh_connector_new().
|
||||
* Any channel callbacks and poll objects associated with the @p connector
|
||||
* are removed and freed before the connector structure itself is released.
|
||||
*
|
||||
* @param[in] connector The connector to free.
|
||||
*/
|
||||
void ssh_connector_free (ssh_connector connector)
|
||||
{
|
||||
if (connector == NULL) {
|
||||
return;
|
||||
}
|
||||
if (connector->in_channel != NULL) {
|
||||
ssh_remove_channel_callbacks(connector->in_channel,
|
||||
&connector->in_channel_cb);
|
||||
@@ -140,6 +166,24 @@ void ssh_connector_free (ssh_connector connector)
|
||||
free(connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the input channel for a connector.
|
||||
*
|
||||
* Associates an SSH channel with the @p connector as its input source and
|
||||
* installs the internal channel callbacks used for reading data. Any
|
||||
* configured input file descriptor is disabled and the connector will
|
||||
* receive data from the given channel only.
|
||||
*
|
||||
* If neither `SSH_CONNECTOR_STDOUT` nor `SSH_CONNECTOR_STDERR` is specified
|
||||
* in @p flags, `SSH_CONNECTOR_STDOUT` is used as the default.
|
||||
*
|
||||
* @param[in] connector The connector to configure.
|
||||
* @param[in] channel The SSH channel to use as input.
|
||||
* @param[in] flags A combination of ssh_connector_flags_e values
|
||||
* selecting which channel streams to read from.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
|
||||
*/
|
||||
int ssh_connector_set_in_channel(ssh_connector connector,
|
||||
ssh_channel channel,
|
||||
enum ssh_connector_flags_e flags)
|
||||
@@ -156,6 +200,24 @@ int ssh_connector_set_in_channel(ssh_connector connector,
|
||||
return ssh_add_channel_callbacks(channel, &connector->in_channel_cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the output channel for a connector.
|
||||
*
|
||||
* Associates an SSH channel with the @p connector as its output target and
|
||||
* installs the internal channel callbacks used for writing data. Any
|
||||
* configured output file descriptor is disabled and the connector will
|
||||
* send data to the given channel only.
|
||||
*
|
||||
* If neither `SSH_CONNECTOR_STDOUT` nor `SSH_CONNECTOR_STDERR` is specified
|
||||
* in @p flags, `SSH_CONNECTOR_STDOUT` is used as the default.
|
||||
*
|
||||
* @param[in] connector The connector to configure.
|
||||
* @param[in] channel The SSH channel to use as output.
|
||||
* @param[in] flags A combination of ssh_connector_flags_e values
|
||||
* selecting which channel streams to write to.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
|
||||
*/
|
||||
int ssh_connector_set_out_channel(ssh_connector connector,
|
||||
ssh_channel channel,
|
||||
enum ssh_connector_flags_e flags)
|
||||
@@ -166,12 +228,21 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the connector's input file descriptor.
|
||||
*
|
||||
* Sets the @p fd (file descriptor) to be used as the input source for the
|
||||
* @p connector , replacing any previously configured input channel.
|
||||
*
|
||||
* @param[in] connector The connector to configure.
|
||||
* @param[in] fd The file descriptor (socket or regular).
|
||||
*/
|
||||
void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
|
||||
{
|
||||
connector->in_fd = fd;
|
||||
@@ -179,6 +250,15 @@ void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
|
||||
connector->in_channel = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the connector's output file descriptor.
|
||||
*
|
||||
* Sets the @p fd (file descriptor) to be used as the output target for the
|
||||
* @p connector , replacing any previously configured output channel.
|
||||
*
|
||||
* @param[in] connector The connector to configure.
|
||||
* @param[in] fd The file descriptor (socket or regular).
|
||||
*/
|
||||
void ssh_connector_set_out_fd(ssh_connector connector, socket_t fd)
|
||||
{
|
||||
connector->out_fd = fd;
|
||||
@@ -250,7 +330,9 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
|
||||
}
|
||||
|
||||
r = ssh_connector_fd_read(connector, buffer, toread);
|
||||
if (r < 0) {
|
||||
/* Sanity: Make sure we do not get too large return value to make static
|
||||
* analysis tools happy */
|
||||
if (r < 0 || r > (ssize_t)toread) {
|
||||
ssh_connector_except(connector, connector->in_fd);
|
||||
return;
|
||||
}
|
||||
@@ -295,7 +377,9 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
|
||||
w = ssh_connector_fd_write(connector,
|
||||
buffer + total,
|
||||
(uint32_t)(r - total));
|
||||
if (w < 0) {
|
||||
/* Sanity: Make sure we do not get too large return value
|
||||
* to make static analysis tools happy */
|
||||
if (w < 0 || w > (r - total)) {
|
||||
ssh_connector_except(connector, connector->out_fd);
|
||||
return;
|
||||
}
|
||||
@@ -372,7 +456,7 @@ ssh_connector_fd_out_cb(ssh_connector connector)
|
||||
*
|
||||
* @brief Callback called when a poll event is received on a file descriptor.
|
||||
*
|
||||
* This is for (input or output.
|
||||
* This is for input or output.
|
||||
*
|
||||
* @param[in] fd file descriptor receiving the event
|
||||
*
|
||||
@@ -382,15 +466,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) {
|
||||
@@ -409,6 +491,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
|
||||
@@ -420,7 +506,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,
|
||||
@@ -430,11 +516,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 */
|
||||
@@ -448,6 +534,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;
|
||||
|
||||
@@ -496,6 +583,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;
|
||||
@@ -513,10 +601,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];
|
||||
@@ -524,7 +613,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);
|
||||
@@ -535,7 +629,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);
|
||||
@@ -606,15 +700,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);
|
||||
|
||||
@@ -101,7 +101,7 @@ void ssh_client_curve25519_remove_callbacks(ssh_session session)
|
||||
ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
|
||||
}
|
||||
|
||||
static int ssh_curve25519_build_k(ssh_session session)
|
||||
int ssh_curve25519_build_k(ssh_session session)
|
||||
{
|
||||
ssh_curve25519_pubkey k;
|
||||
int rc;
|
||||
|
||||
@@ -79,6 +79,12 @@ int ssh_curve25519_init(ssh_session session)
|
||||
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;
|
||||
|
||||
|
||||
@@ -89,6 +89,12 @@ int ssh_curve25519_init(ssh_session session)
|
||||
|
||||
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;
|
||||
|
||||
@@ -409,6 +409,7 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
|
||||
size_t line = 0;
|
||||
size_t best_nlines = 0;
|
||||
|
||||
*best_size = 0;
|
||||
for(;;) {
|
||||
line++;
|
||||
firstbyte = getc(moduli);
|
||||
|
||||
5
src/dh.c
@@ -26,6 +26,10 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef WITH_GSSAPI
|
||||
#include "libssh/gssapi.h"
|
||||
#include <gssapi/gssapi.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/crypto.h"
|
||||
@@ -36,6 +40,7 @@
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/string.h"
|
||||
|
||||
static unsigned char p_group1_value[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
|
||||
|
||||
@@ -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;
|
||||
@@ -419,9 +424,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
|
||||
@@ -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;
|
||||
@@ -248,9 +253,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
|
||||
@@ -51,18 +51,25 @@ static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
|
||||
#else
|
||||
static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
|
||||
switch (kex_type) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
return NISTP256;
|
||||
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
|
||||
return NISTP384;
|
||||
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
|
||||
return NISTP521;
|
||||
}
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
return SSH_ERROR;
|
||||
#else
|
||||
return NULL;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
#if HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
return NISTP384;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
return NISTP521;
|
||||
default:
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
return SSH_ERROR;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal
|
||||
@@ -191,16 +198,51 @@ 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;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set up a nistp{256,384,521} key pair for ECDH key exchange.
|
||||
*/
|
||||
int ssh_ecdh_init(ssh_session session)
|
||||
{
|
||||
ssh_string pubkey = NULL;
|
||||
ssh_string *pubkey_loc = NULL;
|
||||
|
||||
pubkey = ssh_ecdh_generate(session);
|
||||
if (pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
pubkey_loc = &session->next_crypto->ecdh_server_pubkey;
|
||||
} else {
|
||||
pubkey_loc = &session->next_crypto->ecdh_client_pubkey;
|
||||
}
|
||||
|
||||
ssh_string_free(*pubkey_loc);
|
||||
*pubkey_loc = pubkey;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Starts ecdh-sha2-nistp256 key exchange
|
||||
*/
|
||||
int ssh_client_ecdh_init(ssh_session session)
|
||||
{
|
||||
ssh_string client_pubkey = NULL;
|
||||
int rc;
|
||||
|
||||
rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
|
||||
@@ -208,18 +250,16 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
client_pubkey = ssh_ecdh_generate(session);
|
||||
if (client_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc < 0) {
|
||||
ssh_string_free(client_pubkey);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
session->next_crypto->ecdh_client_pubkey = client_pubkey;
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer,
|
||||
session->next_crypto->ecdh_client_pubkey);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* register the packet callbacks */
|
||||
ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
|
||||
@@ -442,7 +482,6 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
|
||||
{
|
||||
/* ECDH keys */
|
||||
ssh_string q_c_string = NULL;
|
||||
ssh_string q_s_string = NULL;
|
||||
/* SSH host keys (rsa, ed25519 and ecdsa) */
|
||||
ssh_key privkey = NULL;
|
||||
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
|
||||
@@ -463,13 +502,11 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
|
||||
}
|
||||
session->next_crypto->ecdh_client_pubkey = q_c_string;
|
||||
|
||||
q_s_string = ssh_ecdh_generate(session);
|
||||
if (q_s_string == NULL) {
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
session->next_crypto->ecdh_server_pubkey = q_s_string;
|
||||
|
||||
/* build k and session_id */
|
||||
rc = ecdh_build_k(session);
|
||||
if (rc < 0) {
|
||||
@@ -506,7 +543,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
|
||||
"bSSS",
|
||||
SSH2_MSG_KEXDH_REPLY,
|
||||
pubkey_blob, /* host's pubkey */
|
||||
q_s_string, /* ecdh public key */
|
||||
session->next_crypto->ecdh_server_pubkey, /* ecdh public key */
|
||||
sig_blob); /* signature blob */
|
||||
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
|
||||
@@ -36,28 +36,46 @@
|
||||
/** @internal
|
||||
* @brief Map the given key exchange enum value to its curve name.
|
||||
*/
|
||||
static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
|
||||
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
|
||||
static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
|
||||
{
|
||||
switch (kex_type) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
return "NIST P-256";
|
||||
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
#if HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
return "NIST P-384";
|
||||
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
return "NIST P-521";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Starts ecdh-sha2-nistp{256,384,521} key exchange.
|
||||
* @brief Set up a nistp{256,384,521} key pair for ECDH key exchange.
|
||||
*/
|
||||
int ssh_client_ecdh_init(ssh_session session)
|
||||
int ssh_ecdh_init(ssh_session session)
|
||||
{
|
||||
int rc;
|
||||
int rc = SSH_OK;
|
||||
const char *curve = NULL;
|
||||
const char *genstring = NULL;
|
||||
gpg_error_t err;
|
||||
ssh_string client_pubkey = NULL;
|
||||
gcry_sexp_t param = NULL;
|
||||
gcry_sexp_t key = NULL;
|
||||
const char *curve = NULL;
|
||||
ssh_string pubkey = NULL;
|
||||
ssh_string *pubkey_loc = NULL;
|
||||
|
||||
if (session->server) {
|
||||
pubkey_loc = &session->next_crypto->ecdh_server_pubkey;
|
||||
genstring = "(genkey(ecdh(curve %s) (flags transient-key)))";
|
||||
} else {
|
||||
pubkey_loc = &session->next_crypto->ecdh_client_pubkey;
|
||||
genstring = "(genkey(ecdh(curve %s)))";
|
||||
}
|
||||
|
||||
curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
|
||||
if (curve == NULL) {
|
||||
@@ -65,16 +83,7 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
|
||||
if (rc < 0) {
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = gcry_sexp_build(¶m,
|
||||
NULL,
|
||||
"(genkey(ecdh(curve %s)))",
|
||||
curve);
|
||||
err = gcry_sexp_build(¶m, NULL, genstring, curve);
|
||||
if (err) {
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
@@ -86,36 +95,59 @@ int ssh_client_ecdh_init(ssh_session session)
|
||||
goto out;
|
||||
}
|
||||
|
||||
client_pubkey = ssh_sexp_extract_mpi(key,
|
||||
"q",
|
||||
GCRYMPI_FMT_USG,
|
||||
GCRYMPI_FMT_STD);
|
||||
if (client_pubkey == NULL) {
|
||||
pubkey = ssh_sexp_extract_mpi(key, "q", GCRYMPI_FMT_USG, GCRYMPI_FMT_STD);
|
||||
if (pubkey == NULL) {
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
|
||||
if (rc < 0) {
|
||||
rc = SSH_ERROR;
|
||||
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;
|
||||
session->next_crypto->ecdh_client_pubkey = client_pubkey;
|
||||
client_pubkey = NULL;
|
||||
|
||||
SSH_STRING_FREE(*pubkey_loc);
|
||||
*pubkey_loc = pubkey;
|
||||
pubkey = NULL;
|
||||
|
||||
out:
|
||||
gcry_sexp_release(param);
|
||||
gcry_sexp_release(key);
|
||||
SSH_STRING_FREE(pubkey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Starts ecdh-sha2-nistp{256,384,521} key exchange.
|
||||
*/
|
||||
int ssh_client_ecdh_init(ssh_session session)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer,
|
||||
session->next_crypto->ecdh_client_pubkey);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* register the packet callbacks */
|
||||
ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
|
||||
session->dh_handshake_state = DH_STATE_INIT_SENT;
|
||||
|
||||
rc = ssh_packet_send(session);
|
||||
|
||||
out:
|
||||
gcry_sexp_release(param);
|
||||
gcry_sexp_release(key);
|
||||
SSH_STRING_FREE(client_pubkey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -265,27 +297,16 @@ int ecdh_build_k(ssh_session session)
|
||||
* SSH_MSG_KEXDH_REPLY
|
||||
*/
|
||||
SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
|
||||
gpg_error_t err;
|
||||
/* ECDH keys */
|
||||
ssh_string q_c_string = NULL;
|
||||
ssh_string q_s_string = NULL;
|
||||
gcry_sexp_t param = NULL;
|
||||
gcry_sexp_t key = NULL;
|
||||
/* SSH host keys (rsa, ed25519 and ecdsa) */
|
||||
ssh_key privkey = NULL;
|
||||
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
|
||||
ssh_string sig_blob = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
int rc = SSH_ERROR;
|
||||
const char *curve = NULL;
|
||||
(void)type;
|
||||
(void)user;
|
||||
|
||||
ssh_packet_remove_callbacks(session, &ssh_ecdh_server_callbacks);
|
||||
curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
|
||||
if (curve == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Extract the client pubkey from the init packet */
|
||||
q_c_string = ssh_buffer_get_ssh_string(packet);
|
||||
@@ -295,29 +316,12 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
|
||||
}
|
||||
session->next_crypto->ecdh_client_pubkey = q_c_string;
|
||||
|
||||
/* Build server's key pair */
|
||||
err = gcry_sexp_build(¶m, NULL, "(genkey(ecdh(curve %s) (flags transient-key)))",
|
||||
curve);
|
||||
if (err) {
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to generate a key pair");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = gcry_pk_genkey(&key, param);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
q_s_string = ssh_sexp_extract_mpi(key,
|
||||
"q",
|
||||
GCRYMPI_FMT_USG,
|
||||
GCRYMPI_FMT_STD);
|
||||
if (q_s_string == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
session->next_crypto->ecdh_privkey = key;
|
||||
key = NULL;
|
||||
session->next_crypto->ecdh_server_pubkey = q_s_string;
|
||||
|
||||
/* build k and session_id */
|
||||
rc = ecdh_build_k(session);
|
||||
if (rc != SSH_OK) {
|
||||
@@ -355,7 +359,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
|
||||
"bSSS",
|
||||
SSH2_MSG_KEXDH_REPLY,
|
||||
pubkey_blob, /* host's pubkey */
|
||||
q_s_string, /* ecdh public key */
|
||||
session->next_crypto->ecdh_server_pubkey, /* ecdh public key */
|
||||
sig_blob); /* signature blob */
|
||||
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
@@ -380,8 +384,6 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
|
||||
}
|
||||
|
||||
out:
|
||||
gcry_sexp_release(param);
|
||||
gcry_sexp_release(key);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_buffer_reinit(session->out_buffer);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
|
||||