mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
Compare commits
93 Commits
3526e02dee
...
34db488e4d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
1
.clang-format-ignore
Normal file
1
.clang-format-ignore
Normal file
@@ -0,0 +1 @@
|
||||
src/external/*
|
||||
@@ -83,6 +83,12 @@ 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
|
||||
@@ -178,7 +184,7 @@ centos10s/openssl_3.5.x/x86_64/fips:
|
||||
centos9s/openssl_3.5.x/x86_64:
|
||||
extends: .centos9
|
||||
variables:
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
|
||||
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
|
||||
script:
|
||||
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
|
||||
make -j$(nproc) &&
|
||||
@@ -196,9 +202,11 @@ centos9s/openssl_3.5.x/x86_64/fips:
|
||||
variables:
|
||||
OPENSSL_ENABLE_SHA1_SIGNATURES: 1
|
||||
script:
|
||||
# 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
|
||||
@@ -214,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 #
|
||||
@@ -229,7 +239,7 @@ 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:
|
||||
@@ -504,6 +514,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$"
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -104,9 +104,10 @@ if (OPENSSL_FOUND)
|
||||
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
|
||||
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
|
||||
|
||||
# Check for ML-KEM768 availability (OpenSSL 3.5+)
|
||||
# Check for ML-KEM availability (OpenSSL 3.5+)
|
||||
if (OPENSSL_VERSION VERSION_GREATER_EQUAL "3.5.0")
|
||||
set(HAVE_MLKEM 1)
|
||||
set(HAVE_OPENSSL_MLKEM 1)
|
||||
set(HAVE_MLKEM1024 1)
|
||||
endif ()
|
||||
|
||||
unset(CMAKE_REQUIRED_INCLUDES)
|
||||
@@ -234,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)
|
||||
|
||||
@@ -194,8 +194,14 @@
|
||||
/* Define to 1 if we have support for blowfish */
|
||||
#cmakedefine HAVE_BLOWFISH 1
|
||||
|
||||
/* Define to 1 if we have support for ML-KEM */
|
||||
#cmakedefine HAVE_MLKEM 1
|
||||
/* 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 ***************************/
|
||||
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
#
|
||||
# Build the documentation
|
||||
#
|
||||
# To build the documentation with a local doxygen-awesome-css tarball:
|
||||
# To build the documentation with a local doxygen-awesome-css directory:
|
||||
#
|
||||
# cmake -S . -B obj \
|
||||
# -DDOXYGEN_AWESOME_CSS_TARBALL=/path/to/doxygen-awesome-css.tar.gz cmake
|
||||
# --build obj --target docs
|
||||
# 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
|
||||
# https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz
|
||||
#
|
||||
find_package(Doxygen)
|
||||
|
||||
if (DOXYGEN_FOUND)
|
||||
# Allow specifying a local tarball for doxygen-awesome-css (useful for
|
||||
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_TARBALL)
|
||||
set(DOXYGEN_AWESOME_CSS_TARBALL
|
||||
"https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz"
|
||||
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 ()
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
doxygen-awesome-css URL ${DOXYGEN_AWESOME_CSS_TARBALL}
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
|
||||
FetchContent_MakeAvailable(doxygen-awesome-css)
|
||||
|
||||
# Get the path to doxygen-awesome.css
|
||||
FetchContent_GetProperties(doxygen-awesome-css SOURCE_DIR AWESOME_CSS_DIR)
|
||||
|
||||
# Project title shown in documentation
|
||||
set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME})
|
||||
# Project version number shown in documentation
|
||||
@@ -49,8 +62,13 @@ if (DOXYGEN_FOUND)
|
||||
set(DOXYGEN_WARN_IF_UNDOCUMENTED YES)
|
||||
# Do not extract private class members
|
||||
set(DOXYGEN_EXTRACT_PRIVATE NO)
|
||||
# Do not include internal documentation
|
||||
set(DOXYGEN_INTERNAL_DOCS 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)
|
||||
@@ -219,6 +237,11 @@ if (DOXYGEN_FOUND)
|
||||
doxygen_add_docs(docs ${CMAKE_SOURCE_DIR}/include/libssh
|
||||
${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# 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 ()
|
||||
|
||||
add_custom_target(
|
||||
docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh
|
||||
${CMAKE_BINARY_DIR})
|
||||
|
||||
41
doc/fetch_doxygen_awesome.cmake
Normal file
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()
|
||||
@@ -20,11 +20,12 @@ the interesting functions as you go.
|
||||
The libssh library provides:
|
||||
|
||||
- <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>
|
||||
|
||||
@@ -355,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -68,6 +68,9 @@ enum ssh_config_opcode_e {
|
||||
SOC_CONTROLPATH,
|
||||
SOC_CERTIFICATE,
|
||||
SOC_REQUIRED_RSA_SIZE,
|
||||
SOC_ADDRESSFAMILY,
|
||||
SOC_GSSAPIKEYEXCHANGE,
|
||||
SOC_GSSAPIKEXALGORITHMS,
|
||||
|
||||
SOC_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
@@ -87,14 +87,22 @@ enum ssh_key_exchange_e {
|
||||
SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM,
|
||||
/* sntrup761x25519-sha512 */
|
||||
SSH_KEX_SNTRUP761X25519_SHA512,
|
||||
#ifdef HAVE_MLKEM
|
||||
/* mlkem768x25519-sha256 */
|
||||
SSH_KEX_MLKEM768X25519_SHA256,
|
||||
/* mlkem768nistp256-sha256 */
|
||||
SSH_KEX_MLKEM768NISTP256_SHA256,
|
||||
#ifdef HAVE_MLKEM1024
|
||||
/* mlkem1024nistp384-sha384 */
|
||||
SSH_KEX_MLKEM1024NISTP384_SHA384,
|
||||
#endif /* HAVE_MLKEM */
|
||||
#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 {
|
||||
@@ -151,11 +159,14 @@ struct ssh_crypto_struct {
|
||||
ssh_curve25519_pubkey curve25519_client_pubkey;
|
||||
ssh_curve25519_pubkey curve25519_server_pubkey;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
#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;
|
||||
#endif
|
||||
#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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
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);
|
||||
|
||||
|
||||
36
include/libssh/kex-gss.h
Normal file
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
|
||||
@@ -152,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 {
|
||||
@@ -371,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,
|
||||
@@ -422,6 +429,9 @@ enum ssh_options_e {
|
||||
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 {
|
||||
|
||||
@@ -36,11 +36,20 @@ extern "C" {
|
||||
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
|
||||
|
||||
|
||||
127
include/libssh/mlkem_native.h
Normal file
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_ */
|
||||
@@ -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 */
|
||||
@@ -201,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;
|
||||
@@ -265,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;
|
||||
@@ -277,6 +282,7 @@ struct ssh_session_struct {
|
||||
bool identities_only;
|
||||
int control_master;
|
||||
char *control_path;
|
||||
int address_family;
|
||||
} opts;
|
||||
|
||||
/* server options */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,6 +24,15 @@
|
||||
#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);
|
||||
|
||||
@@ -105,6 +105,7 @@ set(libssh_SRCS
|
||||
error.c
|
||||
getpass.c
|
||||
gzip.c
|
||||
hybrid_mlkem.c
|
||||
init.c
|
||||
kdf.c
|
||||
kex.c
|
||||
@@ -115,6 +116,7 @@ set(libssh_SRCS
|
||||
match.c
|
||||
messages.c
|
||||
misc.c
|
||||
mlkem.c
|
||||
options.c
|
||||
packet.c
|
||||
packet_cb.c
|
||||
@@ -195,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}
|
||||
@@ -248,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)
|
||||
@@ -286,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)
|
||||
|
||||
@@ -299,14 +315,18 @@ if (NOT WITH_NACL)
|
||||
endif()
|
||||
endif (NOT WITH_NACL)
|
||||
|
||||
if (HAVE_MLKEM)
|
||||
set(libssh_SRCS
|
||||
${libssh_SRCS}
|
||||
hybrid_mlkem.c
|
||||
mlkem_crypto.c
|
||||
mlkem.c
|
||||
)
|
||||
endif (HAVE_MLKEM)
|
||||
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
|
||||
|
||||
143
src/auth.c
143
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;
|
||||
@@ -2446,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;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
30
src/bind.c
30
src/bind.c
@@ -245,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,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;
|
||||
@@ -463,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);
|
||||
@@ -509,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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -602,8 +602,9 @@ ssh_bind_config_parse_line(ssh_bind bind,
|
||||
break;
|
||||
case BIND_CFG_REQUIRED_RSA_SIZE:
|
||||
l = ssh_config_get_long(&s, -1);
|
||||
if (l >= 0 && (*parser_flags & PARSING)) {
|
||||
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSA_MIN_SIZE, &l);
|
||||
if (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'",
|
||||
|
||||
248
src/channels.c
248
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)
|
||||
@@ -1059,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()
|
||||
@@ -1084,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()
|
||||
@@ -1110,7 +1110,6 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Open a TCP/IP forwarding channel.
|
||||
*
|
||||
@@ -1127,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)
|
||||
@@ -1199,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,
|
||||
@@ -1359,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
|
||||
@@ -1436,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()
|
||||
@@ -1524,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)
|
||||
{
|
||||
@@ -1719,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()
|
||||
*/
|
||||
@@ -1997,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,
|
||||
@@ -2055,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)
|
||||
{
|
||||
@@ -2077,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()
|
||||
@@ -2098,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
|
||||
@@ -2139,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)
|
||||
@@ -2160,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().
|
||||
@@ -2210,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.
|
||||
@@ -2266,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,
|
||||
@@ -2401,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) {
|
||||
@@ -2490,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,
|
||||
@@ -2584,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.
|
||||
*
|
||||
@@ -2599,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,
|
||||
@@ -2708,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,
|
||||
@@ -2759,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.
|
||||
*/
|
||||
@@ -2815,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:
|
||||
@@ -2880,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.
|
||||
@@ -2906,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)
|
||||
{
|
||||
@@ -2939,7 +2952,6 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a break signal to the server (as described in RFC 4335).
|
||||
*
|
||||
@@ -2951,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)
|
||||
{
|
||||
@@ -2989,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
|
||||
@@ -3098,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)
|
||||
{
|
||||
@@ -3127,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.
|
||||
@@ -3238,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()
|
||||
*/
|
||||
@@ -3296,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()
|
||||
*/
|
||||
@@ -3343,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()
|
||||
*/
|
||||
@@ -3455,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!
|
||||
@@ -3524,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.
|
||||
@@ -3641,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)
|
||||
@@ -3840,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)
|
||||
@@ -3905,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)
|
||||
@@ -3966,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)
|
||||
{
|
||||
@@ -4010,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)
|
||||
|
||||
29
src/client.c
29
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,9 +47,7 @@
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/kex.h"
|
||||
#ifdef HAVE_MLKEM
|
||||
#include "libssh/hybrid_mlkem.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef HAVE_PTHREAD
|
||||
@@ -267,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:
|
||||
@@ -299,13 +306,13 @@ int dh_handshake(ssh_session session)
|
||||
rc = ssh_client_sntrup761x25519_init(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
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;
|
||||
#endif
|
||||
default:
|
||||
rc = SSH_ERROR;
|
||||
}
|
||||
@@ -915,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 "
|
||||
|
||||
112
src/config.c
112
src/config.c
@@ -91,7 +91,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{"passwordauthentication", SOC_PASSWORDAUTHENTICATION, true},
|
||||
{"pubkeyauthentication", SOC_PUBKEYAUTHENTICATION, true},
|
||||
{"addkeystoagent", SOC_UNSUPPORTED, true},
|
||||
{"addressfamily", SOC_UNSUPPORTED, true},
|
||||
{"addressfamily", SOC_ADDRESSFAMILY, true},
|
||||
{"batchmode", SOC_UNSUPPORTED, true},
|
||||
{"canonicaldomains", SOC_UNSUPPORTED, true},
|
||||
{"canonicalizefallbacklocal", SOC_UNSUPPORTED, true},
|
||||
@@ -155,6 +155,8 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{"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},
|
||||
};
|
||||
|
||||
@@ -806,28 +808,26 @@ ssh_config_get_auth_option(enum ssh_config_opcode_e opcode)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define CHECK_COND_OR_FAIL(cond, error_message) \
|
||||
do { \
|
||||
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; \
|
||||
} \
|
||||
} while (0)
|
||||
#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,
|
||||
@@ -1177,14 +1177,12 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
}
|
||||
break;
|
||||
case SOC_USERNAME:
|
||||
if (session->opts.username == NULL) {
|
||||
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;
|
||||
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;
|
||||
case SOC_IDENTITY:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
|
||||
@@ -1559,11 +1557,57 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
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, "Invalid argument");
|
||||
CHECK_COND_OR_FAIL(l < 0 || l > INT_MAX, "Invalid argument");
|
||||
if (*parsing) {
|
||||
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &l);
|
||||
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:
|
||||
@@ -1578,7 +1622,7 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SSH_PARSE_OR_FAIL
|
||||
#undef CHECK_COND_OR_FAIL
|
||||
|
||||
int ssh_config_parse_line(ssh_session session,
|
||||
const char *line,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
@@ -172,6 +234,15 @@ int ssh_connector_set_out_channel(ssh_connector connector,
|
||||
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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
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,
|
||||
|
||||
@@ -424,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:
|
||||
|
||||
@@ -253,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:
|
||||
|
||||
@@ -53,12 +53,11 @@ static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
|
||||
#endif /* OPENSSL_VERSION_NUMBER */
|
||||
switch (kex_type) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#endif
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
return NISTP256;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
#ifdef HAVE_MLKEM
|
||||
#if HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
return NISTP384;
|
||||
|
||||
@@ -36,15 +36,23 @@
|
||||
/** @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
|
||||
|
||||
@@ -38,15 +38,21 @@
|
||||
|
||||
#ifdef HAVE_ECDH
|
||||
|
||||
static mbedtls_ecp_group_id ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
|
||||
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
|
||||
static mbedtls_ecp_group_id
|
||||
ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
|
||||
{
|
||||
switch (kex_type) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
return MBEDTLS_ECP_DP_SECP256R1;
|
||||
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
return MBEDTLS_ECP_DP_SECP384R1;
|
||||
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
return MBEDTLS_ECP_DP_SECP521R1;
|
||||
default:
|
||||
return MBEDTLS_ECP_DP_NONE;
|
||||
}
|
||||
|
||||
return MBEDTLS_ECP_DP_NONE;
|
||||
}
|
||||
|
||||
|
||||
8896
src/external/libcrux_mlkem768_sha3.c
vendored
Normal file
8896
src/external/libcrux_mlkem768_sha3.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
src/external/sntrup761.c
vendored
6
src/external/sntrup761.c
vendored
@@ -27,9 +27,7 @@ sntrup761_enc (uint8_t *c, uint8_t *k, const uint8_t *pk,
|
||||
void
|
||||
sntrup761_dec (uint8_t *k, const uint8_t *c, const uint8_t *sk);
|
||||
|
||||
extern void crypto_hash_sha512 (unsigned char *out,
|
||||
const unsigned char *in,
|
||||
unsigned long long inlen);
|
||||
extern int sha512(const unsigned char *digest, size_t len, unsigned char *hash);
|
||||
|
||||
#define MAX_LEN 761
|
||||
|
||||
@@ -701,7 +699,7 @@ Hash_prefix (unsigned char *out, int b, const unsigned char *in, int inlen)
|
||||
x[0] = b;
|
||||
for (i = 0; i < inlen; ++i)
|
||||
x[i + 1] = in[i];
|
||||
crypto_hash_sha512 (h, x, inlen + 1);
|
||||
sha512 (x, inlen + 1, h);
|
||||
for (i = 0; i < 32; ++i)
|
||||
out[i] = h[i];
|
||||
}
|
||||
|
||||
642
src/gssapi.c
642
src/gssapi.c
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
@@ -29,14 +30,17 @@
|
||||
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
#include <libssh/buffer.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/crypto.h>
|
||||
#include <libssh/gssapi.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/ssh2.h>
|
||||
#include <libssh/buffer.h>
|
||||
#include <libssh/crypto.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/string.h>
|
||||
#include <libssh/server.h>
|
||||
#include <libssh/ssh2.h>
|
||||
#include <libssh/string.h>
|
||||
#include <libssh/token.h>
|
||||
|
||||
static gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
|
||||
|
||||
/** @internal
|
||||
* @initializes a gssapi context for authentication
|
||||
@@ -110,7 +114,6 @@ ssh_gssapi_free(ssh_session session)
|
||||
gss_release_oid(&min, &session->gssapi->client.oid);
|
||||
gss_delete_sec_context(&min, &session->gssapi->ctx, GSS_C_NO_BUFFER);
|
||||
|
||||
SAFE_FREE(session->gssapi->mech.elements);
|
||||
SAFE_FREE(session->gssapi->canonic_user);
|
||||
SAFE_FREE(session->gssapi);
|
||||
}
|
||||
@@ -147,6 +150,47 @@ static int ssh_gssapi_send_response(ssh_session session, ssh_string oid)
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
/** @internal
|
||||
* @brief get all the oids server supports
|
||||
* @param[out] selected OID set of supported oids
|
||||
* @returns SSH_OK if successful, SSH_ERROR otherwise
|
||||
*/
|
||||
int ssh_gssapi_server_oids(gss_OID_set *selected)
|
||||
{
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
size_t i;
|
||||
char *ptr = NULL;
|
||||
gss_OID_set supported; /* oids supported by server */
|
||||
|
||||
maj_stat = gss_indicate_mechs(&min_stat, &supported);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"indicate mechs",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < supported->count; ++i) {
|
||||
ptr = ssh_get_hexa(supported->elements[i].elements,
|
||||
supported->elements[i].length);
|
||||
/* According to RFC 4462 we MUST NOT use SPNEGO */
|
||||
if (supported->elements[i].length == spnego_oid.length &&
|
||||
memcmp(supported->elements[i].elements,
|
||||
spnego_oid.elements,
|
||||
supported->elements[i].length) == 0) {
|
||||
SAFE_FREE(ptr);
|
||||
continue;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
|
||||
SAFE_FREE(ptr);
|
||||
}
|
||||
|
||||
*selected = supported;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief handles an user authentication using GSSAPI
|
||||
*/
|
||||
@@ -154,12 +198,9 @@ int
|
||||
ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
uint32_t n_oid, ssh_string *oids)
|
||||
{
|
||||
char service_name[] = "host";
|
||||
gss_buffer_desc name_buf;
|
||||
gss_name_t server_name; /* local server fqdn */
|
||||
char hostname[NI_MAXHOST] = {0};
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
size_t i;
|
||||
char *ptr = NULL;
|
||||
gss_OID_set supported; /* oids supported by server */
|
||||
gss_OID_set both_supported; /* oids supported by both client and server */
|
||||
gss_OID_set selected; /* oid selected for authentication */
|
||||
@@ -167,12 +208,22 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
size_t oid_count=0;
|
||||
struct gss_OID_desc_struct oid;
|
||||
int rc;
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
|
||||
rc = gethostname(hostname, 64);
|
||||
if (rc != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Destroy earlier GSSAPI context if any */
|
||||
ssh_gssapi_free(session);
|
||||
rc = ssh_gssapi_init(session);
|
||||
if (rc == SSH_ERROR)
|
||||
if (rc == SSH_ERROR) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Callback should select oid and acquire credential */
|
||||
if (ssh_callbacks_exists(session->server_callbacks,
|
||||
@@ -197,23 +248,13 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
/* Default implementation for selecting oid and acquiring credential */
|
||||
gss_create_empty_oid_set(&min_stat, &both_supported);
|
||||
|
||||
maj_stat = gss_indicate_mechs(&min_stat, &supported);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "indicate mechs %d, %d", maj_stat, min_stat);
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"indicate mechs",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
gss_release_oid_set(&min_stat, &both_supported);
|
||||
/* Get the server supported oids */
|
||||
rc = ssh_gssapi_server_oids(&supported);
|
||||
if (rc != SSH_OK) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i=0; i < supported->count; ++i){
|
||||
ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length);
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/* Loop through client supported oids */
|
||||
for (i=0 ; i< n_oid ; ++i){
|
||||
unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]);
|
||||
size_t len = ssh_string_len(oids[i]);
|
||||
@@ -225,8 +266,10 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
|
||||
continue;
|
||||
}
|
||||
/* Convert oid from string to gssapi format */
|
||||
oid.elements = &oid_s[2];
|
||||
oid.length = len - 2;
|
||||
/* Check if this client oid is supported by server */
|
||||
gss_test_oid_set_member(&min_stat,&oid,supported,&present);
|
||||
if(present){
|
||||
gss_add_oid_set_member(&min_stat,&oid,&both_supported);
|
||||
@@ -241,28 +284,23 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
name_buf.value = service_name;
|
||||
name_buf.length = strlen(name_buf.value) + 1;
|
||||
maj_stat = gss_import_name(&min_stat, &name_buf,
|
||||
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"importing name",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
rc = ssh_gssapi_import_name(session->gssapi, hostname);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_auth_reply_default(session, 0);
|
||||
gss_release_oid_set(&min_stat, &both_supported);
|
||||
return -1;
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
|
||||
both_supported, GSS_C_ACCEPT,
|
||||
&session->gssapi->server_creds, &selected, NULL);
|
||||
gss_release_name(&min_stat, &server_name);
|
||||
maj_stat = gss_acquire_cred(&min_stat,
|
||||
session->gssapi->client.server_name,
|
||||
0,
|
||||
both_supported,
|
||||
GSS_C_ACCEPT,
|
||||
&session->gssapi->server_creds,
|
||||
&selected,
|
||||
NULL);
|
||||
gss_release_oid_set(&min_stat, &both_supported);
|
||||
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "error acquiring credentials %d, %d", maj_stat, min_stat);
|
||||
ssh_gssapi_log_error(SSH_LOG_TRACE,
|
||||
"acquiring creds",
|
||||
maj_stat,
|
||||
@@ -270,8 +308,7 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
ssh_auth_reply_default(session,0);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_DEBUG, "acquiring credentials %d, %d", maj_stat, min_stat);
|
||||
SSH_LOG(SSH_LOG_DEBUG, "acquired credentials");
|
||||
|
||||
/* finding which OID from client we selected */
|
||||
for (i=0 ; i< n_oid ; ++i){
|
||||
@@ -293,17 +330,8 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
break;
|
||||
}
|
||||
}
|
||||
session->gssapi->mech.length = oid.length;
|
||||
session->gssapi->mech.elements = malloc(oid.length);
|
||||
if (session->gssapi->mech.elements == NULL){
|
||||
ssh_set_error_oom(session);
|
||||
gss_release_oid_set(&min_stat, &selected);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(session->gssapi->mech.elements, oid.elements, oid.length);
|
||||
gss_release_oid_set(&min_stat, &selected);
|
||||
session->gssapi->user = strdup(user);
|
||||
session->gssapi->service = service_name;
|
||||
session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN;
|
||||
return ssh_gssapi_send_response(session, oids[i]);
|
||||
}
|
||||
@@ -381,7 +409,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token));
|
||||
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa);
|
||||
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s", hexa);
|
||||
SAFE_FREE(hexa);
|
||||
input_token.length = ssh_string_len(token);
|
||||
input_token.value = ssh_string_data(token);
|
||||
@@ -400,7 +428,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
|
||||
}
|
||||
if (GSS_ERROR(maj_stat)){
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"Gssapi error",
|
||||
"accepting token failed",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
@@ -428,7 +456,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
gss_release_name(&min_stat, &client_name);
|
||||
|
||||
if(maj_stat == GSS_S_COMPLETE){
|
||||
if (maj_stat == GSS_S_COMPLETE) {
|
||||
session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC;
|
||||
}
|
||||
return SSH_PACKET_USED;
|
||||
@@ -436,7 +464,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
static ssh_buffer ssh_gssapi_build_mic(ssh_session session)
|
||||
ssh_buffer ssh_gssapi_build_mic(ssh_session session, const char *context)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = NULL;
|
||||
ssh_buffer mic_buffer = NULL;
|
||||
@@ -456,11 +484,12 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session)
|
||||
rc = ssh_buffer_pack(mic_buffer,
|
||||
"dPbsss",
|
||||
crypto->session_id_len,
|
||||
crypto->session_id_len, crypto->session_id,
|
||||
crypto->session_id_len,
|
||||
crypto->session_id,
|
||||
SSH2_MSG_USERAUTH_REQUEST,
|
||||
session->gssapi->user,
|
||||
"ssh-connection",
|
||||
"gssapi-with-mic");
|
||||
context);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
SSH_BUFFER_FREE(mic_buffer);
|
||||
@@ -483,24 +512,27 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic)
|
||||
(void)user;
|
||||
(void)type;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_MIC");
|
||||
SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_USERAUTH_GSSAPI_MIC");
|
||||
mic_token = ssh_buffer_get_ssh_string(packet);
|
||||
if (mic_token == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Missing MIC in packet");
|
||||
goto error;
|
||||
}
|
||||
if (session->gssapi == NULL
|
||||
|| session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) {
|
||||
ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_MIC in invalid state");
|
||||
if (session->gssapi == NULL ||
|
||||
session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Received SSH_MSG_USERAUTH_GSSAPI_MIC in invalid state");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mic_buffer = ssh_gssapi_build_mic(session);
|
||||
mic_buffer = ssh_gssapi_build_mic(session, "gssapi-with-mic");
|
||||
if (mic_buffer == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
if (ssh_callbacks_exists(session->server_callbacks, gssapi_verify_mic_function)){
|
||||
if (ssh_callbacks_exists(session->server_callbacks,
|
||||
gssapi_verify_mic_function)) {
|
||||
int rc = session->server_callbacks->gssapi_verify_mic_function(session, mic_token,
|
||||
ssh_buffer_get(mic_buffer), ssh_buffer_get_len(mic_buffer),
|
||||
session->server_callbacks->userdata);
|
||||
@@ -513,7 +545,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic)
|
||||
mic_token_buf.length = ssh_string_len(mic_token);
|
||||
mic_token_buf.value = ssh_string_data(mic_token);
|
||||
|
||||
maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL);
|
||||
maj_stat = gss_verify_mic(&min_stat,
|
||||
session->gssapi->ctx,
|
||||
&mic_buf,
|
||||
&mic_token_buf,
|
||||
NULL);
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"verifying MIC",
|
||||
maj_stat,
|
||||
@@ -580,12 +616,14 @@ ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session)
|
||||
*/
|
||||
void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (session == NULL) {
|
||||
return;
|
||||
}
|
||||
if (session->gssapi == NULL) {
|
||||
ssh_gssapi_init(session);
|
||||
if (session->gssapi == NULL) {
|
||||
rc = ssh_gssapi_init(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -626,9 +664,182 @@ fail:
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/** @brief returns the OIDs of the mechs that have usable credentials
|
||||
/** @internal
|
||||
* @brief Get the base64 encoding of md5 of the oid to add as suffix to GSSAPI
|
||||
* key exchange algorithms.
|
||||
*
|
||||
* @param[in] oid The OID as a ssh_string
|
||||
*
|
||||
* @returns the hash or NULL on error
|
||||
*/
|
||||
static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
|
||||
char *ssh_gssapi_oid_hash(ssh_string oid)
|
||||
{
|
||||
unsigned char *h = NULL;
|
||||
int rc;
|
||||
char *base64 = NULL;
|
||||
|
||||
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
|
||||
if (h == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = md5(ssh_string_data(oid), ssh_string_len(oid), h);
|
||||
if (rc != SSH_OK) {
|
||||
SAFE_FREE(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base64 = (char *)bin_to_base64(h, 16);
|
||||
SAFE_FREE(h);
|
||||
return base64;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check if client has GSSAPI mechanisms configured
|
||||
*
|
||||
* @param[in] session The SSH session
|
||||
*
|
||||
* @returns SSH_OK if any one of the mechanisms is configured or NULL
|
||||
*/
|
||||
int ssh_gssapi_check_client_config(ssh_session session)
|
||||
{
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
size_t i;
|
||||
char *ptr = NULL;
|
||||
gss_OID_set supported = GSS_C_NO_OID_SET;
|
||||
gss_name_t client_id = GSS_C_NO_NAME;
|
||||
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 oflags;
|
||||
struct ssh_gssapi_struct *gssapi = NULL;
|
||||
int ret = SSH_ERROR;
|
||||
gss_OID_set one_oidset = GSS_C_NO_OID_SET;
|
||||
|
||||
maj_stat = gss_indicate_mechs(&min_stat, &supported);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"indicate mechs",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < supported->count; ++i) {
|
||||
gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
|
||||
if (gssapi == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
gssapi->server_creds = GSS_C_NO_CREDENTIAL;
|
||||
gssapi->client_creds = GSS_C_NO_CREDENTIAL;
|
||||
gssapi->ctx = GSS_C_NO_CONTEXT;
|
||||
gssapi->state = SSH_GSSAPI_STATE_NONE;
|
||||
|
||||
/* According to RFC 4462 we MUST NOT use SPNEGO */
|
||||
if (supported->elements[i].length == spnego_oid.length &&
|
||||
memcmp(supported->elements[i].elements,
|
||||
spnego_oid.elements,
|
||||
supported->elements[i].length) == 0) {
|
||||
ret = SSH_ERROR;
|
||||
goto end;
|
||||
}
|
||||
|
||||
gss_create_empty_oid_set(&min_stat, &one_oidset);
|
||||
gss_add_oid_set_member(&min_stat, &supported->elements[i], &one_oidset);
|
||||
|
||||
if (session->opts.gss_client_identity != NULL) {
|
||||
namebuf.value = (void *)session->opts.gss_client_identity;
|
||||
namebuf.length = strlen(session->opts.gss_client_identity);
|
||||
|
||||
maj_stat = gss_import_name(&min_stat,
|
||||
&namebuf,
|
||||
GSS_C_NT_USER_NAME,
|
||||
&client_id);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ret = SSH_ERROR;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
maj_stat = gss_acquire_cred(&min_stat,
|
||||
client_id,
|
||||
GSS_C_INDEFINITE,
|
||||
one_oidset,
|
||||
GSS_C_INITIATE,
|
||||
&gssapi->client.creds,
|
||||
NULL,
|
||||
NULL);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_WARN,
|
||||
"acquiring credential",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
ret = SSH_ERROR;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = ssh_gssapi_import_name(gssapi, session->opts.host);
|
||||
if (ret != SSH_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
maj_stat =
|
||||
ssh_gssapi_init_ctx(gssapi, &input_token, &output_token, &oflags);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_WARN,
|
||||
"initializing context",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
ret = SSH_ERROR;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ptr = ssh_get_hexa(supported->elements[i].elements,
|
||||
supported->elements[i].length);
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
|
||||
free(ptr);
|
||||
|
||||
/* If at least one mechanism is configured then return successfully */
|
||||
ret = SSH_OK;
|
||||
|
||||
end:
|
||||
if (ret == SSH_ERROR) {
|
||||
SSH_LOG(SSH_LOG_WARN, "GSSAPI not configured correctly");
|
||||
}
|
||||
SAFE_FREE(gssapi->user);
|
||||
|
||||
gss_release_oid_set(&min_stat, &one_oidset);
|
||||
|
||||
gss_release_name(&min_stat, &gssapi->client.server_name);
|
||||
gss_release_cred(&min_stat, &gssapi->server_creds);
|
||||
gss_release_cred(&min_stat, &gssapi->client.creds);
|
||||
gss_release_oid(&min_stat, &gssapi->client.oid);
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
gss_delete_sec_context(&min_stat, &gssapi->ctx, GSS_C_NO_BUFFER);
|
||||
|
||||
SAFE_FREE(gssapi->canonic_user);
|
||||
SAFE_FREE(gssapi);
|
||||
|
||||
if (ret == SSH_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
gss_release_oid_set(&min_stat, &supported);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief acquires a credential and returns a set of mechanisms for which it is
|
||||
* valid
|
||||
*
|
||||
* @param[in] session The SSH session
|
||||
* @param[out] valid_oids The set of OIDs for which the credential is valid
|
||||
*
|
||||
* @returns SSH_OK if successful, SSH_ERROR otherwise
|
||||
*/
|
||||
int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids)
|
||||
{
|
||||
OM_uint32 maj_stat, min_stat, lifetime;
|
||||
gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
|
||||
@@ -675,6 +886,7 @@ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "acquired credentials");
|
||||
|
||||
gss_create_empty_oid_set(&min_stat, valid_oids);
|
||||
|
||||
@@ -702,6 +914,189 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Add suffixes of oid hash to each GSSAPI key exchange algorithm
|
||||
* @param[in] session current session handler
|
||||
* @returns string suffixed kex algorithms or NULL on error
|
||||
*/
|
||||
char *ssh_gssapi_kex_mechs(ssh_session session)
|
||||
{
|
||||
size_t i, j;
|
||||
/* oid selected for authentication */
|
||||
gss_OID_set selected = GSS_C_NO_OID_SET;
|
||||
ssh_string *oids = NULL;
|
||||
int rc;
|
||||
size_t n_oids = 0;
|
||||
struct ssh_tokens_st *algs = NULL;
|
||||
char *oid_hash = NULL;
|
||||
const char *gss_algs = session->opts.gssapi_key_exchange_algs;
|
||||
char *new_gss_algs = NULL;
|
||||
char gss_kex_algs[8000] = {0};
|
||||
OM_uint32 min_stat;
|
||||
size_t offset = 0;
|
||||
|
||||
/* Get supported oids */
|
||||
if (session->server) {
|
||||
#ifdef WITH_SERVER
|
||||
rc = ssh_gssapi_server_oids(&selected);
|
||||
if (rc == SSH_ERROR) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
rc = ssh_gssapi_client_identity(session, &selected);
|
||||
if (rc == SSH_ERROR) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
ssh_gssapi_free(session);
|
||||
|
||||
n_oids = selected->count;
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Sending %zu oids", n_oids);
|
||||
|
||||
oids = calloc(n_oids, sizeof(ssh_string));
|
||||
if (oids == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if algorithms are valid */
|
||||
new_gss_algs =
|
||||
ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, gss_algs);
|
||||
if (gss_algs == NULL) {
|
||||
ssh_set_error(
|
||||
session,
|
||||
SSH_FATAL,
|
||||
"GSSAPI key exchange algorithms not supported or invalid");
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
algs = ssh_tokenize(new_gss_algs, ',');
|
||||
if (algs == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Couldn't tokenize GSSAPI key exchange algs");
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < n_oids; ++i) {
|
||||
oids[i] = ssh_string_new(selected->elements[i].length + 2);
|
||||
if (oids[i] == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
((unsigned char *)oids[i]->data)[0] = SSH_OID_TAG;
|
||||
((unsigned char *)oids[i]->data)[1] = selected->elements[i].length;
|
||||
memcpy((unsigned char *)oids[i]->data + 2,
|
||||
selected->elements[i].elements,
|
||||
selected->elements[i].length);
|
||||
|
||||
/* Get the algorithm suffix */
|
||||
oid_hash = ssh_gssapi_oid_hash(oids[i]);
|
||||
if (oid_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* For each oid loop through the algorithms, append the oid and append
|
||||
* the algorithms to a string */
|
||||
for (j = 0; algs->tokens[j]; j++) {
|
||||
if (sizeof(gss_kex_algs) < offset) {
|
||||
ssh_set_error(session, SSH_FATAL, "snprintf failed");
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
rc = snprintf(&gss_kex_algs[offset],
|
||||
sizeof(gss_kex_algs) - offset,
|
||||
"%s%s,",
|
||||
algs->tokens[j],
|
||||
oid_hash);
|
||||
if (rc < 0 || rc >= (ssize_t)sizeof(gss_kex_algs)) {
|
||||
ssh_set_error(session, SSH_FATAL, "snprintf failed");
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
/* + 1 for ',' */
|
||||
offset += strlen(algs->tokens[j]) + strlen(oid_hash) + 1;
|
||||
}
|
||||
SAFE_FREE(oid_hash);
|
||||
SSH_STRING_FREE(oids[i]);
|
||||
}
|
||||
|
||||
rc = SSH_OK;
|
||||
|
||||
out:
|
||||
SAFE_FREE(oid_hash);
|
||||
SAFE_FREE(oids);
|
||||
SAFE_FREE(new_gss_algs);
|
||||
gss_release_oid_set(&min_stat, &selected);
|
||||
ssh_tokens_free(algs);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return strdup(gss_kex_algs);
|
||||
}
|
||||
|
||||
int ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host)
|
||||
{
|
||||
gss_buffer_desc hostname;
|
||||
char name_buf[256] = {0};
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
|
||||
/* import target host name */
|
||||
snprintf(name_buf, sizeof(name_buf), "host@%s", host);
|
||||
|
||||
hostname.value = name_buf;
|
||||
hostname.length = strlen(name_buf) + 1;
|
||||
maj_stat = gss_import_name(&min_stat,
|
||||
&hostname,
|
||||
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
|
||||
&gssapi->client.server_name);
|
||||
SSH_LOG(SSH_LOG_DEBUG, "importing name: %s", name_buf);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"error importing name",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
}
|
||||
|
||||
return maj_stat;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
|
||||
maj_stat = gss_init_sec_context(&min_stat,
|
||||
gssapi->client.creds,
|
||||
&gssapi->ctx,
|
||||
gssapi->client.server_name,
|
||||
gssapi->client.oid,
|
||||
gssapi->client.flags,
|
||||
0,
|
||||
NULL,
|
||||
input_token,
|
||||
NULL,
|
||||
output_token,
|
||||
ret_flags,
|
||||
NULL);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"initializing gssapi context",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
}
|
||||
return maj_stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief launches a gssapi-with-mic auth request
|
||||
* @returns SSH_AUTH_ERROR: A serious error happened\n
|
||||
@@ -716,9 +1111,7 @@ int ssh_gssapi_auth_mic(ssh_session session)
|
||||
ssh_string *oids = NULL;
|
||||
int rc;
|
||||
size_t n_oids = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
char name_buf[256] = {0};
|
||||
gss_buffer_desc hostname;
|
||||
OM_uint32 min_stat;
|
||||
const char *gss_host = session->opts.host;
|
||||
|
||||
/* Destroy earlier GSSAPI context if any */
|
||||
@@ -731,20 +1124,9 @@ int ssh_gssapi_auth_mic(ssh_session session)
|
||||
if (session->opts.gss_server_identity != NULL) {
|
||||
gss_host = session->opts.gss_server_identity;
|
||||
}
|
||||
/* import target host name */
|
||||
snprintf(name_buf, sizeof(name_buf), "host@%s", gss_host);
|
||||
|
||||
hostname.value = name_buf;
|
||||
hostname.length = strlen(name_buf) + 1;
|
||||
maj_stat = gss_import_name(&min_stat, &hostname,
|
||||
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
|
||||
&session->gssapi->client.server_name);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"importing name",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
rc = ssh_gssapi_import_name(session->gssapi, gss_host);
|
||||
if (rc != SSH_OK) {
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
@@ -757,7 +1139,7 @@ int ssh_gssapi_auth_mic(ssh_session session)
|
||||
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi to host %s with user %s",
|
||||
session->opts.host, session->gssapi->user);
|
||||
rc = ssh_gssapi_match(session, &selected);
|
||||
rc = ssh_gssapi_client_identity(session, &selected);
|
||||
if (rc == SSH_ERROR) {
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
@@ -800,6 +1182,50 @@ out:
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MIC for "gssapi-keyex" authentication.
|
||||
* @returns SSH_ERROR: A serious error happened\n
|
||||
* SSH_OK: MIC token is stored in mic_token_buf
|
||||
*/
|
||||
int ssh_gssapi_auth_keyex_mic(ssh_session session,
|
||||
gss_buffer_desc *mic_token_buf)
|
||||
{
|
||||
ssh_buffer buf = NULL;
|
||||
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
|
||||
if (session->gssapi == NULL || session->gssapi->ctx == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
buf = ssh_gssapi_build_mic(session, "gssapi-keyex");
|
||||
if (buf == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
mic_buf.length = ssh_buffer_get_len(buf);
|
||||
mic_buf.value = ssh_buffer_get(buf);
|
||||
|
||||
maj_stat = gss_get_mic(&min_stat,
|
||||
session->gssapi->ctx,
|
||||
GSS_C_QOP_DEFAULT,
|
||||
&mic_buf,
|
||||
mic_token_buf);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"generating MIC",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
SSH_BUFFER_FREE(buf);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SSH_BUFFER_FREE(buf);
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s)
|
||||
{
|
||||
gss_OID ret = NULL;
|
||||
@@ -869,22 +1295,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
|
||||
session->gssapi->client.flags |= GSS_C_DELEG_FLAG;
|
||||
}
|
||||
|
||||
/* prepare the first TOKEN response */
|
||||
maj_stat = gss_init_sec_context(&min_stat,
|
||||
session->gssapi->client.creds,
|
||||
&session->gssapi->ctx,
|
||||
session->gssapi->client.server_name,
|
||||
session->gssapi->client.oid,
|
||||
session->gssapi->client.flags,
|
||||
0, NULL, &input_token, NULL,
|
||||
&output_token, NULL, NULL);
|
||||
if(GSS_ERROR(maj_stat)){
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"Initializing gssapi context",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
maj_stat =
|
||||
ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, NULL);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (output_token.length != 0){
|
||||
hexa = ssh_get_hexa(output_token.value, output_token.length);
|
||||
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s", hexa);
|
||||
@@ -920,7 +1336,7 @@ static int ssh_gssapi_send_mic(ssh_session session)
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET,"Sending SSH_MSG_USERAUTH_GSSAPI_MIC");
|
||||
|
||||
mic_buffer = ssh_gssapi_build_mic(session);
|
||||
mic_buffer = ssh_gssapi_build_mic(session, "gssapi-with-mic");
|
||||
if (mic_buffer == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
@@ -984,27 +1400,13 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client)
|
||||
hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token));
|
||||
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa);
|
||||
SAFE_FREE(hexa);
|
||||
|
||||
input_token.length = ssh_string_len(token);
|
||||
input_token.value = ssh_string_data(token);
|
||||
maj_stat = gss_init_sec_context(&min_stat,
|
||||
session->gssapi->client.creds,
|
||||
&session->gssapi->ctx,
|
||||
session->gssapi->client.server_name,
|
||||
session->gssapi->client.oid,
|
||||
session->gssapi->client.flags,
|
||||
0, NULL, &input_token, NULL,
|
||||
&output_token, NULL, NULL);
|
||||
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"accepting token",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
maj_stat =
|
||||
ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, NULL);
|
||||
SSH_STRING_FREE(token);
|
||||
if (GSS_ERROR(maj_stat)){
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"Gssapi error",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/hybrid_mlkem.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
/* sorry, this needs to come last to avoid header dependency issues */
|
||||
#include "libssh/bignum.h"
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_hybrid_mlkem_reply);
|
||||
|
||||
static ssh_packet_callback dh_client_callbacks[] = {
|
||||
@@ -43,44 +45,68 @@ static struct ssh_packet_callbacks_struct ssh_hybrid_mlkem_client_callbacks = {
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
static ssh_string derive_ecdh_secret(ssh_session session)
|
||||
static ssh_string derive_curve25519_secret(ssh_session session)
|
||||
{
|
||||
ssh_string secret = NULL;
|
||||
int rc;
|
||||
|
||||
secret = ssh_string_new(CURVE25519_PUBKEY_SIZE);
|
||||
if (secret == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_curve25519_create_k(session, ssh_string_data(secret));
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Curve25519 secret derivation failed");
|
||||
ssh_string_free(secret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return secret;
|
||||
}
|
||||
|
||||
static ssh_string derive_nist_curve_secret(ssh_session session,
|
||||
size_t secret_size)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_string secret = NULL;
|
||||
size_t secret_size;
|
||||
int rc;
|
||||
|
||||
switch (crypto->kex_type) {
|
||||
rc = ecdh_build_k(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "ECDH secret derivation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
secret = ssh_make_padded_bignum_string(crypto->shared_secret, secret_size);
|
||||
if (secret == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to encode the shared secret");
|
||||
}
|
||||
|
||||
bignum_safe_free(crypto->shared_secret);
|
||||
|
||||
return secret;
|
||||
}
|
||||
|
||||
static ssh_string derive_ecdh_secret(ssh_session session)
|
||||
{
|
||||
ssh_string secret = NULL;
|
||||
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
secret = ssh_string_new(CURVE25519_PUBKEY_SIZE);
|
||||
if (secret == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return NULL;
|
||||
}
|
||||
rc = ssh_curve25519_create_k(session, ssh_string_data(secret));
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Curve25519 secret derivation failed");
|
||||
ssh_string_free(secret);
|
||||
return NULL;
|
||||
}
|
||||
secret = derive_curve25519_secret(session);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
rc = ecdh_build_k(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "ECDH secret derivation failed");
|
||||
return NULL;
|
||||
}
|
||||
secret_size = bignum_num_bytes(crypto->shared_secret);
|
||||
secret = ssh_string_new(secret_size);
|
||||
if (secret == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
bignum_safe_free(crypto->shared_secret);
|
||||
return NULL;
|
||||
}
|
||||
bignum_bn2bin(crypto->shared_secret, secret_size, ssh_string_data(secret));
|
||||
bignum_safe_free(crypto->shared_secret);
|
||||
secret = derive_nist_curve_secret(session, NISTP256_SHARED_SECRET_SIZE);
|
||||
break;
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
secret = derive_nist_curve_secret(session, NISTP384_SHARED_SECRET_SIZE);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported KEX type");
|
||||
return NULL;
|
||||
@@ -105,10 +131,12 @@ static int derive_hybrid_secret(ssh_session session,
|
||||
digest = sha256;
|
||||
digest_len = SHA256_DIGEST_LEN;
|
||||
break;
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
digest = sha384;
|
||||
digest_len = SHA384_DIGEST_LEN;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported KEX type");
|
||||
goto cleanup;
|
||||
@@ -220,7 +248,9 @@ int ssh_client_hybrid_mlkem_init(ssh_session session)
|
||||
crypto->curve25519_client_pubkey);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
@@ -411,7 +441,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_hybrid_mlkem_reply)
|
||||
#endif
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
ecdh_server_pubkey_size = ssh_buffer_get_len(server_reply_buffer);
|
||||
ssh_string_free(crypto->ecdh_server_pubkey);
|
||||
crypto->ecdh_server_pubkey = ssh_string_new(ecdh_server_pubkey_size);
|
||||
@@ -561,7 +593,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
|
||||
#endif
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
@@ -658,7 +692,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
|
||||
#endif
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
ecdh_client_pubkey_size = ssh_buffer_get_len(client_init_buffer);
|
||||
ssh_string_free(crypto->ecdh_client_pubkey);
|
||||
crypto->ecdh_client_pubkey = ssh_string_new(ecdh_client_pubkey_size);
|
||||
@@ -736,7 +772,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
|
||||
crypto->curve25519_server_pubkey);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
rc = ssh_buffer_pack(server_reply_buffer,
|
||||
"PP",
|
||||
ssh_string_len(crypto->mlkem_ciphertext),
|
||||
|
||||
670
src/kex-gss.c
Normal file
670
src/kex-gss.c
Normal file
@@ -0,0 +1,670 @@
|
||||
/*
|
||||
* kex-gss.c - 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/gssapi.h"
|
||||
#include <errno.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/kex-gss.h"
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/ecdh.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_reply);
|
||||
|
||||
static ssh_packet_callback gss_kex_client_callbacks[] = {
|
||||
ssh_packet_client_gss_kex_reply,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_gss_kex_client_callbacks = {
|
||||
.start = SSH2_MSG_KEXGSS_COMPLETE,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = gss_kex_client_callbacks,
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_hostkey);
|
||||
|
||||
static ssh_packet_callback gss_kex_client_callback_hostkey[] = {
|
||||
ssh_packet_client_gss_kex_hostkey,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_gss_kex_client_callback_hostkey = {
|
||||
.start = SSH2_MSG_KEXGSS_HOSTKEY,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = gss_kex_client_callback_hostkey,
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
static ssh_string dh_init(ssh_session session)
|
||||
{
|
||||
int rc, keypair;
|
||||
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
const_bignum const_pubkey;
|
||||
#endif
|
||||
bignum pubkey = NULL;
|
||||
ssh_string pubkey_string = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
|
||||
if (session->server) {
|
||||
keypair = DH_SERVER_KEYPAIR;
|
||||
} else {
|
||||
keypair = DH_CLIENT_KEYPAIR;
|
||||
}
|
||||
|
||||
rc = ssh_dh_init_common(crypto);
|
||||
if (rc != SSH_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = ssh_dh_keypair_gen_keys(crypto->dh_ctx, keypair);
|
||||
if (rc != SSH_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
rc = ssh_dh_keypair_get_keys(crypto->dh_ctx, keypair, NULL, &const_pubkey);
|
||||
bignum_dup(const_pubkey, &pubkey);
|
||||
#else
|
||||
rc = ssh_dh_keypair_get_keys(crypto->dh_ctx, keypair, NULL, &pubkey);
|
||||
#endif
|
||||
if (rc != SSH_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
pubkey_string = ssh_make_bignum_string(pubkey);
|
||||
|
||||
end:
|
||||
bignum_safe_free(pubkey);
|
||||
return pubkey_string;
|
||||
}
|
||||
|
||||
static int dh_import_peer_key(ssh_session session, ssh_string peer_key)
|
||||
{
|
||||
int rc, keypair;
|
||||
bignum peer_key_bn;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
|
||||
if (session->server) {
|
||||
keypair = DH_CLIENT_KEYPAIR;
|
||||
} else {
|
||||
keypair = DH_SERVER_KEYPAIR;
|
||||
}
|
||||
|
||||
peer_key_bn = ssh_make_string_bn(peer_key);
|
||||
rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, keypair, NULL, peer_key_bn);
|
||||
if (rc != SSH_OK) {
|
||||
bignum_safe_free(peer_key_bn);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Starts gssapi key exchange
|
||||
*/
|
||||
int ssh_client_gss_kex_init(ssh_session session)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
int rc, ret = SSH_ERROR;
|
||||
/* oid selected for authentication */
|
||||
gss_OID_set selected = GSS_C_NO_OID_SET;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
const char *gss_host = session->opts.host;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 oflags;
|
||||
ssh_string pubkey = NULL;
|
||||
|
||||
switch (crypto->kex_type) {
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
pubkey = dh_init(session);
|
||||
if (pubkey == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to generate DH keypair");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to generate ECDH keypair");
|
||||
goto out;
|
||||
}
|
||||
pubkey = ssh_string_copy(crypto->ecdh_client_pubkey);
|
||||
break;
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
rc = ssh_curve25519_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to generate Curve25519 keypair");
|
||||
goto out;
|
||||
}
|
||||
pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE);
|
||||
if (pubkey == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto out;
|
||||
}
|
||||
rc = ssh_string_fill(pubkey,
|
||||
crypto->curve25519_client_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to copy Curve25519 pubkey");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported GSSAPI KEX method");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (session->opts.gss_server_identity != NULL) {
|
||||
gss_host = session->opts.gss_server_identity;
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_import_name(session->gssapi, gss_host);
|
||||
if (rc != SSH_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_client_identity(session, &selected);
|
||||
if (rc != SSH_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
session->gssapi->client.flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
|
||||
maj_stat = ssh_gssapi_init_ctx(session->gssapi,
|
||||
&input_token,
|
||||
&output_token,
|
||||
&oflags);
|
||||
gss_release_oid_set(&min_stat, &selected);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_WARN,
|
||||
"Initializing gssapi context",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
goto out;
|
||||
}
|
||||
if (!(oflags & GSS_C_INTEG_FLAG) || !(oflags & GSS_C_MUTUAL_FLAG)) {
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"GSSAPI(init) integrity and mutual flags were not set");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bdPS",
|
||||
SSH2_MSG_KEXGSS_INIT,
|
||||
output_token.length,
|
||||
(size_t)output_token.length,
|
||||
output_token.value,
|
||||
pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* register the packet callbacks */
|
||||
ssh_packet_set_callbacks(session, &ssh_gss_kex_client_callbacks);
|
||||
ssh_packet_set_callbacks(session, &ssh_gss_kex_client_callback_hostkey);
|
||||
session->dh_handshake_state = DH_STATE_INIT_SENT;
|
||||
|
||||
rc = ssh_packet_send(session);
|
||||
if (rc != SSH_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
ssh_string_free(pubkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ssh_client_gss_kex_remove_callbacks(ssh_session session)
|
||||
{
|
||||
ssh_packet_remove_callbacks(session, &ssh_gss_kex_client_callbacks);
|
||||
}
|
||||
|
||||
void ssh_client_gss_kex_remove_callback_hostkey(ssh_session session)
|
||||
{
|
||||
ssh_packet_remove_callbacks(session, &ssh_gss_kex_client_callback_hostkey);
|
||||
}
|
||||
|
||||
SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_reply)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_string mic = NULL, otoken = NULL, server_pubkey = NULL;
|
||||
uint8_t b;
|
||||
int rc;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 oflags;
|
||||
OM_uint32 maj_stat;
|
||||
|
||||
(void)type;
|
||||
(void)user;
|
||||
|
||||
ssh_client_gss_kex_remove_callbacks(session);
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "SSbS", &server_pubkey, &mic, &b, &otoken);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "No public key in server reply");
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSH_STRING_FREE(session->gssapi_key_exchange_mic);
|
||||
session->gssapi_key_exchange_mic = mic;
|
||||
input_token.length = ssh_string_len(otoken);
|
||||
input_token.value = ssh_string_data(otoken);
|
||||
maj_stat = ssh_gssapi_init_ctx(session->gssapi,
|
||||
&input_token,
|
||||
&output_token,
|
||||
&oflags);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
goto error;
|
||||
}
|
||||
SSH_STRING_FREE(otoken);
|
||||
|
||||
switch (crypto->kex_type) {
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
rc = dh_import_peer_key(session, server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not import server pubkey");
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_dh_compute_shared_secret(crypto->dh_ctx,
|
||||
DH_CLIENT_KEYPAIR,
|
||||
DH_SERVER_KEYPAIR,
|
||||
&crypto->shared_secret);
|
||||
ssh_dh_debug_crypto(crypto);
|
||||
break;
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
crypto->ecdh_server_pubkey = ssh_string_copy(server_pubkey);
|
||||
rc = ecdh_build_k(session);
|
||||
break;
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
memcpy(crypto->curve25519_server_pubkey,
|
||||
ssh_string_data(server_pubkey),
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
rc = ssh_curve25519_build_k(session);
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported GSSAPI KEX method");
|
||||
goto error;
|
||||
}
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not derive shared secret");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Send the MSG_NEWKEYS */
|
||||
rc = ssh_packet_send_newkeys(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ssh_string_free(server_pubkey);
|
||||
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
|
||||
return SSH_PACKET_USED;
|
||||
|
||||
error:
|
||||
ssh_string_free(server_pubkey);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_hostkey)
|
||||
{
|
||||
ssh_string pubkey_blob = NULL;
|
||||
int rc;
|
||||
|
||||
(void)type;
|
||||
(void)user;
|
||||
|
||||
ssh_client_gss_kex_remove_callback_hostkey(session);
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "S", &pubkey_blob);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid SSH2_MSG_KEXGSS_HOSTKEY packet");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
error:
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_gss_kex_init);
|
||||
|
||||
static ssh_packet_callback gss_kex_server_callbacks[] = {
|
||||
ssh_packet_server_gss_kex_init,
|
||||
};
|
||||
|
||||
static struct ssh_packet_callbacks_struct ssh_gss_kex_server_callbacks = {
|
||||
.start = SSH2_MSG_KEXGSS_INIT,
|
||||
.n_callbacks = 1,
|
||||
.callbacks = gss_kex_server_callbacks,
|
||||
.user = NULL,
|
||||
};
|
||||
|
||||
/** @internal
|
||||
* @brief sets up the gssapi kex callbacks
|
||||
*/
|
||||
void ssh_server_gss_kex_init(ssh_session session)
|
||||
{
|
||||
/* register the packet callbacks */
|
||||
ssh_packet_set_callbacks(session, &ssh_gss_kex_server_callbacks);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief processes a SSH_MSG_KEXGSS_INIT and sends
|
||||
* the appropriate SSH_MSG_KEXGSS_COMPLETE
|
||||
*/
|
||||
int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_key privkey = NULL;
|
||||
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
|
||||
ssh_string client_pubkey = NULL;
|
||||
ssh_string server_pubkey = NULL;
|
||||
int rc;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
|
||||
ssh_string otoken = NULL;
|
||||
ssh_string server_pubkey_blob = NULL;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
gss_name_t client_name = GSS_C_NO_NAME;
|
||||
OM_uint32 ret_flags = 0;
|
||||
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
|
||||
char hostname[NI_MAXHOST] = {0};
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "S", &otoken);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session, SSH_FATAL, "No token in client request");
|
||||
goto error;
|
||||
}
|
||||
input_token.length = ssh_string_len(otoken);
|
||||
input_token.value = ssh_string_data(otoken);
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "S", &client_pubkey);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session, SSH_FATAL, "No public key in client request");
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (crypto->kex_type) {
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
server_pubkey = dh_init(session);
|
||||
if (server_pubkey == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not generate a DH keypair");
|
||||
goto error;
|
||||
}
|
||||
rc = dh_import_peer_key(session, client_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not import client pubkey");
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_dh_compute_shared_secret(crypto->dh_ctx,
|
||||
DH_SERVER_KEYPAIR,
|
||||
DH_CLIENT_KEYPAIR,
|
||||
&crypto->shared_secret);
|
||||
ssh_dh_debug_crypto(crypto);
|
||||
break;
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
rc = ssh_ecdh_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not generate an ECDH keypair");
|
||||
goto error;
|
||||
}
|
||||
crypto->ecdh_client_pubkey = ssh_string_copy(client_pubkey);
|
||||
server_pubkey = ssh_string_copy(crypto->ecdh_server_pubkey);
|
||||
rc = ecdh_build_k(session);
|
||||
break;
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
rc = ssh_curve25519_init(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not generate a Curve25519 keypair");
|
||||
goto error;
|
||||
}
|
||||
server_pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE);
|
||||
if (server_pubkey == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_string_fill(server_pubkey,
|
||||
crypto->curve25519_server_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to copy Curve25519 pubkey");
|
||||
goto error;
|
||||
}
|
||||
memcpy(crypto->curve25519_client_pubkey,
|
||||
ssh_string_data(client_pubkey),
|
||||
CURVE25519_PUBKEY_SIZE);
|
||||
rc = ssh_curve25519_build_k(session);
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Unsupported GSSAPI KEX method");
|
||||
goto error;
|
||||
}
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not derive shared secret");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Also imports next_crypto->server_pubkey
|
||||
* Can give error when using null hostkey */
|
||||
ssh_get_key_params(session, &privkey, &digest);
|
||||
|
||||
rc = ssh_make_sessionid(session);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session, SSH_FATAL, "Could not create a session id");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(crypto->kex_methods[SSH_HOSTKEYS], "null") != 0) {
|
||||
rc =
|
||||
ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bS",
|
||||
SSH2_MSG_KEXGSS_HOSTKEY,
|
||||
server_pubkey_blob);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
ssh_buffer_reinit(session->out_buffer);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_packet_send(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Sent SSH2_MSG_KEXGSS_HOSTKEY");
|
||||
SSH_STRING_FREE(server_pubkey_blob);
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_init(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = gethostname(hostname, 64);
|
||||
if (rc != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_import_name(session->gssapi, hostname);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
maj_stat = gss_acquire_cred(&min_stat,
|
||||
session->gssapi->client.server_name,
|
||||
0,
|
||||
GSS_C_NO_OID_SET,
|
||||
GSS_C_ACCEPT,
|
||||
&session->gssapi->server_creds,
|
||||
NULL,
|
||||
NULL);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
ssh_gssapi_log_error(SSH_LOG_TRACE,
|
||||
"acquiring credentials",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
goto error;
|
||||
}
|
||||
|
||||
maj_stat = gss_accept_sec_context(&min_stat,
|
||||
&session->gssapi->ctx,
|
||||
session->gssapi->server_creds,
|
||||
&input_token,
|
||||
GSS_C_NO_CHANNEL_BINDINGS,
|
||||
&client_name,
|
||||
NULL /*mech_oid*/,
|
||||
&output_token,
|
||||
&ret_flags,
|
||||
NULL /*time*/,
|
||||
&session->gssapi->client_creds);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"accepting token failed",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
goto error;
|
||||
}
|
||||
SSH_STRING_FREE(otoken);
|
||||
gss_release_name(&min_stat, &client_name);
|
||||
if (!(ret_flags & GSS_C_INTEG_FLAG) || !(ret_flags & GSS_C_MUTUAL_FLAG)) {
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"GSSAPI(accept) integrity and mutual flags were not set");
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "token accepted");
|
||||
|
||||
msg.length = session->next_crypto->digest_len;
|
||||
msg.value = session->next_crypto->secret_hash;
|
||||
maj_stat = gss_get_mic(&min_stat,
|
||||
session->gssapi->ctx,
|
||||
GSS_C_QOP_DEFAULT,
|
||||
&msg,
|
||||
&mic);
|
||||
if (GSS_ERROR(maj_stat)) {
|
||||
ssh_gssapi_log_error(SSH_LOG_DEBUG,
|
||||
"creating mic failed",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bSdPbdP",
|
||||
SSH2_MSG_KEXGSS_COMPLETE,
|
||||
server_pubkey,
|
||||
mic.length,
|
||||
(size_t)mic.length,
|
||||
mic.value,
|
||||
1,
|
||||
output_token.length,
|
||||
(size_t)output_token.length,
|
||||
output_token.value);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
ssh_buffer_reinit(session->out_buffer);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
gss_release_buffer(&min_stat, &mic);
|
||||
|
||||
rc = ssh_packet_send(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Sent SSH2_MSG_KEXGSS_COMPLETE");
|
||||
|
||||
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
|
||||
/* Send the MSG_NEWKEYS */
|
||||
rc = ssh_packet_send_newkeys(session);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ssh_string_free(server_pubkey);
|
||||
ssh_string_free(client_pubkey);
|
||||
return SSH_OK;
|
||||
error:
|
||||
SSH_STRING_FREE(server_pubkey_blob);
|
||||
ssh_string_free(server_pubkey);
|
||||
ssh_string_free(client_pubkey);
|
||||
session->session_state = SSH_SESSION_STATE_ERROR;
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief parse an incoming SSH_MSG_KEXGSS_INIT packet and complete
|
||||
* Diffie-Hellman key exchange
|
||||
**/
|
||||
static SSH_PACKET_CALLBACK(ssh_packet_server_gss_kex_init)
|
||||
{
|
||||
(void)type;
|
||||
(void)user;
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_KEXGSS_INIT");
|
||||
ssh_packet_remove_callbacks(session, &ssh_gss_kex_server_callbacks);
|
||||
ssh_server_gss_kex_process_init(session, packet);
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
136
src/kex.c
136
src/kex.c
@@ -41,14 +41,14 @@
|
||||
#include "libssh/string.h"
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/sntrup761.h"
|
||||
#ifdef HAVE_MLKEM
|
||||
#include "libssh/hybrid_mlkem.h"
|
||||
#endif
|
||||
#include "libssh/kex-gss.h"
|
||||
#include "libssh/knownhosts.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/token.h"
|
||||
#include "libssh/gssapi.h"
|
||||
|
||||
#ifdef HAVE_BLOWFISH
|
||||
# define BLOWFISH ",blowfish-cbc"
|
||||
@@ -105,13 +105,14 @@
|
||||
#define SNTRUP761X25519 ""
|
||||
#endif /* HAVE_SNTRUP761 */
|
||||
|
||||
#ifdef HAVE_MLKEM
|
||||
#ifdef HAVE_MLKEM1024
|
||||
#define HYBRID_MLKEM "mlkem768x25519-sha256," \
|
||||
"mlkem768nistp256-sha256," \
|
||||
"mlkem1024nistp384-sha384,"
|
||||
#else
|
||||
#define HYBRID_MLKEM ""
|
||||
#endif /* HAVE_MLKEM */
|
||||
#define HYBRID_MLKEM "mlkem768x25519-sha256," \
|
||||
"mlkem768nistp256-sha256,"
|
||||
#endif /* HAVE_MLKEM1024 */
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
|
||||
@@ -794,6 +795,8 @@ int ssh_set_client_kex(ssh_session session)
|
||||
const char *wanted = NULL;
|
||||
int ok;
|
||||
int i;
|
||||
bool gssapi_null_alg = false;
|
||||
char *hostkeys = NULL;
|
||||
|
||||
/* Skip if already set, for example for the rekey or when we do the guessing
|
||||
* it could have been already used to make some protocol decisions. */
|
||||
@@ -806,6 +809,42 @@ int ssh_set_client_kex(ssh_session session)
|
||||
ssh_set_error(session, SSH_FATAL, "PRNG error");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
#ifdef WITH_GSSAPI
|
||||
if (session->opts.gssapi_key_exchange) {
|
||||
char *gssapi_algs = NULL;
|
||||
|
||||
ok = ssh_gssapi_init(session);
|
||||
if (ok != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ok = ssh_gssapi_import_name(session->gssapi, session->opts.host);
|
||||
if (ok != SSH_OK) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
gssapi_algs = ssh_gssapi_kex_mechs(session);
|
||||
if (gssapi_algs == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Prefix the default algorithms with gsskex algs */
|
||||
if (ssh_fips_mode()) {
|
||||
session->opts.wanted_methods[SSH_KEX] =
|
||||
ssh_prefix_without_duplicates(fips_methods[SSH_KEX],
|
||||
gssapi_algs);
|
||||
} else {
|
||||
session->opts.wanted_methods[SSH_KEX] =
|
||||
ssh_prefix_without_duplicates(default_methods[SSH_KEX],
|
||||
gssapi_algs);
|
||||
}
|
||||
|
||||
gssapi_null_alg = true;
|
||||
|
||||
SAFE_FREE(gssapi_algs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the list of allowed algorithms in order of preference, if it hadn't
|
||||
* been set yet. */
|
||||
@@ -819,6 +858,16 @@ int ssh_set_client_kex(ssh_session session)
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (gssapi_null_alg) {
|
||||
hostkeys =
|
||||
ssh_append_without_duplicates(client->methods[i], "null");
|
||||
if (hostkeys == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SAFE_FREE(client->methods[i]);
|
||||
client->methods[i] = hostkeys;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -910,6 +959,14 @@ kex_select_kex_type(const char *kex)
|
||||
{
|
||||
if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) {
|
||||
return SSH_KEX_DH_GROUP1_SHA1;
|
||||
} else if (strncmp(kex, "gss-group14-sha256-", 19) == 0) {
|
||||
return SSH_GSS_KEX_DH_GROUP14_SHA256;
|
||||
} else if (strncmp(kex, "gss-group16-sha512-", 19) == 0) {
|
||||
return SSH_GSS_KEX_DH_GROUP16_SHA512;
|
||||
} else if (strncmp(kex, "gss-nistp256-sha256-", 20) == 0) {
|
||||
return SSH_GSS_KEX_ECDH_NISTP256_SHA256;
|
||||
} else if (strncmp(kex, "gss-curve25519-sha256-", 22) == 0) {
|
||||
return SSH_GSS_KEX_CURVE25519_SHA256;
|
||||
} else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) {
|
||||
return SSH_KEX_DH_GROUP14_SHA1;
|
||||
} else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) {
|
||||
@@ -938,11 +995,11 @@ kex_select_kex_type(const char *kex)
|
||||
return SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM;
|
||||
} else if (strcmp(kex, "sntrup761x25519-sha512") == 0) {
|
||||
return SSH_KEX_SNTRUP761X25519_SHA512;
|
||||
#ifdef HAVE_MLKEM
|
||||
} else if (strcmp(kex, "mlkem768x25519-sha256") == 0) {
|
||||
return SSH_KEX_MLKEM768X25519_SHA256;
|
||||
} else if (strcmp(kex, "mlkem768nistp256-sha256") == 0) {
|
||||
return SSH_KEX_MLKEM768NISTP256_SHA256;
|
||||
#ifdef HAVE_MLKEM1024
|
||||
} else if (strcmp(kex, "mlkem1024nistp384-sha384") == 0) {
|
||||
return SSH_KEX_MLKEM1024NISTP384_SHA384;
|
||||
#endif
|
||||
@@ -967,6 +1024,14 @@ static void revert_kex_callbacks(ssh_session session)
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
ssh_client_dh_remove_callbacks(session);
|
||||
break;
|
||||
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:
|
||||
#ifdef WITH_GSSAPI
|
||||
ssh_client_gss_kex_remove_callbacks(session);
|
||||
#endif /* WITH_GSSAPI */
|
||||
break;
|
||||
#ifdef WITH_GEX
|
||||
case SSH_KEX_DH_GEX_SHA1:
|
||||
case SSH_KEX_DH_GEX_SHA256:
|
||||
@@ -992,13 +1057,13 @@ static void revert_kex_callbacks(ssh_session session)
|
||||
ssh_client_sntrup761x25519_remove_callbacks(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
ssh_client_hybrid_mlkem_remove_callbacks(session);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1436,6 +1501,18 @@ int ssh_make_sessionid(ssh_session session)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (server_pubkey_blob == NULL) {
|
||||
if ((session->server && ssh_kex_is_gss(session->next_crypto)) ||
|
||||
session->opts.gssapi_key_exchange) {
|
||||
server_pubkey_blob = ssh_string_new(0);
|
||||
if (server_pubkey_blob == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
rc = SSH_ERROR;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPdPS",
|
||||
ssh_buffer_get_len(client_hash),
|
||||
@@ -1457,7 +1534,9 @@ int ssh_make_sessionid(ssh_session session)
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx,
|
||||
DH_CLIENT_KEYPAIR, NULL, &client_pubkey);
|
||||
@@ -1523,6 +1602,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
if (session->next_crypto->ecdh_client_pubkey == NULL ||
|
||||
session->next_crypto->ecdh_server_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE, "ECDH parameter missing");
|
||||
@@ -1541,6 +1621,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
#ifdef HAVE_CURVE25519
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPdP",
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
@@ -1582,10 +1663,11 @@ int ssh_make_sessionid(ssh_session session)
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_SNTRUP761 */
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"SS",
|
||||
session->next_crypto->hybrid_client_init,
|
||||
@@ -1597,7 +1679,6 @@ int ssh_make_sessionid(ssh_session session)
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_MLKEM */
|
||||
default:
|
||||
/* Handle unsupported kex types - this should not happen in normal operation */
|
||||
rc = SSH_ERROR;
|
||||
@@ -1612,13 +1693,13 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->shared_secret,
|
||||
SHA512_DIGEST_LEN);
|
||||
break;
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
rc = ssh_buffer_pack(buf, "S", session->next_crypto->hybrid_shared_secret);
|
||||
break;
|
||||
#endif /* HAVE_MLKEM */
|
||||
default:
|
||||
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
|
||||
break;
|
||||
@@ -1651,13 +1732,14 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#endif
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
#ifdef WITH_GEX
|
||||
case SSH_KEX_DH_GEX_SHA256:
|
||||
#endif /* WITH_GEX */
|
||||
@@ -1672,7 +1754,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
#ifdef HAVE_MLKEM
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
session->next_crypto->digest_len = SHA384_DIGEST_LENGTH;
|
||||
@@ -1686,6 +1768,7 @@ int ssh_make_sessionid(ssh_session session)
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
@@ -1839,13 +1922,13 @@ int ssh_generate_session_keys(ssh_session session)
|
||||
k_string = ssh_make_padded_bignum_string(crypto->shared_secret,
|
||||
crypto->digest_len);
|
||||
break;
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
k_string = ssh_string_copy(crypto->hybrid_shared_secret);
|
||||
break;
|
||||
#endif /* HAVE_MLKEM */
|
||||
default:
|
||||
k_string = ssh_make_bignum_string(crypto->shared_secret);
|
||||
break;
|
||||
@@ -1961,3 +2044,22 @@ error:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check if a given crypto context has a GSSAPI KEX set
|
||||
*
|
||||
* @param[in] crypto The SSH crypto context
|
||||
* @return true if the KEX of the context is a GSSAPI KEX, false otherwise
|
||||
*/
|
||||
bool ssh_kex_is_gss(struct ssh_crypto_struct *crypto)
|
||||
{
|
||||
switch (crypto->kex_type) {
|
||||
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:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,3 +322,52 @@ md5_final(unsigned char *md, MD5CTX c)
|
||||
}
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ brief One-shot MD5. Not intended for use in security-relevant contexts.
|
||||
*/
|
||||
int
|
||||
md5(const unsigned char *digest, size_t len, unsigned char *hash)
|
||||
{
|
||||
int rc, ret = SSH_ERROR;
|
||||
unsigned int mdlen = 0;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
EVP_MD *md5 = NULL;
|
||||
#endif
|
||||
MD5CTX c = EVP_MD_CTX_new();
|
||||
if (c == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
md5 = EVP_MD_fetch(NULL, "MD5", "provider=default,-fips");
|
||||
if (md5 == NULL) {
|
||||
goto out;
|
||||
}
|
||||
rc = EVP_DigestInit(c, md5);
|
||||
#else
|
||||
rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
|
||||
#endif
|
||||
if (rc == 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_DigestUpdate(c, digest, len);
|
||||
if (rc != 1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EVP_DigestFinal(c, hash, &mdlen);
|
||||
if (rc != 1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
EVP_MD_CTX_free(c);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
EVP_MD_free(md5);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -244,3 +244,9 @@ md5_final(unsigned char *md, MD5CTX c)
|
||||
gcry_md_close(c);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int md5(const unsigned char *digest, size_t len, unsigned char *hash)
|
||||
{
|
||||
gcry_md_hash_buffer(GCRY_MD_MD5, hash, digest, len);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
@@ -405,3 +405,51 @@ md5_final(unsigned char *md, MD5CTX c)
|
||||
}
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
int md5(const unsigned char *digest, size_t len, unsigned char *hash)
|
||||
{
|
||||
MD5CTX ctx = NULL;
|
||||
int rc;
|
||||
const mbedtls_md_info_t *md_info =
|
||||
mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
|
||||
if (md_info == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ctx = malloc(sizeof(mbedtls_md_context_t));
|
||||
if (ctx == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
mbedtls_md_init(ctx);
|
||||
|
||||
rc = mbedtls_md_setup(ctx, md_info, 0);
|
||||
if (rc != 0) {
|
||||
mbedtls_md_free(ctx);
|
||||
SAFE_FREE(ctx);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = mbedtls_md_starts(ctx);
|
||||
if (rc != 0) {
|
||||
mbedtls_md_free(ctx);
|
||||
SAFE_FREE(ctx);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = mbedtls_md_update(ctx, digest, len);
|
||||
if (rc != 0) {
|
||||
mbedtls_md_free(ctx);
|
||||
SAFE_FREE(ctx);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = mbedtls_md_finish(ctx, hash);
|
||||
mbedtls_md_free(ctx);
|
||||
SAFE_FREE(ctx);
|
||||
if (rc != 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
@@ -184,6 +184,19 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
|
||||
ssh_message_reply_default(msg);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
} else if (msg->auth_request.method == SSH_AUTH_METHOD_INTERACTIVE &&
|
||||
ssh_callbacks_exists(session->server_callbacks, auth_kbdint_function)) {
|
||||
rc = session->server_callbacks->auth_kbdint_function(msg,
|
||||
session,
|
||||
session->server_callbacks->userdata);
|
||||
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) {
|
||||
ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL);
|
||||
} else if (rc == SSH_AUTH_INFO) {
|
||||
return SSH_OK;
|
||||
} else {
|
||||
ssh_message_reply_default(msg);
|
||||
}
|
||||
return SSH_OK;
|
||||
}
|
||||
break;
|
||||
@@ -1145,6 +1158,75 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request)
|
||||
SAFE_FREE(method);
|
||||
SSH_MESSAGE_FREE(msg);
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
if (strcmp(method, "gssapi-keyex") == 0) {
|
||||
gss_buffer_desc received_mic = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
|
||||
ssh_string mic_token_string = NULL;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
ssh_buffer buf = NULL;
|
||||
|
||||
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.");
|
||||
ssh_auth_reply_default(session, 0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (session->gssapi == NULL || session->gssapi->ctx == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
|
||||
ssh_auth_reply_default(session, 0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "S", &mic_token_string);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_auth_reply_default(session, 0);
|
||||
goto error;
|
||||
}
|
||||
received_mic.length = ssh_string_len(mic_token_string);
|
||||
received_mic.value = ssh_string_data(mic_token_string);
|
||||
|
||||
SAFE_FREE(session->gssapi->user);
|
||||
session->gssapi->user = strdup(msg->auth_request.username);
|
||||
buf = ssh_gssapi_build_mic(session, "gssapi-keyex");
|
||||
if (buf == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
SSH_STRING_FREE(mic_token_string);
|
||||
ssh_auth_reply_default(session, 0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
mic_buf.length = ssh_buffer_get_len(buf);
|
||||
mic_buf.value = ssh_buffer_get(buf);
|
||||
|
||||
maj_stat = gss_verify_mic(&min_stat,
|
||||
session->gssapi->ctx,
|
||||
&mic_buf,
|
||||
&received_mic,
|
||||
NULL);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to verify MIC for gssapi-keyex auth");
|
||||
SSH_BUFFER_FREE(buf);
|
||||
SSH_STRING_FREE(mic_token_string);
|
||||
ssh_auth_reply_default(session, 0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ssh_auth_reply_success(session, 0);
|
||||
|
||||
/* bypass the message queue thing */
|
||||
SAFE_FREE(service);
|
||||
SAFE_FREE(method);
|
||||
SSH_BUFFER_FREE(buf);
|
||||
SSH_MESSAGE_FREE(msg);
|
||||
SSH_STRING_FREE(mic_token_string);
|
||||
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -30,8 +30,10 @@ const struct mlkem_type_info *kex_type_to_mlkem_info(enum ssh_key_exchange_e kex
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
return &MLKEM768_INFO;
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
return &MLKEM1024_INFO;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
207
src/mlkem_gcrypt.c
Normal file
207
src/mlkem_gcrypt.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/mlkem.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
const struct mlkem_type_info MLKEM768_INFO = {
|
||||
.pubkey_size = GCRY_KEM_MLKEM768_PUBKEY_LEN,
|
||||
.privkey_size = GCRY_KEM_MLKEM768_SECKEY_LEN,
|
||||
.ciphertext_size = GCRY_KEM_MLKEM768_CIPHER_LEN,
|
||||
.alg = GCRY_KEM_MLKEM768,
|
||||
};
|
||||
|
||||
const struct mlkem_type_info MLKEM1024_INFO = {
|
||||
.pubkey_size = GCRY_KEM_MLKEM1024_PUBKEY_LEN,
|
||||
.privkey_size = GCRY_KEM_MLKEM1024_SECKEY_LEN,
|
||||
.ciphertext_size = GCRY_KEM_MLKEM1024_CIPHER_LEN,
|
||||
.alg = GCRY_KEM_MLKEM1024,
|
||||
};
|
||||
|
||||
int ssh_mlkem_init(ssh_session session)
|
||||
{
|
||||
int ret = SSH_ERROR;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
const struct mlkem_type_info *mlkem_info = NULL;
|
||||
ssh_string pubkey = NULL;
|
||||
unsigned char *privkey = NULL, *pubkey_data = NULL;
|
||||
gcry_error_t err;
|
||||
|
||||
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
|
||||
if (mlkem_info == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
privkey = malloc(mlkem_info->privkey_size);
|
||||
if (privkey == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pubkey = ssh_string_new(mlkem_info->pubkey_size);
|
||||
if (pubkey == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pubkey_data = ssh_string_data(pubkey);
|
||||
err = gcry_kem_keypair(mlkem_info->alg,
|
||||
pubkey_data,
|
||||
mlkem_info->pubkey_size,
|
||||
privkey,
|
||||
mlkem_info->privkey_size);
|
||||
if (err) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to generate ML-KEM key: %s",
|
||||
gpg_strerror(err));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ssh_string_free(crypto->mlkem_client_pubkey);
|
||||
crypto->mlkem_client_pubkey = pubkey;
|
||||
pubkey = NULL;
|
||||
|
||||
free(crypto->mlkem_privkey);
|
||||
crypto->mlkem_privkey = privkey;
|
||||
crypto->mlkem_privkey_len = mlkem_info->privkey_size;
|
||||
privkey = NULL;
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
ssh_string_free(pubkey);
|
||||
if (privkey != NULL) {
|
||||
ssh_burn(privkey, mlkem_info->privkey_size);
|
||||
free(privkey);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssh_mlkem_encapsulate(ssh_session session,
|
||||
ssh_mlkem_shared_secret shared_secret)
|
||||
{
|
||||
int ret = SSH_ERROR;
|
||||
const struct mlkem_type_info *mlkem_info = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
const unsigned char *pubkey_data = NULL;
|
||||
unsigned char *ciphertext_data = NULL;
|
||||
ssh_string ciphertext = NULL;
|
||||
ssh_string pubkey = crypto->mlkem_client_pubkey;
|
||||
gcry_error_t err;
|
||||
|
||||
if (pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing pubkey in session");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
|
||||
if (mlkem_info == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ciphertext = ssh_string_new(mlkem_info->ciphertext_size);
|
||||
if (ciphertext == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pubkey_data = ssh_string_data(pubkey);
|
||||
ciphertext_data = ssh_string_data(ciphertext);
|
||||
err = gcry_kem_encap(mlkem_info->alg,
|
||||
pubkey_data,
|
||||
mlkem_info->pubkey_size,
|
||||
ciphertext_data,
|
||||
mlkem_info->ciphertext_size,
|
||||
shared_secret,
|
||||
MLKEM_SHARED_SECRET_SIZE,
|
||||
NULL,
|
||||
0);
|
||||
if (err) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to encapsulate ML-KEM shared secret: %s",
|
||||
gpg_strerror(err));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ssh_string_free(crypto->mlkem_ciphertext);
|
||||
crypto->mlkem_ciphertext = ciphertext;
|
||||
ciphertext = NULL;
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
ssh_string_free(ciphertext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssh_mlkem_decapsulate(const ssh_session session,
|
||||
ssh_mlkem_shared_secret shared_secret)
|
||||
{
|
||||
const struct mlkem_type_info *mlkem_info = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_string ciphertext = NULL;
|
||||
unsigned char *ciphertext_data = NULL;
|
||||
gcry_error_t err;
|
||||
|
||||
ciphertext = crypto->mlkem_ciphertext;
|
||||
if (ciphertext == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing ciphertext in session");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (crypto->mlkem_privkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing ML-KEM private key in session");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
|
||||
if (mlkem_info == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ciphertext_data = ssh_string_data(ciphertext);
|
||||
err = gcry_kem_decap(mlkem_info->alg,
|
||||
crypto->mlkem_privkey,
|
||||
mlkem_info->privkey_size,
|
||||
ciphertext_data,
|
||||
mlkem_info->ciphertext_size,
|
||||
shared_secret,
|
||||
MLKEM_SHARED_SECRET_SIZE,
|
||||
NULL,
|
||||
0);
|
||||
if (err) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to decapsulate ML-KEM shared secret: %s",
|
||||
gpg_strerror(err));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
202
src/mlkem_native.c
Normal file
202
src/mlkem_native.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/mlkem.h"
|
||||
#include "libssh/mlkem_native.h"
|
||||
#include "libssh/session.h"
|
||||
|
||||
#define crypto_kem_mlkem768_PUBLICKEYBYTES 1184
|
||||
#define crypto_kem_mlkem768_SECRETKEYBYTES 2400
|
||||
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088
|
||||
|
||||
const struct mlkem_type_info MLKEM768_INFO = {
|
||||
.pubkey_size = crypto_kem_mlkem768_PUBLICKEYBYTES,
|
||||
.privkey_size = crypto_kem_mlkem768_SECRETKEYBYTES,
|
||||
.ciphertext_size = crypto_kem_mlkem768_CIPHERTEXTBYTES,
|
||||
};
|
||||
|
||||
int ssh_mlkem_init(ssh_session session)
|
||||
{
|
||||
int ret = SSH_ERROR;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
const struct mlkem_type_info *mlkem_info = NULL;
|
||||
unsigned char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN];
|
||||
struct libcrux_mlkem768_keypair keypair;
|
||||
int err;
|
||||
|
||||
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
|
||||
if (mlkem_info == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = ssh_get_random(rnd, sizeof(rnd), 0);
|
||||
if (err != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to generate random data for ML-KEM keygen");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
|
||||
|
||||
if (ssh_string_len(crypto->mlkem_client_pubkey) < mlkem_info->pubkey_size) {
|
||||
SSH_STRING_FREE(crypto->mlkem_client_pubkey);
|
||||
}
|
||||
if (crypto->mlkem_client_pubkey == NULL) {
|
||||
crypto->mlkem_client_pubkey = ssh_string_new(mlkem_info->pubkey_size);
|
||||
if (crypto->mlkem_client_pubkey == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
err = ssh_string_fill(crypto->mlkem_client_pubkey,
|
||||
keypair.pk.value,
|
||||
mlkem_info->pubkey_size);
|
||||
if (err) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to fill the string with client pubkey");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (crypto->mlkem_privkey == NULL) {
|
||||
crypto->mlkem_privkey = malloc(mlkem_info->privkey_size);
|
||||
if (crypto->mlkem_privkey == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
memcpy(crypto->mlkem_privkey, keypair.sk.value, mlkem_info->privkey_size);
|
||||
crypto->mlkem_privkey_len = mlkem_info->privkey_size;
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
ssh_burn(&keypair, sizeof(keypair));
|
||||
ssh_burn(rnd, sizeof(rnd));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssh_mlkem_encapsulate(ssh_session session,
|
||||
ssh_mlkem_shared_secret shared_secret)
|
||||
{
|
||||
int ret = SSH_ERROR;
|
||||
const struct mlkem_type_info *mlkem_info = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
const unsigned char *pubkey_data = NULL;
|
||||
ssh_string pubkey = crypto->mlkem_client_pubkey;
|
||||
struct libcrux_mlkem768_enc_result enc;
|
||||
struct libcrux_mlkem768_pk mlkem_pub = {0};
|
||||
unsigned char rnd[LIBCRUX_ML_KEM_ENC_PRNG_LEN];
|
||||
int err;
|
||||
|
||||
if (pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing pubkey in session");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
|
||||
if (mlkem_info == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pubkey_data = ssh_string_data(pubkey);
|
||||
memcpy(mlkem_pub.value, pubkey_data, mlkem_info->pubkey_size);
|
||||
err = libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub);
|
||||
if (err == 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Invalid public key");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
err = ssh_get_random(rnd, sizeof(rnd), 0);
|
||||
if (err != 1) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to generate random data for ML-KEM keygen");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub, rnd);
|
||||
|
||||
if (ssh_string_len(crypto->mlkem_ciphertext) < mlkem_info->ciphertext_size) {
|
||||
SSH_STRING_FREE(crypto->mlkem_ciphertext);
|
||||
}
|
||||
if (crypto->mlkem_ciphertext == NULL) {
|
||||
crypto->mlkem_ciphertext = ssh_string_new(mlkem_info->ciphertext_size);
|
||||
if (crypto->mlkem_ciphertext == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
err = ssh_string_fill(crypto->mlkem_ciphertext,
|
||||
enc.fst.value,
|
||||
sizeof(enc.fst.value));
|
||||
if (err != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to fill the string with ciphertext");
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(shared_secret, enc.snd, sizeof(enc.snd));
|
||||
|
||||
ret = SSH_OK;
|
||||
|
||||
cleanup:
|
||||
ssh_burn(rnd, sizeof(rnd));
|
||||
ssh_burn(&enc, sizeof(enc));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssh_mlkem_decapsulate(const ssh_session session,
|
||||
ssh_mlkem_shared_secret shared_secret)
|
||||
{
|
||||
const struct mlkem_type_info *mlkem_info = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
ssh_string ciphertext = NULL;
|
||||
unsigned char *ciphertext_data = NULL;
|
||||
struct libcrux_mlkem768_sk mlkem_priv = {0};
|
||||
struct libcrux_mlkem768_ciphertext mlkem_ciphertext = {0};
|
||||
|
||||
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
|
||||
if (mlkem_info == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ciphertext = crypto->mlkem_ciphertext;
|
||||
if (ciphertext == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing ciphertext in session");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ciphertext_data = ssh_string_data(ciphertext);
|
||||
memcpy(mlkem_ciphertext.value,
|
||||
ciphertext_data,
|
||||
sizeof(mlkem_ciphertext.value));
|
||||
|
||||
memcpy(mlkem_priv.value, crypto->mlkem_privkey, crypto->mlkem_privkey_len);
|
||||
|
||||
libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv,
|
||||
&mlkem_ciphertext,
|
||||
shared_secret);
|
||||
return SSH_OK;
|
||||
}
|
||||
119
src/options.c
119
src/options.c
@@ -41,6 +41,11 @@
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include <sys/types.h>
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/config_parser.h"
|
||||
#include "libssh/gssapi.h"
|
||||
#include "libssh/token.h"
|
||||
#ifdef WITH_SERVER
|
||||
#include "libssh/server.h"
|
||||
#include "libssh/bind.h"
|
||||
@@ -256,6 +261,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
new->opts.nodelay = src->opts.nodelay;
|
||||
new->opts.config_processed = src->opts.config_processed;
|
||||
new->opts.control_master = src->opts.control_master;
|
||||
new->opts.address_family = src->opts.address_family;
|
||||
new->common.log_verbosity = src->common.log_verbosity;
|
||||
new->common.callbacks = src->common.callbacks;
|
||||
|
||||
@@ -559,6 +565,16 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* Set it to specify that GSSAPI should delegate credentials
|
||||
* to the server (int, 0 = false).
|
||||
*
|
||||
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE
|
||||
* Set to true to allow GSSAPI key exchange (bool).
|
||||
*
|
||||
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
|
||||
* Set the GSSAPI key exchange method to be used (const char *,
|
||||
* comma-separated list). ex:
|
||||
* "gss-curve25519-sha256-,gss-nistp256-sha256-"
|
||||
* These will prefix the default algorithms if
|
||||
* SSH_OPTIONS_GSSAPI_KEY_EXCHANGE is true.
|
||||
*
|
||||
* - SSH_OPTIONS_PASSWORD_AUTH
|
||||
* Set it if password authentication should be used
|
||||
* in ssh_userauth_auto_pubkey(). (int, 0=false).
|
||||
@@ -611,12 +627,12 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* Setting 0 will revert the value to defaults.
|
||||
* Default is 3072 bits or 2048 bits in FIPS mode.
|
||||
* (int)
|
||||
|
||||
*
|
||||
* - SSH_OPTIONS_IDENTITY_AGENT
|
||||
* Set the path to the SSH agent socket. If unset, the
|
||||
* SSH_AUTH_SOCK environment is consulted.
|
||||
* (const char *)
|
||||
|
||||
*
|
||||
* - SSH_OPTIONS_IDENTITIES_ONLY
|
||||
* Use only keys specified in the SSH config, even if agent
|
||||
* offers more.
|
||||
@@ -650,6 +666,13 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* context and can free it after this call.
|
||||
* (ssh_pki_ctx)
|
||||
*
|
||||
* - SSH_OPTIONS_ADDRESS_FAMILY
|
||||
* Specify which address family to use when connecting.
|
||||
*
|
||||
* Possible options:
|
||||
* - SSH_ADDRESS_FAMILY_ANY: use any address family
|
||||
* - SSH_ADDRESS_FAMILY_INET: IPv4 only
|
||||
* - SSH_ADDRESS_FAMILY_INET6: IPv6 only
|
||||
*
|
||||
* @param value The value to set. This is a generic pointer and the
|
||||
* datatype which is used should be set according to the
|
||||
@@ -1232,6 +1255,37 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
session->opts.gss_delegate_creds = (x & 0xff);
|
||||
}
|
||||
break;
|
||||
#ifdef WITH_GSSAPI
|
||||
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE:
|
||||
if (value == NULL) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
} else {
|
||||
bool *x = (bool *)value;
|
||||
session->opts.gssapi_key_exchange = *x;
|
||||
}
|
||||
break;
|
||||
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
|
||||
v = value;
|
||||
if (v == NULL || v[0] == '\0') {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
} else {
|
||||
/* Check if algorithms are supported */
|
||||
char *ret =
|
||||
ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, v);
|
||||
if (ret == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"GSSAPI key exchange algorithms not "
|
||||
"supported or invalid");
|
||||
return -1;
|
||||
}
|
||||
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
|
||||
session->opts.gssapi_key_exchange_algs = ret;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case SSH_OPTIONS_PASSWORD_AUTH:
|
||||
case SSH_OPTIONS_PUBKEY_AUTH:
|
||||
case SSH_OPTIONS_KBDINT_AUTH:
|
||||
@@ -1393,6 +1447,20 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SSH_OPTIONS_ADDRESS_FAMILY:
|
||||
if (value == NULL) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
} else {
|
||||
int *x = (int *)value;
|
||||
if (*x < SSH_ADDRESS_FAMILY_ANY ||
|
||||
*x > SSH_ADDRESS_FAMILY_INET6) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
session->opts.address_family = *x;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
|
||||
return -1;
|
||||
@@ -2073,6 +2141,16 @@ int ssh_options_apply(ssh_session session)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_GSSAPI
|
||||
if (session->opts.gssapi_key_exchange) {
|
||||
rc = ssh_gssapi_check_client_config(session);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARN, "Disabled GSSAPI key exchange");
|
||||
session->opts.gssapi_key_exchange = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2255,6 +2333,14 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
|
||||
* Default is 3072 bits or 2048 bits in FIPS mode.
|
||||
* (int)
|
||||
*
|
||||
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE
|
||||
* Set true to enable GSSAPI key exchange,
|
||||
* false to disable GSSAPI key exchange. (bool)
|
||||
*
|
||||
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
|
||||
* Set the GSSAPI key exchange method to be used
|
||||
* (const char *, comma-separated list).
|
||||
* ex: "gss-group14-sha256-,gss-group16-sha512-"
|
||||
*
|
||||
* @param value The value to set. This is a generic pointer and the
|
||||
* datatype which should be used is described at the
|
||||
@@ -2652,6 +2738,35 @@ ssh_bind_options_set(ssh_bind sshbind,
|
||||
sshbind->rsa_min_size = *x;
|
||||
}
|
||||
break;
|
||||
#ifdef WITH_GSSAPI
|
||||
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE:
|
||||
if (value == NULL) {
|
||||
ssh_set_error_invalid(sshbind);
|
||||
return -1;
|
||||
} else {
|
||||
bool *x = (bool *)value;
|
||||
sshbind->gssapi_key_exchange = *x;
|
||||
}
|
||||
break;
|
||||
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
|
||||
if (value == NULL) {
|
||||
ssh_set_error_invalid(sshbind);
|
||||
return -1;
|
||||
} else {
|
||||
char *ret = NULL;
|
||||
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
|
||||
ret = ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, value);
|
||||
if (ret == NULL) {
|
||||
ssh_set_error(
|
||||
sshbind,
|
||||
SSH_REQUEST_DENIED,
|
||||
"GSSAPI key exchange algorithms not supported or invalid");
|
||||
return -1;
|
||||
}
|
||||
sshbind->gssapi_key_exchange_algs = ret;
|
||||
}
|
||||
break;
|
||||
#endif /* WITH_GSSAPI */
|
||||
default:
|
||||
ssh_set_error(sshbind,
|
||||
SSH_REQUEST_DENIED,
|
||||
|
||||
167
src/packet.c
167
src/packet.c
@@ -422,35 +422,67 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
case SSH2_MSG_KEX_DH_GEX_INIT: // 32
|
||||
/* Server only */
|
||||
// SSH2_MSG_KEXGSS_COMPLETE: // 32
|
||||
if (ssh_kex_is_gss(session->next_crypto)) {
|
||||
/* SSH2_MSG_KEXGSS_COMPLETE */
|
||||
/* Client only */
|
||||
|
||||
/*
|
||||
* States required:
|
||||
* - session_state == SSH_SESSION_STATE_DH
|
||||
* - dh_handshake_state == DH_STATE_GROUP_SENT
|
||||
*
|
||||
* Transitions:
|
||||
* - session->dh_handshake_state = DH_STATE_GROUP_SENT
|
||||
* then calls ssh_packet_server_dhgex_init which triggers:
|
||||
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
|
||||
* */
|
||||
/*
|
||||
* States required:
|
||||
* - session_state == SSH_SESSION_STATE_DH
|
||||
* - dh_handshake_state == DH_STATE_INIT_SENT
|
||||
*
|
||||
* Transitions:
|
||||
* - session->dh_handshake_state = DH_STATE_INIT_SENT
|
||||
* then calls ssh_packet_client_gss_kex_reply which triggers:
|
||||
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
|
||||
* */
|
||||
|
||||
if (session->client) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
if (!session->client) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (session->session_state != SSH_SESSION_STATE_DH) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (session->dh_handshake_state != DH_STATE_INIT_SENT) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* SSH2_MSG_KEX_DH_GEX_INIT */
|
||||
/* Server only */
|
||||
|
||||
/*
|
||||
* States required:
|
||||
* - session_state == SSH_SESSION_STATE_DH
|
||||
* - dh_handshake_state == DH_STATE_GROUP_SENT
|
||||
*
|
||||
* Transitions:
|
||||
* - session->dh_handshake_state = DH_STATE_GROUP_SENT
|
||||
* then calls ssh_packet_server_dhgex_init which triggers:
|
||||
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
|
||||
* */
|
||||
|
||||
if (session->client) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (session->session_state != SSH_SESSION_STATE_DH) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Only allowed if dh_handshake_state is in initial state */
|
||||
if (session->dh_handshake_state != DH_STATE_GROUP_SENT) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (session->session_state != SSH_SESSION_STATE_DH) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Only allowed if dh_handshake_state is in initial state */
|
||||
if (session->dh_handshake_state != DH_STATE_GROUP_SENT) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
case SSH2_MSG_KEX_DH_GEX_REPLY: // 33
|
||||
@@ -594,6 +626,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
|
||||
* or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
|
||||
* or session->auth.state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
|
||||
* or session->auth.state == SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT
|
||||
* or session->auth.state == SSH_AUTH_STATE_AUTH_NONE_SENT
|
||||
*
|
||||
* Transitions:
|
||||
@@ -623,8 +656,8 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
(session->auth.state != SSH_AUTH_STATE_PUBKEY_AUTH_SENT) &&
|
||||
(session->auth.state != SSH_AUTH_STATE_PASSWORD_AUTH_SENT) &&
|
||||
(session->auth.state != SSH_AUTH_STATE_GSSAPI_MIC_SENT) &&
|
||||
(session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT))
|
||||
{
|
||||
(session->auth.state != SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT) &&
|
||||
(session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT)) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
@@ -716,17 +749,83 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
case SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE: // 63
|
||||
/* TODO Not filtered */
|
||||
/* Server only */
|
||||
/*
|
||||
* States required:
|
||||
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
|
||||
* - session->gssapi->state == SSH_GSSAPI_STATE_RCV_MIC
|
||||
*
|
||||
* Transitions:
|
||||
* - None
|
||||
*/
|
||||
#ifdef WITH_GSSAPI
|
||||
if (session->client) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
if (session->gssapi == NULL) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
if (session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
#else
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
#endif /* WITH_GSSAPI */
|
||||
case SSH2_MSG_USERAUTH_GSSAPI_ERROR: // 64
|
||||
/* TODO Not filtered */
|
||||
/* Client only */
|
||||
/*
|
||||
* States required:
|
||||
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
|
||||
*
|
||||
* Transitions:
|
||||
* - None
|
||||
*/
|
||||
#ifdef WITH_GSSAPI
|
||||
if (session->server) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
#else
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
#endif /* WITH_GSSAPI */
|
||||
case SSH2_MSG_USERAUTH_GSSAPI_ERRTOK: // 65
|
||||
/* TODO Not filtered */
|
||||
/*
|
||||
* States required:
|
||||
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
|
||||
*
|
||||
* Transitions:
|
||||
* - None
|
||||
*/
|
||||
#ifdef WITH_GSSAPI
|
||||
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
#else
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
#endif /* WITH_GSSAPI */
|
||||
case SSH2_MSG_USERAUTH_GSSAPI_MIC: // 66
|
||||
/* Server only */
|
||||
|
||||
@@ -746,7 +845,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
* - any other case:
|
||||
* - None
|
||||
* */
|
||||
|
||||
#ifdef WITH_GSSAPI
|
||||
/* If this is a client, reject the message */
|
||||
if (session->client) {
|
||||
rc = SSH_PACKET_DENIED;
|
||||
@@ -765,6 +864,10 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
|
||||
|
||||
rc = SSH_PACKET_ALLOWED;
|
||||
break;
|
||||
#else
|
||||
rc = SSH_PACKET_DENIED;
|
||||
break;
|
||||
#endif /* WITH_GSSAPI */
|
||||
case SSH2_MSG_GLOBAL_REQUEST: // 80
|
||||
/*
|
||||
* States required:
|
||||
|
||||
133
src/packet_cb.c
133
src/packet_cb.c
@@ -27,6 +27,10 @@
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#ifdef WITH_GSSAPI
|
||||
#include "libssh/gssapi.h"
|
||||
#include <gssapi/gssapi.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/buffer.h"
|
||||
@@ -173,53 +177,93 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys)
|
||||
/* server things are done in server.c */
|
||||
session->dh_handshake_state=DH_STATE_FINISHED;
|
||||
} else {
|
||||
ssh_key server_key = NULL;
|
||||
#ifdef WITH_GSSAPI
|
||||
if (ssh_kex_is_gss(session->next_crypto)) {
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
/* client */
|
||||
|
||||
/* Verify the host's signature. FIXME do it sooner */
|
||||
sig_blob = session->next_crypto->dh_server_signature;
|
||||
session->next_crypto->dh_server_signature = NULL;
|
||||
|
||||
/* get the server public key */
|
||||
server_key = ssh_dh_get_next_server_publickey(session);
|
||||
if (server_key == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
|
||||
ssh_string_burn(sig_blob);
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Check if signature from server matches user preferences */
|
||||
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
|
||||
rc = match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
|
||||
sig->type_c);
|
||||
if (rc == 0) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Public key from server (%s) doesn't match user "
|
||||
"preference (%s)",
|
||||
sig->type_c,
|
||||
session->opts.wanted_methods[SSH_HOSTKEYS]);
|
||||
if (session->gssapi == NULL || session->gssapi->ctx == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_pki_signature_verify(session,
|
||||
sig,
|
||||
server_key,
|
||||
session->next_crypto->secret_hash,
|
||||
session->next_crypto->digest_len);
|
||||
SSH_SIGNATURE_FREE(sig);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to verify server hostkey signature");
|
||||
goto error;
|
||||
if (session->gssapi_key_exchange_mic == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"GSSAPI mic not set");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mic.length = ssh_string_len(session->gssapi_key_exchange_mic);
|
||||
mic.value = ssh_string_data(session->gssapi_key_exchange_mic);
|
||||
|
||||
msg.length = session->next_crypto->digest_len;
|
||||
msg.value = session->next_crypto->secret_hash;
|
||||
|
||||
maj_stat = gss_verify_mic(&min_stat,
|
||||
session->gssapi->ctx,
|
||||
&msg,
|
||||
&mic,
|
||||
NULL);
|
||||
if (maj_stat != GSS_S_COMPLETE) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to verify mic after GSSAPI Key Exchange");
|
||||
goto error;
|
||||
}
|
||||
SSH_STRING_FREE(session->gssapi_key_exchange_mic);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ssh_key server_key = NULL;
|
||||
|
||||
/* client */
|
||||
|
||||
/* Verify the host's signature. FIXME do it sooner */
|
||||
sig_blob = session->next_crypto->dh_server_signature;
|
||||
session->next_crypto->dh_server_signature = NULL;
|
||||
|
||||
/* get the server public key */
|
||||
server_key = ssh_dh_get_next_server_publickey(session);
|
||||
if (server_key == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
|
||||
ssh_string_burn(sig_blob);
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Check if signature from server matches user preferences */
|
||||
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
|
||||
rc = match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
|
||||
sig->type_c);
|
||||
if (rc == 0) {
|
||||
ssh_set_error(
|
||||
session,
|
||||
SSH_FATAL,
|
||||
"Public key from server (%s) doesn't match user "
|
||||
"preference (%s)",
|
||||
sig->type_c,
|
||||
session->opts.wanted_methods[SSH_HOSTKEYS]);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_pki_signature_verify(session,
|
||||
sig,
|
||||
server_key,
|
||||
session->next_crypto->secret_hash,
|
||||
session->next_crypto->digest_len);
|
||||
SSH_SIGNATURE_FREE(sig);
|
||||
if (rc == SSH_ERROR) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to verify server hostkey signature");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Signature verified and valid");
|
||||
|
||||
@@ -234,6 +278,9 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys)
|
||||
return SSH_PACKET_USED;
|
||||
|
||||
error:
|
||||
#ifdef WITH_GSSAPI
|
||||
SSH_STRING_FREE(session->gssapi_key_exchange_mic);
|
||||
#endif
|
||||
SSH_SIGNATURE_FREE(sig);
|
||||
ssh_string_burn(sig_blob);
|
||||
SSH_STRING_FREE(sig_blob);
|
||||
|
||||
28
src/pki.c
28
src/pki.c
@@ -903,9 +903,9 @@ int ssh_key_cmp(const ssh_key k1,
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBCRYPTO
|
||||
if (k1->type == SSH_KEYTYPE_ED25519) {
|
||||
if (ssh_key_type_plain(k1->type) == SSH_KEYTYPE_ED25519) {
|
||||
return pki_ed25519_key_cmp(k1, k2, what);
|
||||
} else if (k1->type == SSH_KEYTYPE_SK_ED25519) {
|
||||
} else if (ssh_key_type_plain(k1->type) == SSH_KEYTYPE_SK_ED25519) {
|
||||
return pki_ed25519_key_cmp(k1, k2, SSH_KEY_CMP_PUBLIC);
|
||||
}
|
||||
#endif
|
||||
@@ -1882,14 +1882,16 @@ fail:
|
||||
/**
|
||||
* @brief Import a base64 formatted public key from a memory c-string.
|
||||
*
|
||||
* @param[in] b64_key The base64 key to format.
|
||||
*
|
||||
* @param[in] type The type of the key to format.
|
||||
* Note that the public key is just the base64 part (without the key
|
||||
* type prefix and comment suffix you can find in the OpenSSH public
|
||||
* key file or known_hosts file).
|
||||
*
|
||||
* @param[in] b64_key The base64 key to import.
|
||||
* @param[in] type The type of the key to import.
|
||||
* @param[out] pkey A pointer where the allocated key can be stored. You
|
||||
* need to free the memory using ssh_key_free().
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*
|
||||
* @see ssh_key_free()
|
||||
*/
|
||||
@@ -2191,14 +2193,16 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
|
||||
/**
|
||||
* @brief Import a base64 formatted certificate from a memory c-string.
|
||||
*
|
||||
* @param[in] b64_cert The base64 cert to format.
|
||||
* Note that the certificate is just the base64 part (without the key
|
||||
* type prefix and comment suffix you can find in the OpenSSH certificate
|
||||
* file).
|
||||
*
|
||||
* @param[in] type The type of the cert to format.
|
||||
* @param[in] b64_cert The base64 cert to import.
|
||||
* @param[in] type The type of the cert to import.
|
||||
* @param[out] pkey A pointer where the allocated certificate can be stored.
|
||||
* You need to free the memory using ssh_key_free().
|
||||
*
|
||||
* @param[out] pkey A pointer where the allocated key can be stored. You
|
||||
* need to free the memory using ssh_key_free().
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*
|
||||
* @see ssh_key_free()
|
||||
*/
|
||||
|
||||
@@ -1409,7 +1409,7 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519_CERT01:
|
||||
/* ed25519 keys handled globally */
|
||||
return 0;
|
||||
return 1;
|
||||
case SSH_KEYTYPE_ECDSA_P256:
|
||||
case SSH_KEYTYPE_ECDSA_P256_CERT01:
|
||||
case SSH_KEYTYPE_ECDSA_P384:
|
||||
|
||||
@@ -782,7 +782,7 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
case SSH_KEYTYPE_SK_ED25519:
|
||||
/* ed25519 keys handled globally */
|
||||
rc = 0;
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
rc = 1;
|
||||
|
||||
455
src/server.c
455
src/server.c
@@ -44,21 +44,22 @@
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/server.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/packet.h"
|
||||
#include "libssh/socket.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/kex.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/messages.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/gssapi.h"
|
||||
#include "libssh/kex.h"
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/messages.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/packet.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/server.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/socket.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/token.h"
|
||||
|
||||
#define set_status(session, status) do {\
|
||||
@@ -72,15 +73,22 @@
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @internal
|
||||
/**
|
||||
* @internal
|
||||
* @brief Sets the server's key exchange, encryption, MAC, and compression
|
||||
* algorithms.
|
||||
*
|
||||
* @brief initialize the set of key exchange, hostkey, ciphers, MACs, and
|
||||
* compression algorithms for the given ssh_session
|
||||
* Prepares the server key exchange (KEX) proposals by prioritizing the
|
||||
* available host keys (Ed25519, ECDSA, RSA) based on their strength and fills
|
||||
* in the KEX method lists based on session options or defaults. This is
|
||||
* essential for negotiating secure communication parameters in the SSH
|
||||
* handshake.
|
||||
*
|
||||
* The selection of algorithms and keys used are determined by the
|
||||
* options that are currently set in the given ssh_session structure.
|
||||
* @param[in] session The SSH session to set up.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on failure (e.g., no host keys
|
||||
* available, random number generation error, or memory allocation failure).
|
||||
*/
|
||||
|
||||
int server_set_kex(ssh_session session)
|
||||
{
|
||||
struct ssh_kex_struct *server = &session->next_crypto->server_kex;
|
||||
@@ -91,6 +99,9 @@ int server_set_kex(ssh_session session)
|
||||
enum ssh_keytypes_e keytype;
|
||||
size_t len;
|
||||
int ok;
|
||||
#ifdef WITH_GSSAPI
|
||||
char *gssapi_algs = NULL;
|
||||
#endif /* WITH_GSSAPI */
|
||||
|
||||
/* Skip if already set, for example for the rekey or when we do the guessing
|
||||
* it could have been already used to make some protocol decisions. */
|
||||
@@ -130,10 +141,6 @@ int server_set_kex(ssh_session session)
|
||||
",%s", ssh_key_type_to_char(keytype));
|
||||
}
|
||||
|
||||
if (strlen(hostkeys) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
|
||||
allowed = session->opts.wanted_methods[SSH_HOSTKEYS];
|
||||
} else {
|
||||
@@ -144,23 +151,52 @@ int server_set_kex(ssh_session session)
|
||||
}
|
||||
}
|
||||
|
||||
/* It is expected for the list of allowed hostkeys to be ordered by
|
||||
* preference */
|
||||
kept = ssh_find_all_matching(hostkeys[0] == ',' ? hostkeys + 1 : hostkeys,
|
||||
allowed);
|
||||
if (kept == NULL) {
|
||||
/* Nothing was allowed */
|
||||
return -1;
|
||||
}
|
||||
if (strlen(hostkeys) != 0) {
|
||||
/* It is expected for the list of allowed hostkeys to be ordered by
|
||||
* preference */
|
||||
kept =
|
||||
ssh_find_all_matching(hostkeys[0] == ',' ? hostkeys + 1 : hostkeys,
|
||||
allowed);
|
||||
if (kept == NULL) {
|
||||
/* Nothing was allowed */
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_options_set_algo(session,
|
||||
SSH_HOSTKEYS,
|
||||
kept,
|
||||
&session->opts.wanted_methods[SSH_HOSTKEYS]);
|
||||
SAFE_FREE(kept);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
rc = ssh_options_set_algo(session,
|
||||
SSH_HOSTKEYS,
|
||||
kept,
|
||||
&session->opts.wanted_methods[SSH_HOSTKEYS]);
|
||||
SAFE_FREE(kept);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_GSSAPI
|
||||
if (session->opts.gssapi_key_exchange) {
|
||||
ok = ssh_gssapi_init(session);
|
||||
if (ok != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
gssapi_algs = ssh_gssapi_kex_mechs(session);
|
||||
if (gssapi_algs == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_gssapi_free(session);
|
||||
|
||||
/* Prefix the default algorithms with gsskex algs */
|
||||
session->opts.wanted_methods[SSH_KEX] =
|
||||
ssh_prefix_without_duplicates(ssh_kex_get_default_methods(SSH_KEX),
|
||||
gssapi_algs);
|
||||
|
||||
if (strlen(hostkeys) == 0) {
|
||||
session->opts.wanted_methods[SSH_HOSTKEYS] = strdup("null");
|
||||
}
|
||||
|
||||
SAFE_FREE(gssapi_algs);
|
||||
}
|
||||
#endif /* WITH_GSSAPI */
|
||||
|
||||
for (i = 0; i < SSH_KEX_METHODS; i++) {
|
||||
wanted = session->opts.wanted_methods[i];
|
||||
@@ -211,6 +247,23 @@ int ssh_server_init_kex(ssh_session session) {
|
||||
return server_set_kex(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Sends SSH extension information from the server to client.
|
||||
*
|
||||
* A server may send this message (`SSH_MSG_EXT_INFO`) after its first
|
||||
* `SSH_MSG_NEWKEYS` message or just before sending `SSH_MSG_USERAUTH_SUCCESS`
|
||||
* to provide additional extensions support that are not meant for an
|
||||
* unauthenticated client.
|
||||
*
|
||||
* If any error occurs during the packing or sending of the packet, the function
|
||||
* aborts to avoid partial or corrupted sends.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
|
||||
*/
|
||||
static int ssh_server_send_extensions(ssh_session session)
|
||||
{
|
||||
int rc;
|
||||
@@ -276,6 +329,22 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
|
||||
return SSH_PACKET_NOT_USED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares server host key parameters for the key exchange process.
|
||||
*
|
||||
* Selects the appropriate private host key (RSA, ECDSA, or Ed25519) based on
|
||||
* the session's configured host key type, sets the corresponding @p digest
|
||||
* algorithm, and imports the public key blob into the Diffie-Hellman key
|
||||
* exchange state.
|
||||
*
|
||||
* @param[in] session The SSH session to which we are preparing host key
|
||||
* parameters.
|
||||
* @param[out] privkey Pointer to receive the selected private host key.
|
||||
* @param[out] digest Pointer to receive the host key digest algorithm.
|
||||
*
|
||||
* @return `SSH_OK` on success; `SSH_ERROR` on failure (invalid key type, export
|
||||
* failure, or DH import error).
|
||||
*/
|
||||
int
|
||||
ssh_get_key_params(ssh_session session,
|
||||
ssh_key *privkey,
|
||||
@@ -633,6 +702,14 @@ int ssh_auth_reply_default(ssh_session session,int partial) {
|
||||
strncat(methods_c,"gssapi-with-mic,",
|
||||
sizeof(methods_c) - strlen(methods_c) - 1);
|
||||
}
|
||||
/* Check if GSSAPI Key exchange was performed */
|
||||
if (session->auth.supported_methods & SSH_AUTH_METHOD_GSSAPI_KEYEX) {
|
||||
if (ssh_kex_is_gss(session->current_crypto)) {
|
||||
strncat(methods_c,
|
||||
"gssapi-keyex,",
|
||||
sizeof(methods_c) - strlen(methods_c) - 1);
|
||||
}
|
||||
}
|
||||
if (session->auth.supported_methods & SSH_AUTH_METHOD_INTERACTIVE) {
|
||||
strncat(methods_c, "keyboard-interactive,",
|
||||
sizeof(methods_c) - strlen(methods_c) - 1);
|
||||
@@ -669,6 +746,20 @@ int ssh_auth_reply_default(ssh_session session,int partial) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Sends default refusal for a channel open request.
|
||||
*
|
||||
* Default handler that rejects incoming SSH channel open requests by sending
|
||||
* a `SSH2_MSG_CHANNEL_OPEN_FAILURE` packet with "administratively prohibited"
|
||||
* reason code. Used when no custom channel open handler is registered.
|
||||
*
|
||||
* @param[in] msg The SSH message containing the channel open request details.
|
||||
*
|
||||
* @return `SSH_OK` on successful packet send; `SSH_ERROR` on buffer allocation
|
||||
* or packet send failure.
|
||||
*/
|
||||
static int ssh_message_channel_request_open_reply_default(ssh_message msg) {
|
||||
int rc;
|
||||
|
||||
@@ -690,6 +781,21 @@ static int ssh_message_channel_request_open_reply_default(ssh_message msg) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Sends default refusal for a channel request.
|
||||
*
|
||||
* Default handler that rejects incoming SSH channel requests. If the client
|
||||
* requested a reply (`want_reply`), sends `SSH2_MSG_CHANNEL_FAILURE` to the
|
||||
* specific channel. If no reply requested, logs the refusal and returns
|
||||
* success.
|
||||
*
|
||||
* @param[in] msg The SSH message containing the channel request details.
|
||||
*
|
||||
* @return `SSH_OK` on success; `SSH_ERROR` if buffer allocation or packet send
|
||||
* fails.
|
||||
*/
|
||||
static int ssh_message_channel_request_reply_default(ssh_message msg) {
|
||||
uint32_t channel;
|
||||
int rc;
|
||||
@@ -723,11 +829,11 @@ static int ssh_message_service_request_reply_default(ssh_message msg) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends SERVICE_ACCEPT to the client
|
||||
* @brief Sends `SSH2_MSG_SERVICE_ACCEPT` to the client
|
||||
*
|
||||
* @param msg The message to reply to
|
||||
*
|
||||
* @returns SSH_OK when success otherwise SSH_ERROR
|
||||
* @returns `SSH_OK` when success otherwise `SSH_ERROR`
|
||||
*/
|
||||
int ssh_message_service_reply_success(ssh_message msg)
|
||||
{
|
||||
@@ -761,7 +867,7 @@ int ssh_message_service_reply_success(ssh_message msg)
|
||||
*
|
||||
* @param bound_port The remote bind port
|
||||
*
|
||||
* @returns SSH_OK on success, otherwise SSH_ERROR
|
||||
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
|
||||
*/
|
||||
int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) {
|
||||
int rc;
|
||||
@@ -797,6 +903,20 @@ error:
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Sends default refusal for a global request.
|
||||
*
|
||||
* Default handler that rejects incoming SSH global requests. If the client
|
||||
* requested a reply (`want_reply`), sends `SSH2_MSG_REQUEST_FAILURE`. If no
|
||||
* reply requested, logs the refusal and returns success immediately.
|
||||
*
|
||||
* @param[in] msg The SSH message containing the global request details.
|
||||
*
|
||||
* @return `SSH_OK` on success; `SSH_ERROR` if buffer allocation or packet send
|
||||
* fails.
|
||||
*/
|
||||
static int ssh_message_global_request_reply_default(ssh_message msg) {
|
||||
SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a global request");
|
||||
|
||||
@@ -935,6 +1055,28 @@ int ssh_message_auth_set_methods(ssh_message msg, int methods) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends an interactive authentication request message.
|
||||
*
|
||||
* Builds and sends an `SSH2_MSG_USERAUTH_INFO_REQUEST` packet containing the
|
||||
* given name and @p instruction, followed by a number of @p prompts with
|
||||
* associated @p echo flags to control whether user input is echoed.
|
||||
* It initializes the keyboard-interactive state in the session.
|
||||
*
|
||||
* @param[in] msg The SSH message representing the client
|
||||
* authentication request.
|
||||
* @param[in] name The name of the authentication request.
|
||||
* @param[in] instruction Instruction string with information for the user.
|
||||
* @param[in] num_prompts Number of prompts to send. The arrays prompts and
|
||||
* echo must both have num_prompts elements.
|
||||
* @param[in] prompts Array of @p num_prompts prompt strings to display.
|
||||
* @param[in] echo Array of num_prompts boolean values (0 or 1). A
|
||||
* non-zero value means the user input for that prompt
|
||||
* is echoed (visible); 0 means the input is hidden
|
||||
* (typically for passwords).
|
||||
*
|
||||
* @return `SSH_OK` on successful send; `SSH_ERROR` on failure.
|
||||
*/
|
||||
int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
|
||||
const char *instruction, unsigned int num_prompts,
|
||||
const char **prompts, char *echo) {
|
||||
@@ -1041,15 +1183,15 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends SSH2_MSG_USERAUTH_SUCCESS or SSH2_MSG_USERAUTH_FAILURE message
|
||||
* depending on the success of the authentication method
|
||||
* @brief Sends `SSH2_MSG_USERAUTH_SUCCESS` or `SSH2_MSG_USERAUTH_FAILURE`
|
||||
* message depending on the success of the authentication method
|
||||
*
|
||||
* @param session The session to reply to
|
||||
*
|
||||
* @param partial Denotes if the authentication process was partially completed
|
||||
* (unsuccessful)
|
||||
*
|
||||
* @returns SSH_OK on success, otherwise SSH_ERROR
|
||||
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
|
||||
*/
|
||||
int ssh_auth_reply_success(ssh_session session, int partial)
|
||||
{
|
||||
@@ -1073,9 +1215,9 @@ int ssh_auth_reply_success(ssh_session session, int partial)
|
||||
|
||||
/*
|
||||
* Consider the session as having been authenticated only after sending
|
||||
* the USERAUTH_SUCCESS message. Setting these flags after ssh_packet_send
|
||||
* ensures that a rekey is not triggered prematurely, causing the message
|
||||
* to be queued.
|
||||
* the `USERAUTH_SUCCESS` message. Setting these flags after
|
||||
* ssh_packet_send ensures that a rekey is not triggered prematurely,
|
||||
* causing the message to be queued.
|
||||
*/
|
||||
session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
|
||||
session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;
|
||||
@@ -1094,6 +1236,18 @@ int ssh_auth_reply_success(ssh_session session, int partial)
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replies to an authentication request with success.
|
||||
*
|
||||
* Sends an authentication success message (`SSH2_MSG_USERAUTH_SUCCESS`) to the
|
||||
* client, or a partial success if further authentication steps are required.
|
||||
*
|
||||
* @param[in] msg The SSH authentication message being handled.
|
||||
* @param[in] partial Set to nonzero if partial success (more auth needed), zero
|
||||
* for full success.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` if msg is NULL or on send failure.
|
||||
*/
|
||||
int ssh_message_auth_reply_success(ssh_message msg, int partial) {
|
||||
if(msg == NULL)
|
||||
return SSH_ERROR;
|
||||
@@ -1101,7 +1255,7 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
|
||||
* @brief Answer `SSH2_MSG_USERAUTH_PK_OK` to a pubkey authentication request
|
||||
*
|
||||
* @param msg The message
|
||||
*
|
||||
@@ -1109,7 +1263,7 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) {
|
||||
*
|
||||
* @param pubkey The accepted public key
|
||||
*
|
||||
* @returns SSH_OK on success, otherwise SSH_ERROR
|
||||
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
|
||||
*/
|
||||
int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) {
|
||||
int rc;
|
||||
@@ -1132,11 +1286,11 @@ int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pu
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
|
||||
* @brief Answer `SSH2_MSG_USERAUTH_PK_OK` to a pubkey authentication request
|
||||
*
|
||||
* @param msg The message
|
||||
*
|
||||
* @returns SSH_OK on success, otherwise SSH_ERROR
|
||||
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
|
||||
*/
|
||||
int ssh_message_auth_reply_pk_ok_simple(ssh_message msg)
|
||||
{
|
||||
@@ -1163,83 +1317,271 @@ int ssh_message_auth_reply_pk_ok_simple(ssh_message msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the originator address from the channel open message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The originator address, or NULL.
|
||||
*/
|
||||
const char *ssh_message_channel_request_open_originator(ssh_message msg){
|
||||
return msg->channel_request_open.originator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the originator port from the channel open message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The originator port.
|
||||
*/
|
||||
int ssh_message_channel_request_open_originator_port(ssh_message msg){
|
||||
return msg->channel_request_open.originator_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the destination address from the channel open message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The destination address, or NULL.
|
||||
*/
|
||||
const char *ssh_message_channel_request_open_destination(ssh_message msg){
|
||||
return msg->channel_request_open.destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the destination port from the channel open message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The destination port.
|
||||
*/
|
||||
int ssh_message_channel_request_open_destination_port(ssh_message msg){
|
||||
return msg->channel_request_open.destination_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the channel associated with the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The channel associated with the message.
|
||||
*/
|
||||
ssh_channel ssh_message_channel_request_channel(ssh_message msg){
|
||||
return msg->channel_request.channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the terminal type from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The terminal type (e.g. "xterm"), or NULL.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function channel_pty_request_function.
|
||||
*
|
||||
* @see channel_pty_request_function.
|
||||
*/
|
||||
const char *ssh_message_channel_request_pty_term(ssh_message msg){
|
||||
return msg->channel_request.TERM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the terminal width from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The terminal width in characters.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function channel_pty_request_function.
|
||||
*
|
||||
* @see channel_pty_request_function.
|
||||
*/
|
||||
int ssh_message_channel_request_pty_width(ssh_message msg){
|
||||
return msg->channel_request.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the terminal height from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The terminal height in characters.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function channel_pty_request_function.
|
||||
*
|
||||
* @see channel_pty_request_function.
|
||||
*/
|
||||
int ssh_message_channel_request_pty_height(ssh_message msg){
|
||||
return msg->channel_request.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the terminal pixel width from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The pixel width.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function channel_pty_request_function.
|
||||
*
|
||||
* @see channel_pty_request_function.
|
||||
*/
|
||||
int ssh_message_channel_request_pty_pxwidth(ssh_message msg){
|
||||
return msg->channel_request.pxwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the terminal pixel height from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The pixel height.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function channel_pty_request_function.
|
||||
*
|
||||
* @see channel_pty_request_function.
|
||||
*/
|
||||
int ssh_message_channel_request_pty_pxheight(ssh_message msg){
|
||||
return msg->channel_request.pxheight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the environment variable from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The variable name, or NULL.
|
||||
*/
|
||||
const char *ssh_message_channel_request_env_name(ssh_message msg){
|
||||
return msg->channel_request.var_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value of the environment variable from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The variable value, or NULL.
|
||||
*/
|
||||
const char *ssh_message_channel_request_env_value(ssh_message msg){
|
||||
return msg->channel_request.var_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the command from a channel request message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The command, or NULL.
|
||||
*/
|
||||
const char *ssh_message_channel_request_command(ssh_message msg){
|
||||
return msg->channel_request.command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the subsystem from a channel request message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The subsystem, or NULL.
|
||||
*/
|
||||
const char *ssh_message_channel_request_subsystem(ssh_message msg){
|
||||
return msg->channel_request.subsystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the X11 request is for a single connection.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return 1 if single connection, 0 otherwise.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function
|
||||
* channel_open_request_x11_function.
|
||||
*
|
||||
* @see channel_open_request_x11_function.
|
||||
*/
|
||||
int ssh_message_channel_request_x11_single_connection(ssh_message msg){
|
||||
return msg->channel_request.x11_single_connection ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the X11 authentication protocol from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The authentication protocol, or NULL.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function
|
||||
* channel_open_request_x11_function.
|
||||
*
|
||||
* @see channel_open_request_x11_function.
|
||||
*/
|
||||
const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg){
|
||||
return msg->channel_request.x11_auth_protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the X11 authentication cookie from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The authentication cookie, or NULL.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function
|
||||
* channel_open_request_x11_function.
|
||||
*
|
||||
* @see channel_open_request_x11_function.
|
||||
*/
|
||||
const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg){
|
||||
return msg->channel_request.x11_auth_cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the X11 screen number from the message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The screen number.
|
||||
*
|
||||
* @deprecated This function should not be used anymore as there is a
|
||||
* callback based server implementation function
|
||||
* channel_open_request_x11_function.
|
||||
*
|
||||
* @see channel_open_request_x11_function.
|
||||
*/
|
||||
int ssh_message_channel_request_x11_screen_number(ssh_message msg){
|
||||
return msg->channel_request.x11_screen_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the bind address from the global request message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The bind address, or NULL.
|
||||
*/
|
||||
const char *ssh_message_global_request_address(ssh_message msg){
|
||||
return msg->global_request.bind_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the bind port from the global request message.
|
||||
*
|
||||
* @param[in] msg The message.
|
||||
*
|
||||
* @return The bind port.
|
||||
*/
|
||||
int ssh_message_global_request_port(ssh_message msg){
|
||||
return msg->global_request.bind_port;
|
||||
}
|
||||
@@ -1259,6 +1601,13 @@ void ssh_set_message_callback(ssh_session session,
|
||||
session->ssh_message_callback_data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Execute callbacks for the messages in the queue.
|
||||
*
|
||||
* @param[in] session The session.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_execute_message_callbacks(ssh_session session){
|
||||
ssh_message msg=NULL;
|
||||
int ret;
|
||||
@@ -1294,7 +1643,7 @@ int ssh_execute_message_callbacks(ssh_session session){
|
||||
*
|
||||
* @param session The session to send the message to
|
||||
*
|
||||
* @returns SSH_OK
|
||||
* @returns `SSH_OK`
|
||||
*/
|
||||
int ssh_send_keepalive(ssh_session session)
|
||||
{
|
||||
|
||||
193
src/session.c
193
src/session.c
@@ -160,6 +160,14 @@ ssh_session ssh_new(void)
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef WITH_GSSAPI
|
||||
session->opts.gssapi_key_exchange_algs =
|
||||
strdup(GSSAPI_KEY_EXCHANGE_SUPPORTED);
|
||||
if (session->opts.gssapi_key_exchange_algs == NULL) {
|
||||
goto err;
|
||||
}
|
||||
#endif /* WITH_GSSAPI */
|
||||
|
||||
id = strdup("%d/id_ed25519");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
@@ -325,6 +333,7 @@ void ssh_free(ssh_session session)
|
||||
|
||||
#ifdef WITH_GSSAPI
|
||||
ssh_gssapi_free(session);
|
||||
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
|
||||
#endif
|
||||
|
||||
/* options */
|
||||
@@ -470,12 +479,18 @@ const char* ssh_get_kex_algo(ssh_session session) {
|
||||
return "diffie-hellman-group14-sha1";
|
||||
case SSH_KEX_DH_GROUP14_SHA256:
|
||||
return "diffie-hellman-group14-sha256";
|
||||
case SSH_GSS_KEX_DH_GROUP14_SHA256:
|
||||
return "gss-group14-sha256-";
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
return "diffie-hellman-group16-sha512";
|
||||
case SSH_GSS_KEX_DH_GROUP16_SHA512:
|
||||
return "gss-group16-sha512-";
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
return "diffie-hellman-group18-sha512";
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
return "ecdh-sha2-nistp256";
|
||||
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
|
||||
return "gss-nistp256-sha256-";
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
return "ecdh-sha2-nistp384";
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
@@ -484,18 +499,20 @@ const char* ssh_get_kex_algo(ssh_session session) {
|
||||
return "curve25519-sha256";
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
return "curve25519-sha256@libssh.org";
|
||||
case SSH_GSS_KEX_CURVE25519_SHA256:
|
||||
return "gss-curve25519-sha256-";
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
return "sntrup761x25519-sha512@openssh.com";
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
return "sntrup761x25519-sha512";
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
return "mlkem768x25519-sha256";
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
return "mlkem768nistp256-sha256";
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
return "mlkem1024nistp384-sha384";
|
||||
#endif /* HAVE_MLKEM */
|
||||
#endif /* HAVE_MLKEM1024 */
|
||||
#ifdef WITH_GEX
|
||||
case SSH_KEX_DH_GEX_SHA1:
|
||||
return "diffie-hellman-group-exchange-sha1";
|
||||
@@ -646,10 +663,9 @@ static int ssh_flush_termination(void *c){
|
||||
* will block, in milliseconds. Specifying -1
|
||||
* means an infinite timeout. This parameter is passed to
|
||||
* the poll() function.
|
||||
* @returns SSH_OK on success, SSH_AGAIN if timeout occurred,
|
||||
* SSH_ERROR otherwise.
|
||||
* @returns `SSH_OK` on success, `SSH_AGAIN` if timeout occurred,
|
||||
* `SSH_ERROR` otherwise.
|
||||
*/
|
||||
|
||||
int ssh_blocking_flush(ssh_session session, int timeout){
|
||||
int rc;
|
||||
if (session == NULL) {
|
||||
@@ -731,7 +747,7 @@ void ssh_set_fd_towrite(ssh_session session) {
|
||||
/**
|
||||
* @brief Tell the session it has an exception to catch on the file descriptor.
|
||||
*
|
||||
* \param[in] session The ssh session to use.
|
||||
* @param[in] session The ssh session to use.
|
||||
*/
|
||||
void ssh_set_fd_except(ssh_session session) {
|
||||
if (session == NULL) {
|
||||
@@ -745,20 +761,22 @@ void ssh_set_fd_except(ssh_session session) {
|
||||
* @internal
|
||||
*
|
||||
* @brief Poll the current session for an event and call the appropriate
|
||||
* callbacks. This function will not loop until the timeout is expired.
|
||||
* callbacks. This function will not loop until the @p timeout is expired.
|
||||
*
|
||||
* This will block until one event happens.
|
||||
*
|
||||
* @param[in] session The session handle to use.
|
||||
*
|
||||
* @param[in] timeout Set an upper limit on the time for which this function
|
||||
* will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE
|
||||
* will block, in milliseconds. Specifying
|
||||
* `SSH_TIMEOUT_INFINITE`
|
||||
* (-1) means an infinite timeout.
|
||||
* Specifying SSH_TIMEOUT_USER means to use the timeout
|
||||
* specified in options. 0 means poll will return immediately.
|
||||
* Specifying `SSH_TIMEOUT_USER` means to use the timeout
|
||||
* specified in options. 0 means poll will return
|
||||
* immediately.
|
||||
* This parameter is passed to the poll() function.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR otherwise.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` otherwise.
|
||||
*/
|
||||
int ssh_handle_packets(ssh_session session, int timeout)
|
||||
{
|
||||
@@ -813,13 +831,14 @@ int ssh_handle_packets(ssh_session session, int timeout)
|
||||
* @brief Poll the current session for an event and call the appropriate
|
||||
* callbacks.
|
||||
*
|
||||
* This will block until termination function returns true, or timeout expired.
|
||||
* This will block until termination function returns true, or @p timeout
|
||||
* expired.
|
||||
*
|
||||
* @param[in] session The session handle to use.
|
||||
*
|
||||
* @param[in] timeout Set an upper limit on the time for which this function
|
||||
* will block, in milliseconds. Specifying
|
||||
* SSH_TIMEOUT_INFINITE (-1) means an infinite timeout.
|
||||
* `SSH_TIMEOUT_INFINITE` (-1) means an infinite timeout.
|
||||
* Specifying SSH_TIMEOUT_USER means using the timeout
|
||||
* specified in options. 0 means poll will return
|
||||
* immediately.
|
||||
@@ -830,8 +849,8 @@ int ssh_handle_packets(ssh_session session, int timeout)
|
||||
* @param[in] fct Termination function to be used to determine if it is
|
||||
* possible to stop polling.
|
||||
* @param[in] user User parameter to be passed to fct termination function.
|
||||
* @returns SSH_OK on success, SSH_AGAIN if timeout occurred,
|
||||
* SSH_ERROR otherwise.
|
||||
* @returns `SSH_OK` on success, `SSH_AGAIN` if timeout occurred,
|
||||
* `SSH_ERROR` otherwise.
|
||||
*/
|
||||
int ssh_handle_packets_termination(ssh_session session,
|
||||
int timeout,
|
||||
@@ -888,9 +907,10 @@ int ssh_handle_packets_termination(ssh_session session,
|
||||
*
|
||||
* @param session The ssh session to use.
|
||||
*
|
||||
* @returns A bitmask including SSH_CLOSED, SSH_READ_PENDING, SSH_WRITE_PENDING
|
||||
* or SSH_CLOSED_ERROR which respectively means the session is closed,
|
||||
* has data to read on the connection socket and session was closed
|
||||
* @returns A bitmask including `SSH_CLOSED`, `SSH_READ_PENDING`,
|
||||
* `SSH_WRITE_PENDING` or `SSH_CLOSED_ERROR` which
|
||||
* respectively means the session is closed, has data to
|
||||
* read on the connection socket and session was closed
|
||||
* due to an error.
|
||||
*/
|
||||
int ssh_get_status(ssh_session session) {
|
||||
@@ -926,9 +946,9 @@ int ssh_get_status(ssh_session session) {
|
||||
*
|
||||
* @param session The ssh session to use.
|
||||
*
|
||||
* @returns A bitmask including SSH_READ_PENDING or SSH_WRITE_PENDING.
|
||||
* For SSH_READ_PENDING, your invocation of poll() should include
|
||||
* POLLIN. For SSH_WRITE_PENDING, your invocation of poll() should
|
||||
* @returns A bitmask including `SSH_READ_PENDING` or `SSH_WRITE_PENDING`.
|
||||
* For `SSH_READ_PENDING`, your invocation of poll() should include
|
||||
* POLLIN. For `SSH_WRITE_PENDING`, your invocation of poll() should
|
||||
* include POLLOUT.
|
||||
*/
|
||||
int ssh_get_poll_flags(ssh_session session)
|
||||
@@ -1036,7 +1056,7 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){
|
||||
* @param[in] session The SSH session
|
||||
* @param[in] data Data to be sent
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR otherwise.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` otherwise.
|
||||
*/
|
||||
int ssh_send_ignore (ssh_session session, const char *data) {
|
||||
const int type = SSH2_MSG_IGNORE;
|
||||
@@ -1071,7 +1091,7 @@ error:
|
||||
* SHOULD NOT be displayed unless debugging
|
||||
* information has been explicitly requested.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR otherwise.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` otherwise.
|
||||
*/
|
||||
int ssh_send_debug (ssh_session session, const char *message, int always_display) {
|
||||
int rc;
|
||||
@@ -1237,7 +1257,7 @@ void ssh_clean_pubkey_hash(unsigned char **hash)
|
||||
* @param[out] key A pointer to store the allocated key. You need to free
|
||||
* the key using ssh_key_free().
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*
|
||||
* @see ssh_key_free()
|
||||
*/
|
||||
@@ -1271,7 +1291,7 @@ int ssh_get_publickey(ssh_session session, ssh_key *key)
|
||||
/**
|
||||
* @brief Allocates a buffer with the hash of the public key.
|
||||
*
|
||||
* This function allows you to get a hash of the public key. You can then
|
||||
* This function allows you to get a @p hash of the public @p key. You can then
|
||||
* print this hash in a human-readable form to the user so that he is able to
|
||||
* verify it. Use ssh_get_hexa() or ssh_print_hash() to display it.
|
||||
*
|
||||
@@ -1284,7 +1304,7 @@ int ssh_get_publickey(ssh_session session, ssh_key *key)
|
||||
*
|
||||
* @param[in] hlen The length of the hash.
|
||||
*
|
||||
* @return 0 on success, -1 if an error occurred.
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
*
|
||||
* @warning It is very important that you verify at some moment that the hash
|
||||
* matches a known server. If you don't do it, cryptography won't help
|
||||
@@ -1303,126 +1323,57 @@ int ssh_get_publickey_hash(const ssh_key key,
|
||||
{
|
||||
ssh_string blob = NULL;
|
||||
unsigned char *h = NULL;
|
||||
int rc;
|
||||
int (*digest)(const unsigned char *, size_t, unsigned char *) = NULL;
|
||||
int rc, ret = SSH_ERROR;
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(key, &blob);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SSH_PUBLICKEY_HASH_SHA1: {
|
||||
SHACTX ctx = NULL;
|
||||
|
||||
h = calloc(1, SHA_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = sha1_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
sha1_ctx_free(ctx);
|
||||
goto out;
|
||||
}
|
||||
rc = sha1_final(h, ctx);
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
goto out;
|
||||
}
|
||||
|
||||
case SSH_PUBLICKEY_HASH_SHA1:
|
||||
digest = sha1;
|
||||
*hlen = SHA_DIGEST_LEN;
|
||||
break;
|
||||
}
|
||||
case SSH_PUBLICKEY_HASH_SHA256: {
|
||||
SHA256CTX ctx = NULL;
|
||||
|
||||
h = calloc(1, SHA256_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = sha256_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
sha256_ctx_free(ctx);
|
||||
goto out;
|
||||
}
|
||||
rc = sha256_final(h, ctx);
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
goto out;
|
||||
}
|
||||
|
||||
case SSH_PUBLICKEY_HASH_SHA256:
|
||||
digest = sha256;
|
||||
*hlen = SHA256_DIGEST_LEN;
|
||||
break;
|
||||
}
|
||||
case SSH_PUBLICKEY_HASH_MD5: {
|
||||
MD5CTX ctx = NULL;
|
||||
|
||||
/* In FIPS mode, we cannot use MD5 */
|
||||
case SSH_PUBLICKEY_HASH_MD5:
|
||||
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
/* In FIPS mode without OpenSSL providers, we cannot use MD5 */
|
||||
if (ssh_fips_mode()) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"In FIPS mode MD5 is not allowed."
|
||||
"Try using SSH_PUBLICKEY_HASH_SHA256");
|
||||
rc = SSH_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
h = calloc(1, MD5_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = md5_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
md5_ctx_free(ctx);
|
||||
goto out;
|
||||
}
|
||||
rc = md5_final(h, ctx);
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#endif
|
||||
digest = md5;
|
||||
*hlen = MD5_DIGEST_LEN;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
h = calloc(1, *hlen);
|
||||
if (h == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = digest(ssh_string_data(blob), ssh_string_len(blob), h);
|
||||
if (rc != SSH_OK) {
|
||||
free(h);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*hash = h;
|
||||
rc = 0;
|
||||
ret = SSH_OK;
|
||||
|
||||
out:
|
||||
SSH_STRING_FREE(blob);
|
||||
return rc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
242
src/sftpserver.c
242
src/sftpserver.c
@@ -60,6 +60,27 @@
|
||||
#define MAX_ENTRIES_NUM_IN_PACKET 50
|
||||
#define MAX_LONG_NAME_LEN 350
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Creates an SFTP client message from a received packet.
|
||||
*
|
||||
* Allocates and initializes a sftp_client_message structure from a raw
|
||||
* SFTP packet. Copies the complete @p packet payload and parses common
|
||||
* fields such as message type, request id, and message-specific data
|
||||
* (handle, filename, attributes, offsets, etc.) depending on the SFTP
|
||||
* message type.
|
||||
*
|
||||
* On success, the returned message owns its internal buffers and must
|
||||
* be freed with sftp_client_message_free().
|
||||
*
|
||||
* @param[in] sftp The SFTP session associated with the packet.
|
||||
* @param[in] packet The received SFTP packet to decode.
|
||||
*
|
||||
* @return A newly allocated sftp_client_message on success, or NULL on
|
||||
* error (memory allocation failure, malformed packet, or
|
||||
* unsupported message type).
|
||||
*/
|
||||
static sftp_client_message
|
||||
sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
||||
{
|
||||
@@ -256,6 +277,17 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the next SFTP client message from the session.
|
||||
*
|
||||
* Reads a single SFTP packet from the given SFTP session and converts it
|
||||
* into a parsed sftp_client_message structure.
|
||||
*
|
||||
* @param[in] sftp The SFTP session to read from.
|
||||
*
|
||||
* @return A newly allocated sftp_client_message on success; NULL if no packet
|
||||
* is available or an error occurs.
|
||||
*/
|
||||
sftp_client_message sftp_get_client_message(sftp_session sftp)
|
||||
{
|
||||
sftp_packet packet = NULL;
|
||||
@@ -286,22 +318,62 @@ sftp_get_client_message_from_packet(sftp_session sftp)
|
||||
return sftp_make_client_message(sftp, packet);
|
||||
}
|
||||
|
||||
/* Send an sftp client message. Can be used in case of proxying */
|
||||
/**
|
||||
* @brief Send an SFTP client message.
|
||||
*
|
||||
* Writes the given client message as a packet using the stored message
|
||||
* type and complete_message buffer. Can be used in case of proxying.
|
||||
*
|
||||
* @param[in] sftp The SFTP session.
|
||||
* @param[in] msg The client message to send.
|
||||
*
|
||||
* @return 0 on success; -1 on error from sftp_packet_write().
|
||||
*/
|
||||
int sftp_send_client_message(sftp_session sftp, sftp_client_message msg)
|
||||
{
|
||||
return sftp_packet_write(sftp, msg->type, msg->complete_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the SFTP client message type.
|
||||
*
|
||||
* Returns the SFTP packet type associated with the given client message
|
||||
* (for example `SSH_FXP_READ`, `SSH_FXP_WRITE`, `SSH_FXP_OPEN`, ...).
|
||||
*
|
||||
* @param[in] msg The SFTP client message.
|
||||
*
|
||||
* @return The SFTP message type as an unsigned 8-bit value.
|
||||
*/
|
||||
uint8_t sftp_client_message_get_type(sftp_client_message msg)
|
||||
{
|
||||
return msg->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the filename associated with an SFTP client message.
|
||||
*
|
||||
* Returns the filename carried by the given SFTP client message, if the
|
||||
* message type includes a filename field (for example OPEN, REMOVE, RENAME).
|
||||
*
|
||||
* @param[in] msg The SFTP client message.
|
||||
*
|
||||
* @return Filename string, or NULL if no filename
|
||||
* is associated with the message.
|
||||
*/
|
||||
const char *sftp_client_message_get_filename(sftp_client_message msg)
|
||||
{
|
||||
return msg->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the filename associated with an SFTP client message.
|
||||
*
|
||||
* Replaces the current filename stored in the client message with a copy
|
||||
* of the given @p newname string.
|
||||
*
|
||||
* @param[in] msg The SFTP client message to modify.
|
||||
* @param[in] newname The new filename to store in the message.
|
||||
*/
|
||||
void
|
||||
sftp_client_message_set_filename(sftp_client_message msg, const char *newname)
|
||||
{
|
||||
@@ -309,6 +381,17 @@ sftp_client_message_set_filename(sftp_client_message msg, const char *newname)
|
||||
msg->filename = strdup(newname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the data field of an SFTP client message as a string.
|
||||
*
|
||||
* Converts the internal ssh_string data field to a string
|
||||
* on first use and caches the result in the message. Subsequent calls
|
||||
* return the cached pointer.
|
||||
*
|
||||
* @param[in] msg The SFTP client message.
|
||||
*
|
||||
* @return The data as string, or NULL on error.
|
||||
*/
|
||||
const char *sftp_client_message_get_data(sftp_client_message msg)
|
||||
{
|
||||
if (msg->str_data == NULL)
|
||||
@@ -316,16 +399,48 @@ const char *sftp_client_message_get_data(sftp_client_message msg)
|
||||
return msg->str_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the flags associated with an SFTP client message.
|
||||
*
|
||||
* Returns the flags field stored in the given SFTP client message. The exact
|
||||
* meaning of the flags depends on the SFTP message type (for example, open
|
||||
* or stat flags).
|
||||
*
|
||||
* @param[in] msg The SFTP client message.
|
||||
*
|
||||
* @return The flags value as an unsigned 32-bit integer.
|
||||
*/
|
||||
uint32_t sftp_client_message_get_flags(sftp_client_message msg)
|
||||
{
|
||||
return msg->flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submessage name associated with an SFTP client message.
|
||||
*
|
||||
* Returns the submessage string stored in the given SFTP client message.
|
||||
* This is typically used for vendor-specific SFTP operations.
|
||||
*
|
||||
* @param[in] msg The SFTP client message.
|
||||
*
|
||||
* @return The submessage name as a string, or NULL if no
|
||||
* submessage is associated with the message.
|
||||
*/
|
||||
const char *sftp_client_message_get_submessage(sftp_client_message msg)
|
||||
{
|
||||
return msg->submessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free an SFTP client message and its associated resources.
|
||||
*
|
||||
* Releases all dynamically allocated fields in the SFTP client message
|
||||
* (such as filename, submessage, data, handle, attributes, and cached
|
||||
* buffers) and then frees the message structure itself. The function
|
||||
* does nothing if msg is NULL.
|
||||
*
|
||||
* @param[in] msg The SFTP client message to free, or NULL.
|
||||
*/
|
||||
void sftp_client_message_free(sftp_client_message msg)
|
||||
{
|
||||
if (msg == NULL) {
|
||||
@@ -343,6 +458,20 @@ void sftp_client_message_free(sftp_client_message msg)
|
||||
SAFE_FREE(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an SFTP NAME reply for a client message.
|
||||
*
|
||||
* Builds and sends an `SSH_FXP_NAME` packet in response to the given
|
||||
* SFTP client message, containing a single filename and its attributes.
|
||||
* The function encodes the message id, the count of returned names
|
||||
* (always 1), the filename fields and the provided attributes.
|
||||
*
|
||||
* @param[in] msg The SFTP client message being answered.
|
||||
* @param[in] name The filename to return to the client.
|
||||
* @param[in] attr The file attributes associated with the filename.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation failure or packet send error.
|
||||
*/
|
||||
int
|
||||
sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
|
||||
{
|
||||
@@ -378,6 +507,19 @@ sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an SFTP HANDLE reply for a client message.
|
||||
*
|
||||
* Builds and sends an `SSH_FXP_HANDLE` packet in response to the given
|
||||
* SFTP client message, containing the provided file @p handle. The message
|
||||
* id is taken from the client message and the handle is encoded as an
|
||||
* SSH string.
|
||||
*
|
||||
* @param[in] msg The SFTP client message being answered.
|
||||
* @param[in] handle The file handle to return to the client.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation failure or packet send error.
|
||||
*/
|
||||
int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
|
||||
{
|
||||
ssh_buffer out;
|
||||
@@ -402,6 +544,18 @@ int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an SFTP ATTRS reply for a client message.
|
||||
*
|
||||
* Builds and sends an `SSH_FXP_ATTRS` packet in response to the given
|
||||
* SFTP client message, encoding the message id and the provided file
|
||||
* attributes.
|
||||
*
|
||||
* @param[in] msg The SFTP client message being answered.
|
||||
* @param[in] attr The file attributes to return to the client.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation failure or packet send error.
|
||||
*/
|
||||
int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
|
||||
{
|
||||
ssh_buffer out;
|
||||
@@ -424,6 +578,20 @@ int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add one name entry to a multi-name SFTP reply.
|
||||
*
|
||||
* Appends a @p file name, @p longname and attributes to the buffered NAME reply
|
||||
* stored in the client message. Can be called multiple times before the
|
||||
* reply is sent.
|
||||
*
|
||||
* @param[in] msg The SFTP client message being prepared.
|
||||
* @param[in] file The filename to add.
|
||||
* @param[in] longname The long name to add.
|
||||
* @param[in] attr The file attributes for this entry.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation or buffer write error.
|
||||
*/
|
||||
int
|
||||
sftp_reply_names_add(sftp_client_message msg, const char *file,
|
||||
const char *longname, sftp_attributes attr)
|
||||
@@ -464,6 +632,17 @@ sftp_reply_names_add(sftp_client_message msg, const char *file,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a multi-name SFTP reply.
|
||||
*
|
||||
* Sends an `SSH_FXP_NAME` packet for the given client message using the
|
||||
* accumulated name entries stored in msg->attrbuf and msg->attr_num.
|
||||
* After sending, the buffer and counter are reset.
|
||||
*
|
||||
* @param[in] msg The SFTP client message to reply to.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation or packet send error.
|
||||
*/
|
||||
int sftp_reply_names(sftp_client_message msg)
|
||||
{
|
||||
ssh_buffer out;
|
||||
@@ -495,6 +674,20 @@ int sftp_reply_names(sftp_client_message msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an SFTP STATUS reply.
|
||||
*
|
||||
* Sends an `SSH_FXP_STATUS` packet for the given client message, including
|
||||
* the @p status code and an optional human readable @p message. The language
|
||||
* tag is sent as an empty string.
|
||||
*
|
||||
* @param[in] msg The SFTP client message to reply to.
|
||||
* @param[in] status The SFTP status code to send (e.g. `SSH_FX_OK`,
|
||||
* `SSH_FX_FAILURE`).
|
||||
* @param[in] message Optional text message describing the status, or NULL.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation or packet send error.
|
||||
*/
|
||||
int
|
||||
sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
|
||||
{
|
||||
@@ -531,6 +724,18 @@ sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an SFTP DATA reply.
|
||||
*
|
||||
* Sends an `SSH_FXP_DATA` packet for the given client message, containing
|
||||
* the provided data buffer and its length.
|
||||
*
|
||||
* @param[in] msg The SFTP client message to reply to.
|
||||
* @param[in] data The data buffer to send.
|
||||
* @param[in] len Number of bytes from data to send.
|
||||
*
|
||||
* @return 0 on success; -1 on memory allocation or packet send error.
|
||||
*/
|
||||
int sftp_reply_data(sftp_client_message msg, const void *data, int len)
|
||||
{
|
||||
ssh_buffer out;
|
||||
@@ -646,12 +851,18 @@ int sftp_reply_version(sftp_client_message client_msg)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function will return you a new handle to give the client.
|
||||
* the function accepts an info that can be retrieved later with
|
||||
* the handle. Care is given that a corrupted handle won't give a
|
||||
* valid info (or worse).
|
||||
/**
|
||||
* @brief Allocate a new SFTP handle slot.
|
||||
*
|
||||
* Finds a free handle slot in the SFTP session, stores the given @p info
|
||||
* there and returns a 4-byte ssh_string that encodes the handle
|
||||
* index.
|
||||
*
|
||||
* @param[in] sftp The SFTP session.
|
||||
* @param[in] info Info to be stored in the handle slot.
|
||||
*
|
||||
* @return A new handle as ssh_string on success; NULL if no slot is
|
||||
* available or on memory allocation failure.
|
||||
*/
|
||||
ssh_string sftp_handle_alloc(sftp_session sftp, void *info)
|
||||
{
|
||||
@@ -688,6 +899,19 @@ ssh_string sftp_handle_alloc(sftp_session sftp, void *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resolve an SFTP handle to its stored info.
|
||||
*
|
||||
* Decodes the 4-byte @p handle value, checks bounds and returns the pointer
|
||||
* stored in the corresponding handle slot in the SFTP session.
|
||||
*
|
||||
* @param[in] sftp The SFTP session.
|
||||
* @param[in] handle The handle value as ssh_string.
|
||||
*
|
||||
* @return The stored pointer on success, or NULL if the handle table is
|
||||
* not initialized, the handle size is invalid, or the index is
|
||||
* out of range.
|
||||
*/
|
||||
void *sftp_handle(sftp_session sftp, ssh_string handle)
|
||||
{
|
||||
uint32_t val;
|
||||
@@ -1787,8 +2011,8 @@ process_client_message(sftp_client_message client_msg)
|
||||
* @param[out] userdata The pointer to sftp_session which will get the
|
||||
* resulting SFTP session
|
||||
*
|
||||
* @return SSH_OK when the SFTP server was successfully initialized, SSH_ERROR
|
||||
* otherwise.
|
||||
* @return `SSH_OK` when the SFTP server was successfully initialized,
|
||||
* `SSH_ERROR` otherwise.
|
||||
*/
|
||||
int
|
||||
sftp_channel_default_subsystem_request(ssh_session session,
|
||||
|
||||
@@ -37,13 +37,6 @@
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
void crypto_hash_sha512(unsigned char *out,
|
||||
const unsigned char *in,
|
||||
unsigned long long inlen)
|
||||
{
|
||||
sha512(in, inlen, out);
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBGCRYPT
|
||||
static void crypto_random(void *ctx, size_t length, uint8_t *dst)
|
||||
{
|
||||
|
||||
303
src/socket.c
303
src/socket.c
@@ -112,8 +112,14 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
|
||||
uint32_t len);
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief inits the socket system (windows specific)
|
||||
* @internal
|
||||
*
|
||||
* @brief Initialize socket support for libssh.
|
||||
*
|
||||
* Initializes the socket subsystem, calling WSAStartup() on Windows and
|
||||
* ssh_poll_init() on all platforms. Can be called multiple times.
|
||||
*
|
||||
* @return 0 on success; -1 on Windows socket initialization failure.
|
||||
*/
|
||||
int ssh_socket_init(void)
|
||||
{
|
||||
@@ -135,7 +141,12 @@ int ssh_socket_init(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleanup the socket system.
|
||||
* @internal
|
||||
*
|
||||
* @brief Cleanup socket support for libssh.
|
||||
*
|
||||
* Cleans up the socket subsystem, calling ssh_poll_cleanup() on all platforms
|
||||
* and WSACleanup() on Windows. Can be called multiple times.
|
||||
*/
|
||||
void ssh_socket_cleanup(void)
|
||||
{
|
||||
@@ -148,10 +159,17 @@ void ssh_socket_cleanup(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \brief creates a new Socket object
|
||||
* @internal
|
||||
*
|
||||
* @brief Allocate and initialize a new SSH socket structure.
|
||||
*
|
||||
* Creates a new ssh_socket structure associated with the given session,
|
||||
* initializes input/output buffers and sets default socket state.
|
||||
*
|
||||
* @param[in] session The SSH session to associate with the socket.
|
||||
*
|
||||
* @return A new ssh_socket on success; NULL on memory allocation failure.
|
||||
*/
|
||||
ssh_socket ssh_socket_new(ssh_session session)
|
||||
{
|
||||
@@ -189,8 +207,13 @@ ssh_socket ssh_socket_new(ssh_session session)
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Reset the state of a socket so it looks brand-new
|
||||
* @param[in] s socket to rest
|
||||
*
|
||||
* @brief Reset the state of a socket, so it looks brand new.
|
||||
*
|
||||
* Clears the file descriptor, reinitializes input/output buffers, frees
|
||||
* the poll handle if present, and resets all socket state flags.
|
||||
*
|
||||
* @param[in] s The SSH socket to reset.
|
||||
*/
|
||||
void ssh_socket_reset(ssh_socket s)
|
||||
{
|
||||
@@ -219,24 +242,36 @@ void ssh_socket_reset(ssh_socket s)
|
||||
* @param s socket to set callbacks on.
|
||||
* @param callbacks a ssh_socket_callback object reference.
|
||||
*/
|
||||
|
||||
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
|
||||
{
|
||||
s->callbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Mark an SSH socket as connected.
|
||||
*
|
||||
* Sets the socket state to connected and configures the poll handle
|
||||
* to wait for `POLLIN` and `POLLOUT` events (needed for non-blocking connect).
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
* @param[in] p The poll handle to configure, or NULL.
|
||||
*/
|
||||
void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p)
|
||||
{
|
||||
s->state = SSH_SOCKET_CONNECTED;
|
||||
/* POLLOUT is the event to wait for in a nonblocking connect */
|
||||
/* `POLLOUT` is the event to wait for in a non-blocking connect */
|
||||
if (p != NULL) {
|
||||
ssh_poll_set_events(p, POLLIN | POLLOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SSH poll callback. This callback will be used when an event
|
||||
* caught on the socket.
|
||||
* @internal
|
||||
*
|
||||
* @brief SSH poll callback. This callback will be used when an
|
||||
* event caught on the socket.
|
||||
*
|
||||
* @param p Poll object this callback belongs to.
|
||||
* @param fd The raw socket.
|
||||
@@ -416,8 +451,15 @@ ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
|
||||
return s->poll_handle;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief Deletes a socket object
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Deletes a socket object.
|
||||
*
|
||||
* Closes the socket connection, frees input/output buffers and
|
||||
* releases the socket structure memory.
|
||||
*
|
||||
* @param[in] s The SSH socket to free, or NULL.
|
||||
*/
|
||||
void ssh_socket_free(ssh_socket s)
|
||||
{
|
||||
@@ -430,6 +472,20 @@ void ssh_socket_free(ssh_socket s)
|
||||
SAFE_FREE(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Connect an SSH socket to a Unix domain socket.
|
||||
*
|
||||
* Creates a Unix domain socket connection to the given @p path and associates
|
||||
* it with the SSH socket.
|
||||
*
|
||||
* @param[in] s The SSH socket to connect.
|
||||
* @param[in] path Path to the Unix domain socket.
|
||||
*
|
||||
* @return `SSH_OK` on success; `SSH_ERROR` on socket creation, connect, or fd
|
||||
* setup failure.
|
||||
*/
|
||||
int ssh_socket_unix(ssh_socket s, const char *path)
|
||||
{
|
||||
struct sockaddr_un sunaddr;
|
||||
@@ -466,8 +522,17 @@ int ssh_socket_unix(ssh_socket s, const char *path)
|
||||
return ssh_socket_set_fd(s, fd);
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief closes a socket
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Close an SSH socket.
|
||||
*
|
||||
* Closes the socket file descriptor if open, saves the last error code,
|
||||
* frees the poll handle if unlocked, and marks the socket state as closed.
|
||||
* On Unix, attempts to terminate and wait for any running proxy command
|
||||
* process.
|
||||
*
|
||||
* @param[in] s The SSH socket to close.
|
||||
*/
|
||||
void ssh_socket_close(ssh_socket s)
|
||||
{
|
||||
@@ -545,24 +610,48 @@ int ssh_socket_set_fd(ssh_socket s, socket_t fd)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief returns the input file descriptor of the socket
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Returns the input file descriptor of a socket.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return The socket file descriptor (socket_t).
|
||||
*/
|
||||
socket_t ssh_socket_get_fd(ssh_socket s)
|
||||
{
|
||||
return s->fd;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief returns nonzero if the socket is open
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Check if an SSH socket is open.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return Non-zero if socket is open, 0 if closed or invalid.
|
||||
*/
|
||||
int ssh_socket_is_open(ssh_socket s)
|
||||
{
|
||||
return s->fd != SSH_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief read len bytes from socket into buffer
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Perform an unbuffered read from an SSH socket.
|
||||
*
|
||||
* Reads @p len bytes from the socket file descriptor directly into @p buffer,
|
||||
* using `recv()` if the descriptor is a socket, or `read()` otherwise.
|
||||
* Updates internal error and state flags based on the result.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
* @param[out] buffer Buffer to read data into.
|
||||
* @param[in] len Maximum number of bytes to read.
|
||||
*
|
||||
* @return Number of bytes read on success, or -1 on error.
|
||||
*/
|
||||
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
|
||||
void *buffer,
|
||||
@@ -594,8 +683,21 @@ static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief writes len bytes from buffer to socket
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Perform an unbuffered write to an SSH socket.
|
||||
*
|
||||
* Writes @p len bytes from @p buffer to the socket file descriptor,
|
||||
* using `send()` if the descriptor is a socket or `write()` otherwise.
|
||||
* Updates internal error and state flags, and re-enables POLLOUT
|
||||
* polling if a poll handle exists.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
* @param[in] buffer Buffer containing data to write.
|
||||
* @param[in] len Number of bytes to write.
|
||||
*
|
||||
* @return Number of bytes written on success, or -1 on error.
|
||||
*/
|
||||
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
|
||||
const void *buffer,
|
||||
@@ -636,8 +738,18 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
|
||||
return w;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief returns nonzero if the current socket is in the fd_set
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Check if SSH socket file descriptor is set in an fd_set.
|
||||
*
|
||||
* Tests if the socket's file descriptor is present in the
|
||||
* given @p set (fd_set) . Returns 0 if the socket has no valid file descriptor.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
* @param[in] set The fd_set to test against.
|
||||
*
|
||||
* @return Non-zero if the socket fd is set in the fd_set, 0 otherwise.
|
||||
*/
|
||||
int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
|
||||
{
|
||||
@@ -647,10 +759,17 @@ int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
|
||||
return FD_ISSET(s->fd,set);
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief sets the current fd in a fd_set and updates the max_fd
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Add SSH socket file descriptor to an fd_set.
|
||||
*
|
||||
* Adds the socket's file descriptor to the given @p set (fd_set)
|
||||
* and updates @p max_fd if this socket has the highest file descriptor number.
|
||||
* @param[in] s The SSH socket.
|
||||
* @param[in,out] set The fd_set to add the socket to.
|
||||
* @param[in,out] max_fd the maximum fd value.
|
||||
*/
|
||||
|
||||
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
|
||||
{
|
||||
if (s->fd == SSH_INVALID_SOCKET) {
|
||||
@@ -666,10 +785,21 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
|
||||
}
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief buffered write of data
|
||||
* \returns SSH_OK, or SSH_ERROR
|
||||
* \warning has no effect on socket before a flush
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Write data to an SSH socket output buffer.
|
||||
*
|
||||
* Adds the data to the socket's output @p buffer and calls a nonblocking
|
||||
* flush attempt to send buffered data.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
* @param[in] buffer Data to write.
|
||||
* @param[in] len Number of bytes to write.
|
||||
*
|
||||
* @return `SSH_OK` on success; `SSH_ERROR` on buffer allocation failure.
|
||||
*
|
||||
* @warning It has no effect on socket before a flush.
|
||||
*/
|
||||
int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
|
||||
{
|
||||
@@ -684,10 +814,22 @@ int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
|
||||
/** \internal
|
||||
* \brief starts a nonblocking flush of the output buffer
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Starts a nonblocking flush of the output buffer.
|
||||
*
|
||||
* Sends all buffered data from the socket's output buffer.
|
||||
* If the socket is not open, marks the session as dead and calls an
|
||||
* exception callback or sets a fatal error. If the socket cannot currently
|
||||
* accept data, polls for writable events and returns `SSH_AGAIN`.
|
||||
* On write errors, closes the socket and signals the error. Updates
|
||||
* byte counters on successful writes.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return `SSH_OK` if all data was sent; `SSH_AGAIN` if the operation should
|
||||
* be retried later; `SSH_ERROR` on fatal socket error.
|
||||
*/
|
||||
int ssh_socket_nonblocking_flush(ssh_socket s)
|
||||
{
|
||||
@@ -767,26 +909,79 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Set the SSH socket write_wontblock flag.
|
||||
*
|
||||
* Marks the socket as ready for nonblocking writes (`write_wontblock = 1`).
|
||||
* Used by the poll system when POLLOUT becomes available.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*/
|
||||
void ssh_socket_set_write_wontblock(ssh_socket s)
|
||||
{
|
||||
s->write_wontblock = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Set the SSH socket read_wontblock flag.
|
||||
*
|
||||
* Marks the socket as ready for nonblocking reads (`read_wontblock = 1`).
|
||||
* Used by the poll system when POLLIN becomes available.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*/
|
||||
void ssh_socket_set_read_wontblock(ssh_socket s)
|
||||
{
|
||||
s->read_wontblock = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Set the SSH socket exception flag.
|
||||
*
|
||||
* Marks the socket as having an exception condition (`data_except = 1`).
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*/
|
||||
void ssh_socket_set_except(ssh_socket s)
|
||||
{
|
||||
s->data_except = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Check if SSH socket data is available for reading.
|
||||
*
|
||||
* Returns true if the socket is ready for nonblocking reads
|
||||
* (`read_wontblock` flag is set).
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return 1 if data is available, 0 otherwise.
|
||||
*/
|
||||
int ssh_socket_data_available(ssh_socket s)
|
||||
{
|
||||
return s->read_wontblock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Check if SSH socket is writable.
|
||||
*
|
||||
* Returns true if the socket is ready for nonblocking writes
|
||||
* (`write_wontblock` flag is set).
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return 1 if socket is writable, 0 otherwise.
|
||||
*/
|
||||
int ssh_socket_data_writable(ssh_socket s)
|
||||
{
|
||||
return s->write_wontblock;
|
||||
@@ -806,7 +1001,19 @@ int ssh_socket_buffered_write_bytes(ssh_socket s)
|
||||
return ssh_buffer_get_len(s->out_buffer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Get the current status of an SSH socket.
|
||||
*
|
||||
* Checks the input/output buffers and exception flag to determine socket
|
||||
* status: `SSH_READ_PENDING` if input data available, `SSH_WRITE_PENDING`
|
||||
* if output data pending, `SSH_CLOSED_ERROR` if exception occurred.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return Socket status flags.
|
||||
*/
|
||||
int ssh_socket_get_status(ssh_socket s)
|
||||
{
|
||||
int r = 0;
|
||||
@@ -826,6 +1033,18 @@ int ssh_socket_get_status(ssh_socket s)
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Get SSH socket poll flags from the poll handle.
|
||||
*
|
||||
* Checks the poll handle events and returns `SSH_READ_PENDING` if POLLIN
|
||||
* is set, `SSH_WRITE_PENDING` if POLLOUT is set.
|
||||
*
|
||||
* @param[in] s The SSH socket.
|
||||
*
|
||||
* @return Socket status flags based on poll events.
|
||||
*/
|
||||
int ssh_socket_get_poll_flags(ssh_socket s)
|
||||
{
|
||||
int r = 0;
|
||||
@@ -872,8 +1091,8 @@ int ssh_socket_set_blocking(socket_t fd)
|
||||
* @param host hostname or ip address to connect to.
|
||||
* @param port port number to connect to.
|
||||
* @param bind_addr address to bind to, or NULL for default.
|
||||
* @returns SSH_OK socket is being connected.
|
||||
* @returns SSH_ERROR error while connecting to remote host.
|
||||
* @returns `SSH_OK` socket is being connected.
|
||||
* @returns `SSH_ERROR` error while connecting to remote host.
|
||||
*/
|
||||
int ssh_socket_connect(ssh_socket s,
|
||||
const char *host,
|
||||
@@ -958,8 +1177,8 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
|
||||
* This call will always be nonblocking.
|
||||
* @param s socket to connect.
|
||||
* @param command Command to execute.
|
||||
* @returns SSH_OK socket is being connected.
|
||||
* @returns SSH_ERROR error while executing the command.
|
||||
* @returns `SSH_OK` socket is being connected.
|
||||
* @returns `SSH_ERROR` error while executing the command.
|
||||
*/
|
||||
int
|
||||
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
|
||||
@@ -1215,6 +1434,8 @@ exit:
|
||||
ssh_event_free(event);
|
||||
ssh_free(jump_session);
|
||||
|
||||
shutdown(jump_thread_data->fd, SHUT_RDWR);
|
||||
close(jump_thread_data->fd);
|
||||
SAFE_FREE(jump_thread_data);
|
||||
|
||||
pthread_exit(NULL);
|
||||
|
||||
@@ -49,11 +49,10 @@
|
||||
#include "libssh/dh-gex.h"
|
||||
#endif /* WITH_GEX */
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/kex-gss.h"
|
||||
#include "libssh/ecdh.h"
|
||||
#include "libssh/sntrup761.h"
|
||||
#ifdef HAVE_MLKEM
|
||||
#include "libssh/hybrid_mlkem.h"
|
||||
#endif
|
||||
#include "libssh/sntrup761.h"
|
||||
|
||||
static struct ssh_hmac_struct ssh_hmac_tab[] = {
|
||||
{ "hmac-sha1", SSH_HMAC_SHA1, false },
|
||||
@@ -229,15 +228,21 @@ void crypto_free(struct ssh_crypto_struct *crypto)
|
||||
SAFE_FREE(crypto->kex_methods[i]);
|
||||
}
|
||||
|
||||
#ifdef HAVE_MLKEM
|
||||
#ifdef HAVE_OPENSSL_MLKEM
|
||||
EVP_PKEY_free(crypto->mlkem_privkey);
|
||||
#else
|
||||
if (crypto->mlkem_privkey != NULL) {
|
||||
ssh_burn(crypto->mlkem_privkey, crypto->mlkem_privkey_len);
|
||||
SAFE_FREE(crypto->mlkem_privkey);
|
||||
crypto->mlkem_privkey_len = 0;
|
||||
}
|
||||
#endif
|
||||
ssh_string_burn(crypto->hybrid_shared_secret);
|
||||
ssh_string_free(crypto->mlkem_client_pubkey);
|
||||
ssh_string_free(crypto->mlkem_ciphertext);
|
||||
ssh_string_free(crypto->hybrid_client_init);
|
||||
ssh_string_free(crypto->hybrid_server_reply);
|
||||
ssh_string_free(crypto->hybrid_shared_secret);
|
||||
#endif
|
||||
|
||||
ssh_burn(crypto, sizeof(struct ssh_crypto_struct));
|
||||
|
||||
@@ -587,6 +592,14 @@ int crypt_set_algorithms_server(ssh_session session){
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
ssh_server_dh_init(session);
|
||||
break;
|
||||
#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:
|
||||
ssh_server_gss_kex_init(session);
|
||||
break;
|
||||
#endif /* WITH_GSSAPI */
|
||||
#ifdef WITH_GEX
|
||||
case SSH_KEX_DH_GEX_SHA1:
|
||||
case SSH_KEX_DH_GEX_SHA256:
|
||||
@@ -612,13 +625,13 @@ int crypt_set_algorithms_server(ssh_session session){
|
||||
ssh_server_sntrup761x25519_init(session);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_MLKEM
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
case SSH_KEX_MLKEM1024NISTP384_SHA384:
|
||||
#endif
|
||||
ssh_server_hybrid_mlkem_init(session);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
|
||||
@@ -58,8 +58,8 @@ if (SK_DUMMY_LIBRARY)
|
||||
add_library(sk-dummy SHARED IMPORTED)
|
||||
set_target_properties(sk-dummy PROPERTIES IMPORTED_LOCATION "${SK_DUMMY_LIBRARY}")
|
||||
target_link_libraries(${TORTURE_LIBRARY} PRIVATE sk-dummy)
|
||||
target_compile_definitions(${TORTURE_LIBRARY} PUBLIC HAVE_SK_DUMMY)
|
||||
target_compile_definitions(${TORTURE_LIBRARY} PUBLIC SK_DUMMY_LIBRARY_PATH="${SK_DUMMY_LIBRARY}")
|
||||
set(HAVE_SK_DUMMY 1)
|
||||
set(SK_DUMMY_LIBRARY_PATH ${SK_DUMMY_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (WITH_COVERAGE)
|
||||
@@ -90,6 +90,11 @@ if (CLIENT_TESTING)
|
||||
"-Wl,--wrap=sntrup761_keypair"
|
||||
"-Wl,--wrap=sntrup761_enc"
|
||||
"-Wl,--wrap=sntrup761_dec")
|
||||
list(APPEND WRAP_SYMBOLS
|
||||
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_generate_key_pair"
|
||||
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_validate_public_key"
|
||||
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_encapsulate"
|
||||
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_decapsulate")
|
||||
|
||||
add_library(${TORTURE_SHARED_LIBRARY}
|
||||
SHARED
|
||||
@@ -109,8 +114,6 @@ if (CLIENT_TESTING)
|
||||
# Link sk-dummy to torture_shared library if available
|
||||
if (SK_DUMMY_LIBRARY)
|
||||
target_link_libraries(${TORTURE_SHARED_LIBRARY} PRIVATE sk-dummy)
|
||||
target_compile_definitions(${TORTURE_SHARED_LIBRARY} PUBLIC HAVE_SK_DUMMY)
|
||||
target_compile_definitions(${TORTURE_SHARED_LIBRARY} PUBLIC SK_DUMMY_LIBRARY_PATH="${SK_DUMMY_LIBRARY}")
|
||||
endif (SK_DUMMY_LIBRARY)
|
||||
|
||||
target_compile_options(${TORTURE_SHARED_LIBRARY} PRIVATE
|
||||
@@ -231,6 +234,31 @@ else()
|
||||
set(DROPBEAR_EXECUTABLE "/bin/false")
|
||||
endif()
|
||||
|
||||
find_program(PUTTY_EXECUTABLE
|
||||
NAMES
|
||||
plink
|
||||
plink.exe
|
||||
putty # Fallback for systems where plink isn't separate
|
||||
DOC "Path to PuTTY plink executable for automated tests")
|
||||
|
||||
if (PUTTY_EXECUTABLE)
|
||||
message(STATUS "Found PuTTY client: ${PUTTY_EXECUTABLE}")
|
||||
else()
|
||||
set(PUTTY_EXECUTABLE "/bin/putty-not-found")
|
||||
endif()
|
||||
|
||||
find_program(PUTTYGEN_EXECUTABLE
|
||||
NAMES
|
||||
puttygen
|
||||
puttygen.exe
|
||||
DOC "Path to PuTTYgen executable for key conversion")
|
||||
|
||||
if (PUTTYGEN_EXECUTABLE)
|
||||
message(STATUS "Found PuTTY keygen: ${PUTTYGEN_EXECUTABLE}")
|
||||
else()
|
||||
set(PUTTYGEN_EXECUTABLE "/bin/puttygen-not-found")
|
||||
endif()
|
||||
|
||||
find_program(SSHD_EXECUTABLE
|
||||
NAME
|
||||
sshd
|
||||
@@ -334,7 +362,7 @@ if (CLIENT_TESTING OR SERVER_TESTING)
|
||||
|
||||
|
||||
set(TORTURE_ENVIRONMENT
|
||||
"LD_PRELOAD=${SOCKET_WRAPPER_LIBRARY}:${NSS_WRAPPER_LIBRARY}:${UID_WRAPPER_LIBRARY}:${PAM_WRAPPER_LIBRARY}:${CHROOT_WRAPPER}:${FS_WRAPPER}")
|
||||
"LD_PRELOAD=${FS_WRAPPER}:${SOCKET_WRAPPER_LIBRARY}:${NSS_WRAPPER_LIBRARY}:${UID_WRAPPER_LIBRARY}:${PAM_WRAPPER_LIBRARY}:${CHROOT_WRAPPER}")
|
||||
if (priv_wrapper_FOUND)
|
||||
list(APPEND TORTURE_ENVIRONMENT PRIV_WRAPPER=1 PRIV_WRAPPER_CHROOT_DISABLE=1)
|
||||
list(APPEND TORTURE_ENVIRONMENT PRIV_WRAPPER_PRCTL_DISABLE="ALL" PRIV_WRAPPER_SETRLIMIT_DISABLE="ALL")
|
||||
@@ -396,6 +424,9 @@ if (CLIENT_TESTING OR SERVER_TESTING)
|
||||
file(READ keys/pkcs11/id_pkcs11_ecdsa_521_openssh.pub CONTENTS)
|
||||
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
|
||||
|
||||
file(READ keys/pkcs11/id_pkcs11_ed25519_openssh.pub CONTENTS)
|
||||
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
|
||||
|
||||
# Copy the signed key to an doe's homedir.
|
||||
file(COPY keys/certauth/id_rsa DESTINATION
|
||||
${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
|
||||
|
||||
@@ -45,7 +45,9 @@ endif()
|
||||
if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING)
|
||||
set(LIBSSH_CLIENT_TESTS
|
||||
${LIBSSH_CLIENT_TESTS}
|
||||
torture_gssapi_auth)
|
||||
torture_gssapi_auth
|
||||
torture_gssapi_key_exchange
|
||||
torture_gssapi_key_exchange_null)
|
||||
endif()
|
||||
|
||||
if (DEFAULT_C_NO_DEPRECATION_FLAGS)
|
||||
|
||||
@@ -752,7 +752,7 @@ torture_algorithms_ecdh_sntrup761x25519_sha512(void **state)
|
||||
}
|
||||
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
|
||||
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
|
||||
#if defined(OPENSSH_MLKEM768X25519_SHA256)
|
||||
static void torture_algorithms_ecdh_mlkem768x25519_sha256(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -766,9 +766,9 @@ static void torture_algorithms_ecdh_mlkem768x25519_sha256(void **state)
|
||||
NULL /*cipher*/,
|
||||
NULL /*hmac*/);
|
||||
}
|
||||
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768X25519_SHA256) */
|
||||
#endif /* defined(OPENSSH_MLKEM768X25519_SHA256) */
|
||||
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
|
||||
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
|
||||
static void torture_algorithms_ecdh_mlkem768nistp256_sha256(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -782,9 +782,9 @@ static void torture_algorithms_ecdh_mlkem768nistp256_sha256(void **state)
|
||||
NULL /*cipher*/,
|
||||
NULL /*hmac*/);
|
||||
}
|
||||
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768NISTP256_SHA256) */
|
||||
#endif /* defined(OPENSSH_MLKEM768NISTP256_SHA256) */
|
||||
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
|
||||
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
|
||||
static void torture_algorithms_ecdh_mlkem1024nistp384_sha384(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -798,7 +798,7 @@ static void torture_algorithms_ecdh_mlkem1024nistp384_sha384(void **state)
|
||||
NULL /*cipher*/,
|
||||
NULL /*hmac*/);
|
||||
}
|
||||
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
|
||||
#endif /* HAVE_MLKEM1024 && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
|
||||
|
||||
static void torture_algorithms_dh_group1(void **state) {
|
||||
struct torture_state *s = *state;
|
||||
@@ -1077,21 +1077,21 @@ int torture_run_tests(void) {
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
|
||||
#if defined(OPENSSH_MLKEM768X25519_SHA256)
|
||||
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem768x25519_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768X25519_SHA256) */
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
|
||||
#endif /* defined(OPENSSH_MLKEM768X25519_SHA256) */
|
||||
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
|
||||
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem768nistp256_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256) */
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
|
||||
#endif /* defined(OPENSSH_MLKEM768NISTP256_SHA256) */
|
||||
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
|
||||
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem1024nistp384_sha384,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
|
||||
#endif /* defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
|
||||
#if defined(HAVE_ECC)
|
||||
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_sha2_nistp256,
|
||||
session_setup,
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h> /* usleep */
|
||||
|
||||
#ifndef UNIX_PATH_MAX
|
||||
#define UNIX_PATH_MAX 108
|
||||
#endif
|
||||
|
||||
/* struct to store the state of the test */
|
||||
struct agent_callback_state {
|
||||
int called;
|
||||
@@ -137,6 +141,19 @@ static void torture_auth_agent_forwarding(void **state)
|
||||
int read_count = 0;
|
||||
bool agent_available = false;
|
||||
bool agent_not_available_found = false;
|
||||
size_t exp_socket_len;
|
||||
|
||||
/* The forwarded agent socket is created under the home directory, which
|
||||
* might easily extend the maximum unix domain socket path length.
|
||||
* If we see this, just skip the test as it will not work */
|
||||
exp_socket_len = strlen(BINARYDIR) +
|
||||
strlen("/home/bob/.ssh/agent.1234567890.sshd.XXXXXXXXXX");
|
||||
if (exp_socket_len > UNIX_PATH_MAX) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"The working directory is too long for agent forwarding to work"
|
||||
": Skipping the test");
|
||||
skip();
|
||||
}
|
||||
|
||||
assert_non_null(s);
|
||||
session = s->ssh.ssh.session;
|
||||
@@ -205,6 +222,7 @@ static void torture_auth_agent_forwarding(void **state)
|
||||
|
||||
if (nbytes > 0) {
|
||||
buffer[nbytes] = 0;
|
||||
ssh_log_hexdump("Read bytes:", (unsigned char *)buffer, nbytes);
|
||||
|
||||
/* Process the command output to check for three key conditions:
|
||||
* 1. If SSH_AUTH_SOCK is properly set (meaning agent forwarding
|
||||
|
||||
@@ -35,10 +35,11 @@
|
||||
/* agent_is_running */
|
||||
#include "agent.c"
|
||||
|
||||
#define LIBSSH_RSA_TESTKEY "id_pkcs11_rsa"
|
||||
#define LIBSSH_RSA_TESTKEY "id_pkcs11_rsa"
|
||||
#define LIBSSH_ECDSA_256_TESTKEY "id_pkcs11_ecdsa_256"
|
||||
#define LIBSSH_ECDSA_384_TESTKEY "id_pkcs11_ecdsa_384"
|
||||
#define LIBSSH_ECDSA_521_TESTKEY "id_pkcs11_ecdsa_521"
|
||||
#define LIBSSH_ED25519_TESTKEY "id_pkcs11_ed25519"
|
||||
|
||||
const char template[] = "/tmp/temp_dir_XXXXXX";
|
||||
|
||||
@@ -58,17 +59,22 @@ static int setup_tokens(void **state, const char *type, const char *obj_name)
|
||||
cwd = test_state->temp_dir;
|
||||
assert_non_null(cwd);
|
||||
|
||||
snprintf(priv_filename, sizeof(priv_filename), "%s%s", test_state->keys_dir, type);
|
||||
snprintf(priv_filename,
|
||||
sizeof(priv_filename),
|
||||
"%s%s",
|
||||
test_state->keys_dir,
|
||||
type);
|
||||
|
||||
torture_setup_tokens(cwd, priv_filename, obj_name, "1");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
struct torture_state *s = *state;
|
||||
struct passwd *pwd;
|
||||
struct passwd *pwd = NULL;
|
||||
bool b = false;
|
||||
int rc;
|
||||
|
||||
@@ -110,7 +116,7 @@ static int setup_pkcs11(void **state)
|
||||
struct pki_st *test_state = NULL;
|
||||
int rc;
|
||||
char keys_dir[1024] = {0};
|
||||
char *temp_dir;
|
||||
char *temp_dir = NULL;
|
||||
|
||||
test_state = malloc(sizeof(struct pki_st));
|
||||
assert_non_null(test_state);
|
||||
@@ -137,6 +143,9 @@ static int setup_pkcs11(void **state)
|
||||
setup_tokens(state, LIBSSH_ECDSA_256_TESTKEY, "ecdsa256");
|
||||
setup_tokens(state, LIBSSH_ECDSA_384_TESTKEY, "ecdsa384");
|
||||
setup_tokens(state, LIBSSH_ECDSA_521_TESTKEY, "ecdsa521");
|
||||
if (!ssh_fips_mode()) {
|
||||
setup_tokens(state, LIBSSH_ED25519_TESTKEY, "ed25519");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -150,8 +159,8 @@ static int sshd_setup(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshd_teardown(void **state) {
|
||||
|
||||
static int sshd_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
struct pki_st *test_state = s->private_data;
|
||||
int rc;
|
||||
@@ -176,7 +185,9 @@ static int sshd_teardown(void **state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void torture_auth_autopubkey(void **state, const char *obj_name, const char *pin) {
|
||||
static void
|
||||
torture_auth_autopubkey(void **state, const char *obj_name, const char *pin)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
@@ -186,8 +197,12 @@ static void torture_auth_autopubkey(void **state, const char *obj_name, const ch
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_CHARLIE);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
snprintf(priv_uri, sizeof(priv_uri), "pkcs11:token=%s;object=%s;type=private?pin-value=%s",
|
||||
obj_name, obj_name, pin);
|
||||
snprintf(priv_uri,
|
||||
sizeof(priv_uri),
|
||||
"pkcs11:token=%s;object=%s;type=private?pin-value=%s",
|
||||
obj_name,
|
||||
obj_name,
|
||||
pin);
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, priv_uri);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
@@ -207,23 +222,40 @@ static void torture_auth_autopubkey(void **state, const char *obj_name, const ch
|
||||
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||
}
|
||||
|
||||
static void torture_auth_autopubkey_rsa(void **state) {
|
||||
static void torture_auth_autopubkey_rsa(void **state)
|
||||
{
|
||||
torture_auth_autopubkey(state, "rsa", "1234");
|
||||
}
|
||||
|
||||
static void torture_auth_autopubkey_ecdsa_key_256(void **state) {
|
||||
static void torture_auth_autopubkey_ecdsa_key_256(void **state)
|
||||
{
|
||||
torture_auth_autopubkey(state, "ecdsa256", "1234");
|
||||
}
|
||||
|
||||
static void torture_auth_autopubkey_ecdsa_key_384(void **state) {
|
||||
static void torture_auth_autopubkey_ecdsa_key_384(void **state)
|
||||
{
|
||||
torture_auth_autopubkey(state, "ecdsa384", "1234");
|
||||
}
|
||||
|
||||
static void torture_auth_autopubkey_ecdsa_key_521(void **state) {
|
||||
static void torture_auth_autopubkey_ecdsa_key_521(void **state)
|
||||
{
|
||||
torture_auth_autopubkey(state, "ecdsa521", "1234");
|
||||
}
|
||||
|
||||
int torture_run_tests(void) {
|
||||
#ifdef WITH_PKCS11_PROVIDER
|
||||
static void torture_auth_autopubkey_ed25519(void **state)
|
||||
{
|
||||
/* The Ed25519 keys are not supported in FIPS mode */
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
|
||||
torture_auth_autopubkey(state, "ed25519", "1234");
|
||||
}
|
||||
#endif /* WITH_PKCS11_PROVIDER */
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_rsa,
|
||||
@@ -238,6 +270,11 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_ecdsa_key_521,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#ifdef WITH_PKCS11_PROVIDER
|
||||
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_ed25519,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* WITH_PKCS11_PROVIDER */
|
||||
};
|
||||
|
||||
/* Do not use system openssl.cnf for the pkcs11 uri tests.
|
||||
@@ -246,7 +283,7 @@ int torture_run_tests(void) {
|
||||
* tokens, causing unexpected failures.
|
||||
* Make sure this comes before ssh_init(), which initializes OpenSSL!
|
||||
*/
|
||||
setenv("OPENSSL_CONF", "/dev/null", 1);
|
||||
setenv("OPENSSL_CONF", SOURCEDIR "/tests/etc/openssl.cnf", 1);
|
||||
|
||||
ssh_init();
|
||||
torture_filter_tests(tests);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "torture_cmocka.h"
|
||||
|
||||
#define LIBSSH_STATIC
|
||||
|
||||
@@ -143,6 +144,48 @@ static void torture_connect_ipv6(void **state) {
|
||||
assert_ssh_return_code(session, rc);
|
||||
}
|
||||
|
||||
static void torture_connect_addrfamily(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
|
||||
struct aftest {
|
||||
enum ssh_address_family_options_e family;
|
||||
char const *host;
|
||||
int return_code;
|
||||
};
|
||||
static struct aftest aftests[] = {
|
||||
{SSH_ADDRESS_FAMILY_ANY, "afboth", SSH_OK},
|
||||
{SSH_ADDRESS_FAMILY_INET, "afboth", SSH_OK},
|
||||
{SSH_ADDRESS_FAMILY_INET6, "afboth", SSH_OK},
|
||||
{SSH_ADDRESS_FAMILY_ANY, "afinet", SSH_OK},
|
||||
{SSH_ADDRESS_FAMILY_INET, "afinet", SSH_OK},
|
||||
{SSH_ADDRESS_FAMILY_INET6, "afinet", SSH_ERROR},
|
||||
{SSH_ADDRESS_FAMILY_ANY, "afinet6", SSH_OK},
|
||||
{SSH_ADDRESS_FAMILY_INET, "afinet6", SSH_ERROR},
|
||||
{SSH_ADDRESS_FAMILY_INET6, "afinet6", SSH_OK},
|
||||
};
|
||||
|
||||
int aftest_count = sizeof(aftests) / sizeof(aftests[0]);
|
||||
for (int i = 0; i < aftest_count; ++i) {
|
||||
struct aftest const *t = &aftests[i];
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_ADDRESS_FAMILY, &t->family);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_HOST, t->host);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
do {
|
||||
rc = ssh_connect(session);
|
||||
} while (rc == SSH_AGAIN);
|
||||
|
||||
assert_ssh_return_code_equal(session, rc, t->return_code);
|
||||
ssh_disconnect(session);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* This does not work with socket_wrapper */
|
||||
static void torture_connect_timeout(void **state) {
|
||||
struct torture_state *s = *state;
|
||||
@@ -329,6 +372,9 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_connect_ipv6,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_connect_addrfamily,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_connect_double,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
295
tests/client/torture_gssapi_key_exchange.c
Normal file
295
tests/client/torture_gssapi_key_exchange.c
Normal file
@@ -0,0 +1,295 @@
|
||||
#include "config.h"
|
||||
|
||||
#define LIBSSH_STATIC
|
||||
|
||||
#include "libssh/crypto.h"
|
||||
#include "torture.h"
|
||||
#include <libssh/libssh.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <gssapi.h>
|
||||
#include <pwd.h>
|
||||
|
||||
static int sshd_setup(void **state)
|
||||
{
|
||||
torture_setup_sshd_server(state, false);
|
||||
torture_update_sshd_config(state,
|
||||
"GSSAPIAuthentication yes\n"
|
||||
"GSSAPIKeyExchange yes\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshd_teardown(void **state)
|
||||
{
|
||||
assert_non_null(state);
|
||||
|
||||
torture_teardown_sshd_server(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
struct passwd *pwd = NULL;
|
||||
int rc;
|
||||
bool b = false;
|
||||
|
||||
pwd = getpwnam("bob");
|
||||
assert_non_null(pwd);
|
||||
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
|
||||
|
||||
/* Make sure no other configuration options from system will get used */
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
assert_non_null(s);
|
||||
|
||||
ssh_disconnect(s->ssh.session);
|
||||
ssh_free(s->ssh.session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool t = true;
|
||||
|
||||
/* Valid */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(session, rc);
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_no_tgt(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool t = true;
|
||||
|
||||
/* Don't run kinit */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
/* No TGT */
|
||||
"");
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
assert_false(ssh_kex_is_gss(session->current_crypto));
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_alg(void **state,
|
||||
const char *kex_string,
|
||||
enum ssh_key_exchange_e kex_type)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool t = true;
|
||||
|
||||
/* Valid */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_options_set(s->ssh.session,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
|
||||
kex_string);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
assert_int_equal(session->current_crypto->kex_type, kex_type);
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_gss_group14_sha256(void **state)
|
||||
{
|
||||
torture_gssapi_key_exchange_alg(state,
|
||||
"gss-group14-sha256-",
|
||||
SSH_GSS_KEX_DH_GROUP14_SHA256);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_gss_group16_sha512(void **state)
|
||||
{
|
||||
torture_gssapi_key_exchange_alg(state,
|
||||
"gss-group16-sha512-",
|
||||
SSH_GSS_KEX_DH_GROUP16_SHA512);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_gss_nistp256_sha256(void **state)
|
||||
{
|
||||
torture_gssapi_key_exchange_alg(state,
|
||||
"gss-nistp256-sha256-",
|
||||
SSH_GSS_KEX_ECDH_NISTP256_SHA256);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_gss_curve25519_sha256(void **state)
|
||||
{
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
torture_gssapi_key_exchange_alg(state,
|
||||
"gss-curve25519-sha256-",
|
||||
SSH_GSS_KEX_CURVE25519_SHA256);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_auth(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool t = true;
|
||||
|
||||
/* Valid */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
rc = ssh_userauth_gssapi_keyex(session);
|
||||
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_no_auth(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool f = false;
|
||||
|
||||
/* Valid */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
/* Don't do GSSAPI Key Exchange */
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &f);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
/* Still try to do "gssapi-keyex" auth */
|
||||
rc = ssh_userauth_gssapi_keyex(session);
|
||||
assert_int_equal(rc, SSH_AUTH_ERROR);
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_no_tgt,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_gssapi_key_exchange_gss_group14_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_gssapi_key_exchange_gss_group16_sha512,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_gssapi_key_exchange_gss_nistp256_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_gssapi_key_exchange_gss_curve25519_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_auth,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_no_auth,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
torture_filter_tests(tests);
|
||||
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
|
||||
ssh_finalize();
|
||||
|
||||
return rc;
|
||||
}
|
||||
169
tests/client/torture_gssapi_key_exchange_null.c
Normal file
169
tests/client/torture_gssapi_key_exchange_null.c
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "config.h"
|
||||
|
||||
#define LIBSSH_STATIC
|
||||
|
||||
#include "torture.h"
|
||||
#include <libssh/libssh.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <gssapi.h>
|
||||
#include <pwd.h>
|
||||
|
||||
static int sshd_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = NULL;
|
||||
torture_setup_sshd_server(state, false);
|
||||
|
||||
s = *state;
|
||||
s->disable_hostkeys = true;
|
||||
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
torture_update_sshd_config(state,
|
||||
"GSSAPIAuthentication yes\n"
|
||||
"GSSAPIKeyExchange yes\n");
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshd_teardown(void **state)
|
||||
{
|
||||
assert_non_null(state);
|
||||
|
||||
torture_teardown_sshd_server(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
struct passwd *pwd = NULL;
|
||||
int rc;
|
||||
bool b = false;
|
||||
|
||||
pwd = getpwnam("bob");
|
||||
assert_non_null(pwd);
|
||||
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
|
||||
|
||||
/* Make sure no other configuration options from system will get used */
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
assert_non_null(s);
|
||||
|
||||
ssh_disconnect(s->ssh.session);
|
||||
ssh_free(s->ssh.session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_null(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool t = true;
|
||||
|
||||
/* Valid */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
assert_string_equal(session->current_crypto->kex_methods[SSH_HOSTKEYS],
|
||||
"null");
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
static void torture_gssapi_key_exchange_null_pubkey_auth(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
bool t = true;
|
||||
|
||||
/* Valid */
|
||||
torture_setup_kdc_server(
|
||||
state,
|
||||
"kadmin.local addprinc -randkey host/server.libssh.site \n"
|
||||
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
|
||||
"kadmin.local addprinc -pw bar alice \n"
|
||||
"kadmin.local list_principals",
|
||||
|
||||
"echo bar | kinit alice");
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
assert_string_equal(session->current_crypto->kex_methods[SSH_HOSTKEYS],
|
||||
"null");
|
||||
|
||||
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
|
||||
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||
|
||||
torture_teardown_kdc_server(state);
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_null,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_null_pubkey_auth,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
torture_filter_tests(tests);
|
||||
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
|
||||
ssh_finalize();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -5,3 +5,8 @@
|
||||
|
||||
123.0.0.11 testing
|
||||
fd00::5357:5f0a testing
|
||||
|
||||
127.0.0.10 afboth
|
||||
fd00::5357:5f0a afboth
|
||||
127.0.0.10 afinet
|
||||
fd00::5357:5f0a afinet6
|
||||
|
||||
11
tests/etc/openssl.cnf
Normal file
11
tests/etc/openssl.cnf
Normal file
@@ -0,0 +1,11 @@
|
||||
openssl_conf = openssl_init
|
||||
[openssl_init]
|
||||
providers = provider_sect
|
||||
[provider_sect]
|
||||
default = default_sect
|
||||
pkcs11 = pkcs11_sect
|
||||
[default_sect]
|
||||
activate = 1
|
||||
[pkcs11_sect]
|
||||
activate = 1
|
||||
pkcs11-module-assume-fips = true
|
||||
@@ -88,8 +88,19 @@ target_link_libraries(sntrup761_override
|
||||
set(SNTRUP761_OVERRIDE_LIBRARY
|
||||
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}sntrup761_override${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
|
||||
# mlkem768_override
|
||||
add_library(mlkem768_override SHARED
|
||||
mlkem768_override.c
|
||||
${libssh_SOURCE_DIR}/src/external/libcrux_mlkem768_sha3.c
|
||||
${override_src}
|
||||
)
|
||||
target_link_libraries(mlkem768_override
|
||||
PRIVATE ${override_libs})
|
||||
set(MLKEM768_OVERRIDE_LIBRARY
|
||||
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mlkem768_override${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
|
||||
set(OVERRIDE_LIBRARIES
|
||||
${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}:${SNTRUP761_OVERRIDE_LIBRARY}
|
||||
${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}:${SNTRUP761_OVERRIDE_LIBRARY}:${MLKEM768_OVERRIDE_LIBRARY}
|
||||
)
|
||||
|
||||
if (WITH_MBEDTLS)
|
||||
@@ -107,6 +118,7 @@ if (WITH_MBEDTLS)
|
||||
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=1")
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
|
||||
elseif (WITH_GCRYPT)
|
||||
if (HAVE_GCRYPT_CHACHA_POLY)
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
|
||||
@@ -122,6 +134,11 @@ elseif (WITH_GCRYPT)
|
||||
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=0")
|
||||
if(HAVE_GCRYPT_MLKEM)
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=0")
|
||||
else()
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
|
||||
endif()
|
||||
else ()
|
||||
if (HAVE_OPENSSL_EVP_CHACHA20)
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
|
||||
@@ -131,6 +148,11 @@ else ()
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=0")
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=0")
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=1")
|
||||
if(HAVE_OPENSSL_MLKEM)
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=0")
|
||||
else()
|
||||
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (NOT OSX)
|
||||
@@ -162,6 +184,7 @@ foreach(_OVERRIDE_TEST ${LIBSSH_OVERRIDE_TESTS})
|
||||
ed25519_override
|
||||
curve25519_override
|
||||
sntrup761_override
|
||||
mlkem768_override
|
||||
)
|
||||
|
||||
if (OSX)
|
||||
|
||||
83
tests/external_override/mlkem768_override.c
Normal file
83
tests/external_override/mlkem768_override.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2021 - 2025 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Anderson Toshiyuki Sasaki
|
||||
* 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, 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,
|
||||
* see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libssh/priv.h>
|
||||
|
||||
#include "libssh/mlkem_native.h"
|
||||
#include "mlkem768_override.h"
|
||||
|
||||
static bool internal_function_called = false;
|
||||
|
||||
libcrux_ml_kem_mlkem768_MlKem768KeyPair
|
||||
__wrap_libcrux_ml_kem_mlkem768_portable_generate_key_pair(
|
||||
uint8_t randomness[64U])
|
||||
{
|
||||
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
|
||||
internal_function_called = true;
|
||||
return libcrux_ml_kem_mlkem768_portable_generate_key_pair(randomness);
|
||||
}
|
||||
|
||||
bool __wrap_libcrux_ml_kem_mlkem768_portable_validate_public_key(
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key)
|
||||
{
|
||||
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
|
||||
internal_function_called = true;
|
||||
return libcrux_ml_kem_mlkem768_portable_validate_public_key(public_key);
|
||||
}
|
||||
|
||||
tuple_c2 __wrap_libcrux_ml_kem_mlkem768_portable_encapsulate(
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
|
||||
uint8_t randomness[32U])
|
||||
{
|
||||
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
|
||||
internal_function_called = true;
|
||||
return libcrux_ml_kem_mlkem768_portable_encapsulate(public_key, randomness);
|
||||
}
|
||||
|
||||
void __wrap_libcrux_ml_kem_mlkem768_portable_decapsulate(
|
||||
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
|
||||
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
|
||||
uint8_t ret[32U])
|
||||
{
|
||||
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
|
||||
internal_function_called = true;
|
||||
return libcrux_ml_kem_mlkem768_portable_decapsulate(private_key,
|
||||
ciphertext,
|
||||
ret);
|
||||
}
|
||||
|
||||
bool internal_mlkem768_function_called(void)
|
||||
{
|
||||
return internal_function_called;
|
||||
}
|
||||
|
||||
void reset_mlkem768_function_called(void)
|
||||
{
|
||||
internal_function_called = false;
|
||||
}
|
||||
43
tests/external_override/mlkem768_override.h
Normal file
43
tests/external_override/mlkem768_override.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2021 - 2025 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Anderson Toshiyuki Sasaki
|
||||
* 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, 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,
|
||||
* see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "libssh/mlkem_native.h"
|
||||
|
||||
libcrux_ml_kem_mlkem768_MlKem768KeyPair
|
||||
__wrap_libcrux_ml_kem_mlkem768_portable_generate_key_pair(
|
||||
uint8_t randomness[64U]);
|
||||
|
||||
bool __wrap_libcrux_ml_kem_mlkem768_portable_validate_public_key(
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
|
||||
|
||||
tuple_c2 __wrap_libcrux_ml_kem_mlkem768_portable_encapsulate(
|
||||
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
|
||||
uint8_t randomness[32U]);
|
||||
|
||||
void __wrap_libcrux_ml_kem_mlkem768_portable_decapsulate(
|
||||
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
|
||||
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
|
||||
uint8_t ret[32U]);
|
||||
|
||||
bool internal_mlkem768_function_called(void);
|
||||
void reset_mlkem768_function_called(void);
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "chacha20_override.h"
|
||||
#include "curve25519_override.h"
|
||||
#include "ed25519_override.h"
|
||||
#include "mlkem768_override.h"
|
||||
#include "poly1305_override.h"
|
||||
#include "sntrup761_override.h"
|
||||
|
||||
@@ -119,6 +120,8 @@ static int session_setup(void **state)
|
||||
reset_poly1305_function_called();
|
||||
reset_curve25519_function_called();
|
||||
reset_ed25519_function_called();
|
||||
reset_sntrup761_function_called();
|
||||
reset_mlkem768_function_called();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -324,6 +327,64 @@ torture_override_ecdh_sntrup761x25519_sha512(void **state)
|
||||
}
|
||||
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
|
||||
|
||||
#ifdef OPENSSH_MLKEM768X25519_SHA256
|
||||
static void torture_override_mlkem768x25519_sha256(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
bool internal_curve25519_called;
|
||||
bool internal_mlkem768_called;
|
||||
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
|
||||
test_algorithm(s->ssh.session,
|
||||
"mlkem768x25519-sha256",
|
||||
NULL, /* cipher */
|
||||
NULL /* hostkey */);
|
||||
|
||||
internal_curve25519_called = internal_curve25519_function_called();
|
||||
internal_mlkem768_called = internal_mlkem768_function_called();
|
||||
|
||||
#if SHOULD_CALL_INTERNAL_MLKEM
|
||||
assert_true(internal_mlkem768_called);
|
||||
#else
|
||||
assert_false(internal_mlkem768_called);
|
||||
#endif
|
||||
|
||||
#if SHOULD_CALL_INTERNAL_CURVE25519
|
||||
assert_true(internal_curve25519_called);
|
||||
#else
|
||||
assert_false(internal_curve25519_called);
|
||||
#endif
|
||||
}
|
||||
#endif /* OPENSSH_MLKEM768X25519_SHA256 */
|
||||
|
||||
#ifdef OPENSSH_MLKEM768NISTP256_SHA256
|
||||
static void torture_override_mlkem768nistp256_sha256(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
bool internal_mlkem768_called;
|
||||
|
||||
if (ssh_fips_mode()) {
|
||||
skip();
|
||||
}
|
||||
|
||||
test_algorithm(s->ssh.session,
|
||||
"mlkem768nistp256-sha256",
|
||||
NULL, /* cipher */
|
||||
NULL /* hostkey */);
|
||||
|
||||
internal_mlkem768_called = internal_mlkem768_function_called();
|
||||
|
||||
#if SHOULD_CALL_INTERNAL_MLKEM
|
||||
assert_true(internal_mlkem768_called);
|
||||
#else
|
||||
assert_false(internal_mlkem768_called);
|
||||
#endif
|
||||
}
|
||||
#endif /* OPENSSH_MLKEM768NISTP256_SHA256 */
|
||||
|
||||
#ifdef OPENSSH_SSH_ED25519
|
||||
static void torture_override_ed25519(void **state)
|
||||
{
|
||||
@@ -378,6 +439,16 @@ int torture_run_tests(void)
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
|
||||
#ifdef OPENSSH_MLKEM768X25519_SHA256
|
||||
cmocka_unit_test_setup_teardown(torture_override_mlkem768x25519_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* OPENSSH_MLKEM768X25519_SHA256 */
|
||||
#ifdef OPENSSH_MLKEM768NISTP256_SHA256
|
||||
cmocka_unit_test_setup_teardown(torture_override_mlkem768nistp256_sha256,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
#endif /* OPENSSH_MLKEM768NISTP256_SHA256 */
|
||||
#ifdef OPENSSH_SSH_ED25519
|
||||
cmocka_unit_test_setup_teardown(torture_override_ed25519,
|
||||
session_setup,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -211,3 +213,43 @@ statx(int dirfd,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int is_file_blocked(const char *pathname)
|
||||
{
|
||||
if (pathname == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *blocked_files[] = {
|
||||
/* Block for torture_gssapi_server_key_exchange_null */
|
||||
"/etc/ssh/ssh_host_ecdsa_key",
|
||||
"/etc/ssh/ssh_host_rsa_key",
|
||||
"/etc/ssh/ssh_host_ed25519_key",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(blocked_files) / sizeof(blocked_files[0]);
|
||||
i++) {
|
||||
if (strcmp(pathname, blocked_files[i]) == 0) {
|
||||
errno = ENOENT; /* No such file or directory */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WRAP_FOPEN(func_name) \
|
||||
FILE *func_name(const char *pathname, const char *mode) \
|
||||
{ \
|
||||
typedef FILE *(*orig_func_t)(const char *pathname, const char *mode); \
|
||||
static orig_func_t orig_func = NULL; \
|
||||
if (orig_func == NULL) { \
|
||||
orig_func = (orig_func_t)dlsym(RTLD_NEXT, #func_name); \
|
||||
} \
|
||||
if (is_file_blocked(pathname)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
return orig_func(pathname, mode); \
|
||||
}
|
||||
|
||||
WRAP_FOPEN(fopen)
|
||||
WRAP_FOPEN(fopen64)
|
||||
|
||||
1
tests/keys/pkcs11/id_pkcs11_ed25519_openssh.pub
Normal file
1
tests/keys/pkcs11/id_pkcs11_ed25519_openssh.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN1aDCZfQOPArIsxnS8uMipbQtjM/FD77+hpkyo9i95W
|
||||
@@ -24,10 +24,20 @@
|
||||
"-o PubkeyAcceptedKeyTypes=" \
|
||||
OPENSSH_KEYS
|
||||
|
||||
#ifdef HAVE_SK_DUMMY
|
||||
#define SECURITY_KEY_PROVIDER \
|
||||
"-oSecurityKeyProvider=\"" SK_DUMMY_LIBRARY_PATH "\" "
|
||||
#else
|
||||
#define SECURITY_KEY_PROVIDER ""
|
||||
#endif
|
||||
|
||||
/* GlobalKnownHostsFile is just a place holder and won't actually set the hostkey */
|
||||
#define OPENSSH_CMD_START(hostkey_algos) \
|
||||
OPENSSH_BINARY " " \
|
||||
"-o UserKnownHostsFile=/dev/null " \
|
||||
"-o StrictHostKeyChecking=no " \
|
||||
SECURITY_KEY_PROVIDER \
|
||||
"-o GlobalKnownHostsFile=%s " \
|
||||
"-F /dev/null " \
|
||||
hostkey_algos " " \
|
||||
OPENSSH_PKACCEPTED_TYPES " " \
|
||||
@@ -64,9 +74,11 @@
|
||||
#define DROPBEAR_BINARY DROPBEAR_EXECUTABLE
|
||||
#define DROPBEAR_KEYGEN "dropbearkey"
|
||||
|
||||
/* HostKeyAlias is just a place holder and won't actually set the hostkey */
|
||||
#define DROPBEAR_CMD_START \
|
||||
DROPBEAR_BINARY " " \
|
||||
"-y -y " \
|
||||
"-o HostKeyAlias=%s " \
|
||||
"-i " CLIENT_ID_FILE " " \
|
||||
"1> %s.out " \
|
||||
"2> %s.err "
|
||||
@@ -87,4 +99,22 @@
|
||||
#define DROPBEAR_MAC_CMD(macs) \
|
||||
DROPBEAR_CMD_START "-m " macs " " DROPBEAR_CMD_END
|
||||
|
||||
/* PuTTY */
|
||||
|
||||
#define PUTTY_BINARY PUTTY_EXECUTABLE
|
||||
#define PUTTY_KEYGEN PUTTYGEN_EXECUTABLE
|
||||
|
||||
#define PUTTY_CMD_START \
|
||||
PUTTY_BINARY " " \
|
||||
"-batch -ssh -P 1234 " \
|
||||
"-i " CLIENT_ID_FILE " " \
|
||||
"-hostkey $(" OPENSSH_KEYGEN \
|
||||
" -l -f %s.pub -E md5 | awk '{print $2}' | cut -d: -f2-) " \
|
||||
"1> %s.out 2> %s.err "
|
||||
|
||||
#define PUTTY_CMD_END " localhost ls"
|
||||
|
||||
#define PUTTY_CMD \
|
||||
PUTTY_CMD_START PUTTY_CMD_END
|
||||
|
||||
#endif /* __PKD_CLIENT_H__ */
|
||||
|
||||
@@ -215,81 +215,81 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
|
||||
*/
|
||||
|
||||
#define PKDTESTS_DEFAULT_FIPS(f, client, cmd) \
|
||||
f(client, rsa_default, cmd, setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown)
|
||||
f(client, rsa_default, cmd, setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
#define PKDTESTS_DEFAULT(f, client, cmd) \
|
||||
/* Default passes by server key type. */ \
|
||||
PKDTESTS_DEFAULT_FIPS(f, client, cmd) \
|
||||
f(client, ed25519_default, cmd, setup_ed25519, teardown)
|
||||
f(client, ed25519_default, cmd, setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
|
||||
#define GEX_SHA256 "diffie-hellman-group-exchange-sha256"
|
||||
#define GEX_SHA1 "diffie-hellman-group-exchange-sha1"
|
||||
|
||||
#if defined(WITH_GEX)
|
||||
#define PKDTESTS_KEX_FIPS(f, client, kexcmd) \
|
||||
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown) \
|
||||
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown) \
|
||||
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown) \
|
||||
f(client, rsa_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
#else /* !defined(WITH_GEX) */
|
||||
#define PKDTESTS_KEX_FIPS(f, client, kexcmd) \
|
||||
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown) \
|
||||
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown) \
|
||||
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
#endif
|
||||
|
||||
#ifdef OPENSSH_SNTRUP761X25519_SHA512_OPENSSH_COM
|
||||
#define SNTRUP_OPENSSH_NAME "sntrup761x25519-sha512@openssh.com"
|
||||
#define PKDTESTS_KEX_SNTRUP761_OPENSSH(f, client, kexcmd) \
|
||||
f(client, rsa_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_521, teardown) \
|
||||
f(client, ed25519_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ed25519, teardown)
|
||||
f(client, rsa_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ed25519_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#else
|
||||
#define PKDTESTS_KEX_SNTRUP761_OPENSSH(f, client, kexcmd)
|
||||
#endif
|
||||
@@ -297,44 +297,44 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
|
||||
#ifdef OPENSSH_SNTRUP761X25519_SHA512
|
||||
#define SNTRUP_NAME "sntrup761x25519-sha512"
|
||||
#define PKDTESTS_KEX_SNTRUP761(f, client, kexcmd) \
|
||||
f(client, rsa_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_521, teardown) \
|
||||
f(client, ed25519_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ed25519, teardown)
|
||||
f(client, rsa_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ed25519_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#else
|
||||
#define PKDTESTS_KEX_SNTRUP761(f, client, kexcmd)
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
|
||||
#if defined(OPENSSH_MLKEM768X25519_SHA256)
|
||||
#define PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd) \
|
||||
f(client, rsa_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_521, teardown) \
|
||||
f(client, ed25519_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ed25519, teardown)
|
||||
f(client, rsa_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ed25519_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#else
|
||||
#define PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd)
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
|
||||
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
|
||||
#define PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd) \
|
||||
f(client, rsa_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_521, teardown) \
|
||||
f(client, ed25519_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ed25519, teardown)
|
||||
f(client, rsa_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ed25519_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#else
|
||||
#define PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd)
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
|
||||
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
|
||||
#define PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd) \
|
||||
f(client, rsa_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_521, teardown) \
|
||||
f(client, ed25519_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ed25519, teardown)
|
||||
f(client, rsa_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ed25519_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#else
|
||||
#define PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd)
|
||||
#endif
|
||||
@@ -346,146 +346,146 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
|
||||
PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd) \
|
||||
PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd) \
|
||||
PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd) \
|
||||
f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256"), setup_rsa, teardown) \
|
||||
f(client, rsa_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown) \
|
||||
f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
#if defined(WITH_GEX)
|
||||
/* GEX_SHA256 is included in PKDTESTS_KEX_FIPS if available */
|
||||
#define PKDTESTS_KEX(f, client, kexcmd) \
|
||||
/* Kex algorithms. */ \
|
||||
PKDTESTS_KEX_COMMON(f, client, kexcmd) \
|
||||
f(client, rsa_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
#else
|
||||
#define PKDTESTS_KEX(f, client, kexcmd) \
|
||||
/* Kex algorithms. */ \
|
||||
f(client, ed25519_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ed25519, teardown) \
|
||||
f(client, ed25519_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ed25519, teardown)
|
||||
f(client, ed25519_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#endif
|
||||
|
||||
#define PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
|
||||
f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown) \
|
||||
f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
#define PKDTESTS_CIPHER_FIPS(f, client, ciphercmd) \
|
||||
PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
|
||||
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \
|
||||
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
#define CHACHA20 "chacha20-poly1305@openssh.com"
|
||||
#define PKDTESTS_CIPHER_CHACHA(f, client, ciphercmd) \
|
||||
f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
|
||||
f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
|
||||
f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
#define PKDTESTS_CIPHER(f, client, ciphercmd) \
|
||||
/* Ciphers. */ \
|
||||
PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
|
||||
PKDTESTS_CIPHER_CHACHA(f, client, ciphercmd) \
|
||||
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes256_ctr, ciphercmd("aes256-ctr"), setup_ed25519, teardown)
|
||||
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes256_ctr, ciphercmd("aes256-ctr"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
|
||||
#define AES128_GCM "aes128-gcm@openssh.com"
|
||||
#define AES256_GCM "aes256-gcm@openssh.com"
|
||||
|
||||
#define PKDTESTS_CIPHER_OPENSSHONLY_FIPS(f, client, ciphercmd) \
|
||||
f(client, rsa_aes128_gcm, ciphercmd(AES128_GCM), setup_rsa, teardown) \
|
||||
f(client, rsa_aes256_gcm, ciphercmd(AES256_GCM), setup_rsa, teardown) \
|
||||
f(client, ecdsa_256_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_aes128_gcm, ciphercmd(AES128_GCM), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes256_gcm, ciphercmd(AES256_GCM), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ecdsa_256_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
#define PKDTESTS_CIPHER_OPENSSHONLY(f, client, ciphercmd) \
|
||||
/* Ciphers. */ \
|
||||
PKDTESTS_CIPHER_OPENSSHONLY_FIPS(f, client, ciphercmd) \
|
||||
f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown) \
|
||||
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \
|
||||
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
|
||||
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
|
||||
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
|
||||
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes256_cbc, ciphercmd("aes256-cbc"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown) \
|
||||
f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown) \
|
||||
f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown)
|
||||
f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes256_cbc, ciphercmd("aes256-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
|
||||
|
||||
|
||||
#define PKDTESTS_MAC_FIPS_BASE(f, client, maccmd) \
|
||||
f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown) \
|
||||
f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown)
|
||||
f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
|
||||
|
||||
#define PKDTESTS_MAC_FIPS_SHA1(f, client, maccmd) \
|
||||
f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown) \
|
||||
f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown)
|
||||
f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
|
||||
|
||||
#ifdef DROPBEAR_SUPPORTS_HMAC_SHA1
|
||||
#define PKDTESTS_MAC_FIPS(f, client, maccmd) \
|
||||
PKDTESTS_MAC_FIPS_BASE(f, client, maccmd) \
|
||||
PKDTESTS_MAC_FIPS_SHA1(f, client, maccmd) \
|
||||
f(client, ed25519_hmac_sha1, maccmd("hmac-sha1"), setup_ed25519, teardown)
|
||||
f(client, ed25519_hmac_sha1, maccmd("hmac-sha1"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
#define PKDTESTS_MAC_OPENSSHONLY_FIPS_SHA1(f, client, maccmd)
|
||||
#else
|
||||
#define PKDTESTS_MAC_FIPS(f, client, maccmd) \
|
||||
@@ -496,41 +496,41 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
|
||||
|
||||
#define PKDTESTS_MAC_OPENSSHONLY_FIPS(f, client, maccmd) \
|
||||
PKDTESTS_MAC_OPENSSHONLY_FIPS_SHA1(f, client, maccmd) \
|
||||
f(client, ecdsa_256_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_256_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_384_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown) \
|
||||
f(client, ecdsa_521_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_521, teardown) \
|
||||
f(client, rsa_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_rsa, teardown) \
|
||||
f(client, rsa_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_rsa, teardown) \
|
||||
f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown) \
|
||||
f(client, rsa_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_rsa, teardown)
|
||||
f(client, ecdsa_256_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_256_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
|
||||
f(client, ecdsa_384_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_384_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
|
||||
f(client, ecdsa_521_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, ecdsa_521_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
|
||||
f(client, rsa_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
|
||||
|
||||
#define PKDTESTS_MAC(f, client, maccmd) \
|
||||
/* MACs. */ \
|
||||
PKDTESTS_MAC_FIPS(f, client, maccmd) \
|
||||
f(client, ed25519_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ed25519, teardown)
|
||||
f(client, ed25519_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
|
||||
#define PKDTESTS_MAC_OPENSSHONLY(f, client, maccmd) \
|
||||
PKDTESTS_MAC_OPENSSHONLY_FIPS(f, client, maccmd) \
|
||||
f(client, ed25519_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ed25519, teardown) \
|
||||
f(client, ed25519_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ed25519, teardown)
|
||||
f(client, ed25519_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
|
||||
f(client, ed25519_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
|
||||
|
||||
|
||||
#define PKDTESTS_HOSTKEY_OPENSSHONLY_FIPS(f, client, hkcmd) \
|
||||
f(client, rsa_sha2_256, hkcmd("rsa-sha2-256"), setup_rsa, teardown) \
|
||||
f(client, rsa_sha2_512, hkcmd("rsa-sha2-512"), setup_rsa, teardown) \
|
||||
f(client, rsa_sha2_256_512, hkcmd("rsa-sha2-256,rsa-sha2-512"), setup_rsa, teardown) \
|
||||
f(client, rsa_sha2_512_256, hkcmd("rsa-sha2-512,rsa-sha2-256"), setup_rsa, teardown)
|
||||
f(client, rsa_sha2_256, hkcmd("rsa-sha2-256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_sha2_512, hkcmd("rsa-sha2-512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_sha2_256_512, hkcmd("rsa-sha2-256,rsa-sha2-512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
|
||||
f(client, rsa_sha2_512_256, hkcmd("rsa-sha2-512,rsa-sha2-256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
|
||||
|
||||
#define PKDTESTS_HOSTKEY_OPENSSHONLY(f, client, hkcmd) \
|
||||
PKDTESTS_HOSTKEY_OPENSSHONLY_FIPS(f, client, hkcmd)
|
||||
@@ -567,12 +567,12 @@ static void torture_pkd_runtest(const char *testname,
|
||||
* defined here to result in distinct output when running the tests.
|
||||
*/
|
||||
|
||||
#define emit_keytest(client, testname, sshcmd, setup, teardown) \
|
||||
#define emit_keytest(client, testname, sshcmd, setup, teardown, hostkey) \
|
||||
static void torture_pkd_## client ## _ ## testname(void **state) { \
|
||||
const char *tname = "torture_pkd_" #client "_" #testname; \
|
||||
char testcmd[2048] = { 0 }; \
|
||||
(void) state; \
|
||||
snprintf(&testcmd[0], sizeof(testcmd), sshcmd, tname, tname); \
|
||||
snprintf(&testcmd[0], sizeof(testcmd), sshcmd, hostkey, tname, tname); \
|
||||
torture_pkd_runtest(tname, testcmd); \
|
||||
}
|
||||
|
||||
@@ -615,6 +615,28 @@ PKDTESTS_MAC(emit_keytest, openssh_ed, OPENSSH_MAC_CMD)
|
||||
PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_ed, OPENSSH_MAC_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
|
||||
#ifdef HAVE_SK_DUMMY
|
||||
#define CLIENT_ID_FILE OPENSSH_ECDSA_SK_TESTKEY
|
||||
PKDTESTS_DEFAULT(emit_keytest, openssh_ec_sk, OPENSSH_CMD)
|
||||
PKDTESTS_DEFAULT(emit_keytest, openssh_cert_ec_sk, OPENSSH_CERT_CMD)
|
||||
PKDTESTS_KEX(emit_keytest, openssh_ec_sk, OPENSSH_KEX_CMD)
|
||||
PKDTESTS_CIPHER(emit_keytest, openssh_ec_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_ec_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_keytest, openssh_ec_sk, OPENSSH_MAC_CMD)
|
||||
PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_ec_sk, OPENSSH_MAC_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
|
||||
#define CLIENT_ID_FILE OPENSSH_ED25519_SK_TESTKEY
|
||||
PKDTESTS_DEFAULT(emit_keytest, openssh_ed_sk, OPENSSH_CMD)
|
||||
PKDTESTS_DEFAULT(emit_keytest, openssh_cert_ed_sk, OPENSSH_CERT_CMD)
|
||||
PKDTESTS_KEX(emit_keytest, openssh_ed_sk, OPENSSH_KEX_CMD)
|
||||
PKDTESTS_CIPHER(emit_keytest, openssh_ed_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_ed_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_keytest, openssh_ed_sk, OPENSSH_MAC_CMD)
|
||||
PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_ed_sk, OPENSSH_MAC_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
#endif /* HAVE_SK_DUMMY */
|
||||
|
||||
#define CLIENT_ID_FILE DROPBEAR_RSA_TESTKEY
|
||||
PKDTESTS_DEFAULT(emit_keytest, dropbear_rsa, DROPBEAR_CMD)
|
||||
PKDTESTS_CIPHER(emit_keytest, dropbear_rsa, DROPBEAR_CIPHER_CMD)
|
||||
@@ -633,23 +655,35 @@ PKDTESTS_CIPHER(emit_keytest, dropbear_ed, DROPBEAR_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_keytest, dropbear_ed, DROPBEAR_MAC_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
|
||||
#define CLIENT_ID_FILE PUTTY_RSA_PPK_TESTKEY
|
||||
PKDTESTS_DEFAULT(emit_keytest, putty_rsa, PUTTY_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
|
||||
#define CLIENT_ID_FILE PUTTY_ED25519_PPK_TESTKEY
|
||||
PKDTESTS_DEFAULT(emit_keytest, putty_ed, PUTTY_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
|
||||
#define CLIENT_ID_FILE PUTTY_ECDSA256_PPK_TESTKEY
|
||||
PKDTESTS_DEFAULT(emit_keytest, putty_e256, PUTTY_CMD)
|
||||
#undef CLIENT_ID_FILE
|
||||
|
||||
/*
|
||||
* Define an array of testname strings mapped to their associated
|
||||
* test function. Enables running tests individually by name from
|
||||
* the command line.
|
||||
*/
|
||||
|
||||
#define emit_testmap(client, testname, sshcmd, setup, teardown) \
|
||||
#define emit_testmap(client, testname, sshcmd, setup, teardown, ...) \
|
||||
{ "torture_pkd_" #client "_" #testname, \
|
||||
emit_unit_test(client, testname, sshcmd, setup, teardown) },
|
||||
emit_unit_test(client, testname, sshcmd, setup, teardown, ##__VA_ARGS__) },
|
||||
|
||||
#define emit_unit_test(client, testname, sshcmd, setup, teardown) \
|
||||
#define emit_unit_test(client, testname, sshcmd, setup, teardown, ...) \
|
||||
cmocka_unit_test_setup_teardown(torture_pkd_ ## client ## _ ## testname, \
|
||||
torture_pkd_ ## setup, \
|
||||
torture_pkd_ ## teardown)
|
||||
|
||||
#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown) \
|
||||
emit_unit_test(client, testname, sshcmd, setup, teardown),
|
||||
#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown, ...) \
|
||||
emit_unit_test(client, testname, sshcmd, setup, teardown, ##__VA_ARGS__),
|
||||
|
||||
struct {
|
||||
const char *testname;
|
||||
@@ -696,8 +730,15 @@ struct {
|
||||
PKDTESTS_CIPHER(emit_testmap, dropbear_ed, DROPBEAR_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_testmap, dropbear_ed, DROPBEAR_MAC_CMD)
|
||||
|
||||
/* PuTTY */
|
||||
PKDTESTS_DEFAULT(emit_testmap, putty_rsa, PUTTY_CMD)
|
||||
|
||||
PKDTESTS_DEFAULT(emit_testmap, putty_e256, PUTTY_CMD)
|
||||
|
||||
PKDTESTS_DEFAULT(emit_testmap, putty_ed, PUTTY_CMD)
|
||||
|
||||
/* Noop */
|
||||
emit_testmap(client, noop, "", setup_noop, teardown)
|
||||
emit_testmap(client, noop, "", setup_noop, teardown, NULL)
|
||||
|
||||
/* NULL tail entry */
|
||||
{ .testname = NULL,
|
||||
@@ -738,6 +779,24 @@ static int pkd_run_tests(void) {
|
||||
PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD)
|
||||
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD)
|
||||
|
||||
#ifdef HAVE_SK_DUMMY
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ec_sk, OPENSSH_CMD)
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_cert_ec_sk, OPENSSH_CERT_CMD)
|
||||
PKDTESTS_KEX(emit_unit_test_comma, openssh_ec_sk, OPENSSH_KEX_CMD)
|
||||
PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ec_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ec_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_unit_test_comma, openssh_ec_sk, OPENSSH_MAC_CMD)
|
||||
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ec_sk, OPENSSH_MAC_CMD)
|
||||
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ed_sk, OPENSSH_CMD)
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_cert_ed_sk, OPENSSH_CERT_CMD)
|
||||
PKDTESTS_KEX(emit_unit_test_comma, openssh_ed_sk, OPENSSH_KEX_CMD)
|
||||
PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ed_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ed_sk, OPENSSH_CIPHER_CMD)
|
||||
PKDTESTS_MAC(emit_unit_test_comma, openssh_ed_sk, OPENSSH_MAC_CMD)
|
||||
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ed_sk, OPENSSH_MAC_CMD)
|
||||
#endif /* HAVE_SK_DUMMY */
|
||||
};
|
||||
|
||||
/* It is not possible to test hostkey and kex algorithms, because
|
||||
@@ -758,6 +817,14 @@ static int pkd_run_tests(void) {
|
||||
PKDTESTS_MAC(emit_unit_test_comma, dropbear_ed, DROPBEAR_MAC_CMD)
|
||||
};
|
||||
|
||||
const struct CMUnitTest putty_tests[] = {
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, putty_rsa, PUTTY_CMD)
|
||||
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, putty_e256, PUTTY_CMD)
|
||||
|
||||
PKDTESTS_DEFAULT(emit_unit_test_comma, putty_ed, PUTTY_CMD)
|
||||
};
|
||||
|
||||
const struct CMUnitTest openssh_fips_tests[] = {
|
||||
PKDTESTS_DEFAULT_FIPS(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD)
|
||||
PKDTESTS_DEFAULT_FIPS(emit_unit_test_comma, openssh_sha256_cert_rsa,
|
||||
@@ -778,12 +845,13 @@ static int pkd_run_tests(void) {
|
||||
};
|
||||
|
||||
const struct CMUnitTest noop_tests[] = {
|
||||
emit_unit_test(client, noop, "", setup_noop, teardown)
|
||||
emit_unit_test(client, noop, "", setup_noop, teardown, NULL)
|
||||
};
|
||||
|
||||
/* Test list is populated depending on which clients are enabled. */
|
||||
struct CMUnitTest all_tests[(sizeof(openssh_tests) / sizeof(openssh_tests[0])) +
|
||||
(sizeof(dropbear_tests) / sizeof(dropbear_tests[0])) +
|
||||
(sizeof(putty_tests) / sizeof(putty_tests[0])) +
|
||||
(sizeof(noop_tests) / sizeof(noop_tests[0]))];
|
||||
memset(&all_tests[0], 0x0, sizeof(all_tests));
|
||||
|
||||
@@ -807,6 +875,14 @@ static int pkd_run_tests(void) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_putty_client_enabled()) {
|
||||
setup_putty_client_keys();
|
||||
if (!ssh_fips_mode()) {
|
||||
memcpy(&all_tests[tindex], &putty_tests[0], sizeof(putty_tests));
|
||||
tindex += (sizeof(putty_tests) / sizeof(putty_tests[0]));
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&all_tests[tindex], &noop_tests[0], sizeof(noop_tests));
|
||||
tindex += (sizeof(noop_tests) / sizeof(noop_tests[0]));
|
||||
|
||||
@@ -859,6 +935,10 @@ static int pkd_run_tests(void) {
|
||||
cleanup_openssh_client_keys();
|
||||
}
|
||||
|
||||
if (is_putty_client_enabled()) {
|
||||
cleanup_putty_client_keys();
|
||||
}
|
||||
|
||||
/* Clean up any server keys that were generated. */
|
||||
cleanup_rsa_key();
|
||||
cleanup_ecdsa_keys();
|
||||
@@ -1078,4 +1158,4 @@ out_finalize:
|
||||
#endif
|
||||
out:
|
||||
return exit_code;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,21 @@ void setup_openssh_client_keys(void) {
|
||||
}
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SK_DUMMY
|
||||
setenv("SSH_SK_PROVIDER", SK_DUMMY_LIBRARY_PATH, 1);
|
||||
if (access(OPENSSH_ECDSA_SK_TESTKEY, F_OK) != 0) {
|
||||
rc = system_checked(OPENSSH_KEYGEN " -t ecdsa-sk -q -N \"\" -f "
|
||||
OPENSSH_ECDSA_SK_TESTKEY);
|
||||
}
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
if (access(OPENSSH_ED25519_SK_TESTKEY, F_OK) != 0) {
|
||||
rc = system_checked(OPENSSH_KEYGEN " -t ed25519-sk -q -N \"\" -f "
|
||||
OPENSSH_ED25519_SK_TESTKEY);
|
||||
}
|
||||
assert_int_equal(rc, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cleanup_openssh_client_keys(void) {
|
||||
@@ -165,6 +180,10 @@ void cleanup_openssh_client_keys(void) {
|
||||
if (!ssh_fips_mode()) {
|
||||
cleanup_key(OPENSSH_ED25519_TESTKEY);
|
||||
}
|
||||
#ifdef HAVE_SK_DUMMY
|
||||
cleanup_key(OPENSSH_ECDSA_SK_TESTKEY);
|
||||
cleanup_key(OPENSSH_ED25519_SK_TESTKEY);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setup_dropbear_client_keys(void)
|
||||
@@ -195,3 +214,56 @@ void cleanup_dropbear_client_keys(void)
|
||||
cleanup_key(DROPBEAR_ECDSA256_TESTKEY);
|
||||
cleanup_key(DROPBEAR_ED25519_TESTKEY);
|
||||
}
|
||||
|
||||
void setup_putty_client_keys(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* RSA Keys */
|
||||
if (access(PUTTY_RSA_TESTKEY, F_OK) != 0 ||
|
||||
access(PUTTY_RSA_PPK_TESTKEY, F_OK) != 0) {
|
||||
rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f "
|
||||
PUTTY_RSA_TESTKEY);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
rc = system_checked(PUTTY_KEYGEN " " PUTTY_RSA_TESTKEY
|
||||
" -O private -o " PUTTY_RSA_PPK_TESTKEY);
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
/* ECDSA 256 Keys */
|
||||
if (access(PUTTY_ECDSA256_TESTKEY, F_OK) != 0 ||
|
||||
access(PUTTY_ECDSA256_PPK_TESTKEY, F_OK) != 0) {
|
||||
rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f "
|
||||
PUTTY_ECDSA256_TESTKEY);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
rc = system_checked(PUTTY_KEYGEN " " PUTTY_ECDSA256_TESTKEY
|
||||
" -O private -o " PUTTY_ECDSA256_PPK_TESTKEY);
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
/* ED25519 Keys */
|
||||
if (access(PUTTY_ED25519_TESTKEY, F_OK) != 0 ||
|
||||
access(PUTTY_ED25519_PPK_TESTKEY, F_OK) != 0) {
|
||||
rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
|
||||
PUTTY_ED25519_TESTKEY);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
rc = system_checked(PUTTY_KEYGEN " " PUTTY_ED25519_TESTKEY
|
||||
" -O private -o " PUTTY_ED25519_PPK_TESTKEY);
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_putty_client_keys(void)
|
||||
{
|
||||
cleanup_key(PUTTY_RSA_TESTKEY);
|
||||
cleanup_file(PUTTY_RSA_PPK_TESTKEY);
|
||||
|
||||
cleanup_key(PUTTY_ECDSA256_TESTKEY);
|
||||
cleanup_file(PUTTY_ECDSA256_PPK_TESTKEY);
|
||||
|
||||
cleanup_key(PUTTY_ED25519_TESTKEY);
|
||||
cleanup_file(PUTTY_ED25519_PPK_TESTKEY);
|
||||
}
|
||||
@@ -30,17 +30,29 @@ void cleanup_ecdsa_keys(void);
|
||||
#define OPENSSH_ECDSA521_TESTKEY "openssh_testkey.id_ecdsa521"
|
||||
#define OPENSSH_ED25519_TESTKEY "openssh_testkey.id_ed25519"
|
||||
#define OPENSSH_CA_TESTKEY "libssh_testkey.ca"
|
||||
#define OPENSSH_ECDSA_SK_TESTKEY "openssh_testkey.id_ecdsa-sk"
|
||||
#define OPENSSH_ED25519_SK_TESTKEY "openssh_testkey.id_ed25519-sk"
|
||||
|
||||
#define DROPBEAR_RSA_TESTKEY "dropbear_testkey.id_rsa"
|
||||
#define DROPBEAR_ECDSA256_TESTKEY "dropbear_testkey.id_ecdsa256"
|
||||
#define DROPBEAR_ED25519_TESTKEY "dropbear_testkey.id_ed25519"
|
||||
|
||||
#define PUTTY_RSA_TESTKEY "putty_testkey.id_rsa"
|
||||
#define PUTTY_RSA_PPK_TESTKEY "putty_testkey.id_rsa.ppk"
|
||||
#define PUTTY_ECDSA256_TESTKEY "putty_testkey.id_ecdsa256"
|
||||
#define PUTTY_ECDSA256_PPK_TESTKEY "putty_testkey.id_ecdsa256.ppk"
|
||||
#define PUTTY_ED25519_TESTKEY "putty_testkey.id_ed25519"
|
||||
#define PUTTY_ED25519_PPK_TESTKEY "putty_testkey.id_ed25519.ppk"
|
||||
|
||||
void setup_openssh_client_keys(void);
|
||||
void cleanup_openssh_client_keys(void);
|
||||
|
||||
void setup_dropbear_client_keys(void);
|
||||
void cleanup_dropbear_client_keys(void);
|
||||
|
||||
void setup_putty_client_keys(void);
|
||||
void cleanup_putty_client_keys(void);
|
||||
|
||||
#define cleanup_file(name) do {\
|
||||
if (access((name), F_OK) != -1) {\
|
||||
unlink((name));\
|
||||
|
||||
@@ -112,3 +112,10 @@ int is_openssh_client_enabled(void) {
|
||||
int is_dropbear_client_enabled(void) {
|
||||
return (bin_exists(DROPBEAR_BINARY) && bin_exists(DROPBEAR_KEYGEN));
|
||||
}
|
||||
|
||||
int is_putty_client_enabled(void)
|
||||
{
|
||||
return (bin_exists(PUTTY_BINARY) &&
|
||||
bin_exists(PUTTY_KEYGEN) &&
|
||||
bin_exists(OPENSSH_KEYGEN));
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ int system_checked(const char *cmd);
|
||||
/* Is client 'X' enabled? */
|
||||
int is_openssh_client_enabled(void);
|
||||
int is_dropbear_client_enabled(void);
|
||||
int is_putty_client_enabled(void);
|
||||
|
||||
#endif /* __PKD_UTIL_H__ */
|
||||
#endif /* __PKD_UTIL_H__ */
|
||||
@@ -19,7 +19,10 @@ if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING)
|
||||
${LIBSSH_SERVER_TESTS}
|
||||
torture_gssapi_server_auth
|
||||
torture_gssapi_server_auth_cb
|
||||
torture_gssapi_server_delegation)
|
||||
torture_gssapi_server_delegation
|
||||
torture_gssapi_server_key_exchange
|
||||
torture_gssapi_server_key_exchange_null
|
||||
torture_gssapi_server_key_exchange_fallback)
|
||||
endif()
|
||||
|
||||
include_directories(${libssh_SOURCE_DIR}/include
|
||||
|
||||
@@ -17,10 +17,6 @@ if (WITH_COVERAGE)
|
||||
append_coverage_compiler_flags_to_target(testserver)
|
||||
endif (WITH_COVERAGE)
|
||||
|
||||
set(LIBSSH_SERVER_TESTS
|
||||
# torture_server_kbdint
|
||||
)
|
||||
|
||||
include_directories(${libssh_SOURCE_DIR}/include
|
||||
${libssh_BINARY_DIR}/include
|
||||
${libssh_BINARY_DIR})
|
||||
|
||||
@@ -172,6 +172,65 @@ null_userdata:
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
static int kbdint_check_response(ssh_session session, struct session_data_st *sdata)
|
||||
{
|
||||
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(sdata->username, answer);
|
||||
if (cmp != 0) {
|
||||
return 0;
|
||||
}
|
||||
answer = ssh_userauth_kbdint_getanswer(session, 1);
|
||||
cmp = strcmp(sdata->password, answer);
|
||||
if (cmp != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
auth_kbdint_cb(ssh_message message, ssh_session session, void *userdata)
|
||||
{
|
||||
struct session_data_st *sdata = (struct session_data_st *)userdata;
|
||||
|
||||
const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
|
||||
const char *instruction = "Get yourself authenticated";
|
||||
const char *prompts[2] = {"Username: ", "Password: "};
|
||||
char echo[] = {1, 0};
|
||||
|
||||
if (sdata == NULL) {
|
||||
fprintf(stderr, "Error: NULL userdata\n");
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
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)) {
|
||||
sdata->authenticated = 1;
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
#if WITH_GSSAPI
|
||||
int auth_gssapi_mic_cb(ssh_session session,
|
||||
UNUSED_PARAM(const char *user),
|
||||
@@ -783,6 +842,7 @@ struct ssh_server_callbacks_struct *get_default_server_cb(void)
|
||||
cb->auth_password_function = auth_password_cb;
|
||||
cb->auth_pubkey_function = auth_pubkey_cb;
|
||||
cb->channel_open_request_session_function = channel_new_session_cb;
|
||||
cb->auth_kbdint_function = auth_kbdint_cb;
|
||||
#if WITH_GSSAPI
|
||||
cb->auth_gssapi_mic_function = auth_gssapi_mic_cb;
|
||||
#endif
|
||||
@@ -912,7 +972,8 @@ void default_handle_session_cb(ssh_event event,
|
||||
} else {
|
||||
ssh_set_auth_methods(session,
|
||||
SSH_AUTH_METHOD_PASSWORD |
|
||||
SSH_AUTH_METHOD_PUBLICKEY|
|
||||
SSH_AUTH_METHOD_PUBLICKEY |
|
||||
SSH_AUTH_METHOD_INTERACTIVE |
|
||||
SSH_AUTH_METHOD_GSSAPI_MIC);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ struct arguments_st {
|
||||
static void free_arguments(struct arguments_st *arguments)
|
||||
{
|
||||
if (arguments == NULL) {
|
||||
goto end;
|
||||
return;
|
||||
}
|
||||
|
||||
SAFE_FREE(arguments->address);
|
||||
@@ -87,9 +87,6 @@ static void free_arguments(struct arguments_st *arguments)
|
||||
SAFE_FREE(arguments->config_file);
|
||||
SAFE_FREE(arguments->log_file);
|
||||
SAFE_FREE(arguments->pid_file);
|
||||
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ARGP_H
|
||||
@@ -115,6 +112,9 @@ static void print_auth_methods(int auth_methods)
|
||||
if (auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
|
||||
printf("\tSSH_AUTH_METHOD_GSSAPI_MIC\n");
|
||||
}
|
||||
if (auth_methods & SSH_AUTH_METHOD_GSSAPI_KEYEX) {
|
||||
printf("\tSSH_AUTH_METHOD_GSSAPI_KEYEX\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_verbosity(int verbosity)
|
||||
@@ -273,6 +273,7 @@ static int init_server_state(struct server_state_st *state,
|
||||
} else {
|
||||
state->auth_methods = SSH_AUTH_METHOD_PASSWORD |
|
||||
SSH_AUTH_METHOD_PUBLICKEY |
|
||||
SSH_AUTH_METHOD_INTERACTIVE |
|
||||
SSH_AUTH_METHOD_GSSAPI_MIC;
|
||||
}
|
||||
|
||||
@@ -295,6 +296,7 @@ static int init_server_state(struct server_state_st *state,
|
||||
}
|
||||
|
||||
state->parse_global_config = arguments->with_global_config;
|
||||
state->gssapi_key_exchange_algs = NULL;
|
||||
|
||||
if (arguments->config_file) {
|
||||
state->config_file = arguments->config_file;
|
||||
|
||||
@@ -145,9 +145,8 @@ int run_server(struct server_state_st *state)
|
||||
}
|
||||
|
||||
if (state->host_key == NULL && state->rsa_key == NULL &&
|
||||
state->ecdsa_key == NULL && state->ed25519_key) {
|
||||
state->ecdsa_key == NULL && state->ed25519_key == NULL) {
|
||||
fprintf(stderr, "Missing host key\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sshbind = ssh_bind_new();
|
||||
@@ -194,6 +193,30 @@ int run_server(struct server_state_st *state)
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef WITH_GSSAPI
|
||||
rc = ssh_bind_options_set(sshbind,
|
||||
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
|
||||
&(state->gssapi_key_exchange));
|
||||
if (rc != 0) {
|
||||
fprintf(stderr,
|
||||
"Error setting GSSAPI key exchange: %s\n",
|
||||
ssh_get_error(sshbind));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (state->gssapi_key_exchange_algs != NULL) {
|
||||
rc = ssh_bind_options_set(sshbind,
|
||||
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
|
||||
state->gssapi_key_exchange_algs);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr,
|
||||
"Error setting GSSAPI key exchange algorithms: %s\n",
|
||||
ssh_get_error(sshbind));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif /* WITH_GSSAPI */
|
||||
|
||||
rc = ssh_bind_options_set(sshbind,
|
||||
SSH_BIND_OPTIONS_BINDPORT,
|
||||
&(state->port));
|
||||
|
||||
@@ -53,6 +53,8 @@ struct server_state_st {
|
||||
bool parse_global_config;
|
||||
|
||||
char *log_file;
|
||||
bool gssapi_key_exchange;
|
||||
const char *gssapi_key_exchange_algs;
|
||||
|
||||
/* State */
|
||||
int max_tries;
|
||||
|
||||
@@ -160,6 +160,7 @@ setup_default_server(void **state)
|
||||
ss = tss->ss;
|
||||
s = tss->state;
|
||||
|
||||
setenv("NSS_WRAPPER_HOSTNAME", "server.libssh.site", 1);
|
||||
/* Start the server using the default values */
|
||||
pid = fork_run_server(ss, free_test_server_state, &tss);
|
||||
if (pid < 0) {
|
||||
@@ -335,7 +336,7 @@ torture_gssapi_server_auth_invalid_host(void **state)
|
||||
|
||||
"echo bar | kinit alice");
|
||||
rc = ssh_userauth_gssapi(session);
|
||||
assert_int_equal(rc, SSH_AUTH_ERROR);
|
||||
assert_int_equal(rc, SSH_AUTH_DENIED);
|
||||
torture_teardown_kdc_server((void **)&s);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user